001    /*
002     *  PAVLOV -- Multiple Choice Study System
003     *  Copyright (C) 2000 - 2004 T.J. Willis
004     *
005     *  This program is free software; you can redistribute it and/or
006     *  modify it under the terms of the GNU General Public License
007     *  as published by the Free Software Foundation; either version 2
008     *  of the License, or (at your option) any later version.
009     *
010     *  This program is distributed in the hope that it will be useful,
011     *  but WITHOUT ANY WARRANTY; without even the implied warranty of
012     *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013     *  GNU General Public License for more details.
014     *
015     *  You should have received a copy of the GNU General Public License
016     *  along with this program; if not, write to the Free Software
017     *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
018     *
019     *  $Header: /cvsroot/pavlov/net/sourceforge/pavlov/main/standalone/SwingUIFactory.java,v 1.20 2004/07/01 09:03:43 tj_willis Exp $
020     */
021    package net.sourceforge.pavlov.main.standalone;
022    
023    //import static net.sourceforge.pavlov.main.standalone.ResourceBroker;
024    //import static ResourceBroker;
025    import java.awt.*;
026    import java.awt.event.*;
027    import java.net.URL;
028    import java.util.Hashtable;
029    import javax.help.*;
030    import javax.swing.*;
031    import javax.swing.event.HyperlinkEvent;
032    import javax.swing.event.HyperlinkListener;
033    import net.sourceforge.pavlov.controllers.*;
034    import net.sourceforge.pavlov.event.*;
035    import net.sourceforge.pavlov.library.AbstractLibrary;
036    import net.sourceforge.pavlov.library.ChapterReference;
037    import net.sourceforge.pavlov.main.*;
038    import net.sourceforge.pavlov.pluglets.*;
039    import net.sourceforge.pavlov.pluglets.feedback.velocity.*;
040    import net.sourceforge.pavlov.pluglets.strategy.*;
041    import net.sourceforge.pavlov.randommedia.ImageIconUtilities;
042    import net.sourceforge.pavlov.startup.*;
043    import net.sourceforge.pavlov.swing.*;
044    import net.sourceforge.pavlov.user.*;
045    import net.sourceforge.sillyview.*;
046    import net.sourceforge.steelme.*;
047    import org.apache.log4j.*;
048    import java.beans.PropertyChangeListener;
049    import java.beans.PropertyChangeEvent;
050    
051    /**
052     *  The purpose of this class and AbstractUIFactory is to separate
053     *  user-interface details and program logic so that Pavlov can easily be
054     *  reimplemented in user interface environments other than Swing. As of 1.0
055     *  that separation has not been accomplished. There needs to be a greater level
056     *  of abstraction here and throughout the code. For example, many classes
057     *  throughout Pavlov are subclasses of JI_nternalFrame and JComponent.
058     *  Reimplementation of these classes as Model-View-Controller would allow
059     *  porting to consist of writing a view, and a UIFactory that couples that view
060     *  with the controller.
061     *
062     *@author     <a href="mailto:tj_willis@users.sourceforge.net">T.J. Willis</a>
063     *@created    June 26, 2004
064     *@version    1.0
065     */
066    public class SwingUIFactory extends AbstractUIFactory
067                     implements ActionListener, HyperlinkListener, LogonListener
068    {
069            // FIXED: broke startup window closing frame references removed
070            // FIXED: isolate login functionality in main.LoginController
071            // FIXED: remove old quizSelector
072            // FIXED: apparently don't need getSelectedChapterReference()
073            // FIXED: de-static all fields
074            // FIXED: minimize access all fields
075            // FIXED: move to standalone directory
076            // FIXED: remove frame reference
077            // FIXME: class-field JMenuItems should be replaced with actions
078            // FIXED: move in CVS
079            private LibraryController libController;
080            private HardcopyQuiz hardcopyQuiz;
081            private StartupWindow lstartup = null;
082            private ResourceBroker rb;
083    
084            /**
085             *  Describe variable <code>MYFRAME</code> here.
086             */
087            protected final JFrame MYFRAME;
088            /**
089             *  Describe variable <code>SPLITTER</code> here.
090             */
091            protected final JSplitPane SPLITTER;
092            /**
093             *  Describe variable <code>pavlov</code> here.
094             */
095            protected final Pavlov pavlov;
096            /**
097             *  Describe variable <code>quiz</code> here.
098             */
099            protected Quiz quiz;
100    
101            /**
102             *  Describe variable <code>templateKit</code> here.
103             */
104            private Skin templateKit;
105            private static Category cat = Category.getInstance(SwingUIFactory.class.getName());
106    
107            /**
108             *  Describe variable <code>quizController</code> here.
109             */
110            protected QuizController quizController;
111    
112            /**
113             *  Describe variable <code>theme</code> here.
114             */
115            protected SteelmeTheme theme;
116    
117            /**
118             *  Describe variable <code>themeDirectory</code> here.
119             */
120            protected java.io.File themeDirectory;
121            /**
122             *  Describe variable <code>feedbackPlugins</code> here.
123             */
124            protected PlugletLoader feedbackPlugins;
125            /**
126             *  Describe variable <code>velocityPlugins</code> here.
127             */
128            protected VelocityPlugletLoader velocityPlugins;
129    
130            /**
131             *  Describe variable <code>strategyPluglets</code> here.
132             */
133            protected StrategyLoader strategyPluglets;
134            /**
135             *  Describe variable <code>toolPluglets</code> here.
136             */
137            protected PlugletLoader toolPluglets;
138    
139            private JFrameView loginView;
140            private JMenuItem saveMenuItem, exitMenuItem;
141            private JRadioButtonMenuItem incorrectMenuItem;
142            private volatile JMenu feedbackMenu,strategyMenu, toolsMenu;
143            private JDialog incorrect;
144            private Incorrect ict;
145            
146            /**
147             *  Creates a new <code>SwingUIFactory</code> instance.
148             *
149             *@param  thePavlov  a <code>Pavlov</code> value
150             *@param  base       a <code>JComponent</code> value
151             */
152            public SwingUIFactory(Pavlov thePavlov, JFrame base)
153            {
154                    rb = ResourceBroker.getInstance();
155                    // FIXED: remove frame reference
156                    MYFRAME = base;
157                    pavlov = thePavlov;
158                    try {
159                            //FIXED: read from properties
160                            java.io.File def = rb.getFile(rb.SKINS_DEFAULT_DIR);
161                            templateKit = new Skin(def);
162                            setTemplateToolkit(templateKit);
163                    }
164                    catch (Exception ex) {
165                            error(rb.SKINS_ERROR_DEFAULT, ex);
166                    }
167                    //JInternalFrame jif = new JInternalFrame();
168    
169                    SPLITTER = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true);
170                    //SPLITTER.setRightComponent(tmp2);
171                    SPLITTER.setOneTouchExpandable(true);
172                    //jif.getContentPane().add(SPLITTER);
173                    //addInternalFrame(jif);
174                    //jif.setResizable(true);
175                    //try {
176                    //      jif.setMaximum(true);
177                    //} catch (java.beans.PropertyVetoException p) {
178                    // screw it
179                    //}
180            }
181    
182    
183            //FIXME: crappy name
184            /**
185             *  Gets the main attribute of the SwingUIFactory object
186             *
187             *@return    The main value
188             */
189            public JComponent getMain()
190            {
191                    return SPLITTER;
192            }
193    
194    
195            /**
196             *  Description of the Method
197             *
198             *@param  key     Description of the Parameter
199             *@param  except  Description of the Parameter
200             */
201            private void error(final String key, Exception except)
202            {
203                    cat.error(rb.getString(key), except);
204            }
205    
206    
207            /**
208             *  Describe <code>confirmQuit</code> method here.
209             *
210             *@return    a <code>boolean</code> value
211             */
212            public boolean confirmQuit()
213            {
214                    int ret = showConfirmDialog(rb.getString(rb.QUIT));
215                    return (ret == JOptionPane.YES_OPTION);
216            }
217    
218    
219            /**
220             *  Describe <code>setQuiz</code> method here.
221             *
222             *@param  quiz  The new quiz value
223             */
224            public void setQuiz(Quiz quiz)
225            {
226                    this.quiz = quiz;
227            }
228    
229    
230            /**
231             *  Describe <code>getQuiz</code> method here.
232             *
233             *@return    a <code>Quiz</code> value
234             */
235            public Quiz getQuiz()
236            {
237                    return quiz;
238            }
239    
240    
241            /**
242             *  Describe <code>setFeedbackPluglets</code> method here.
243             *
244             *@param  loader  The new feedbackPluglets value
245             */
246            public void setFeedbackPluglets(PlugletLoader loader)
247            {
248                    this.feedbackPlugins = loader;
249                    if(feedbackMenu==null){
250                            cat.error("Null feedbackMenu: race condition"); 
251                    }
252                    feedbackPlugins.addToJMenuAsCheckboxes(feedbackMenu);
253    
254            }
255    
256    
257            /**
258             *  Describe <code>setVelocityPluglets</code> method here.
259             *
260             *@param  loader  The new velocityPluglets value
261             */
262            public void setVelocityPluglets(VelocityPlugletLoader loader)
263            {
264                    velocityPlugins = loader;
265                    if(feedbackMenu==null){
266                            cat.error("Null feedbackMenu: race condition"); 
267                    }
268                    velocityPlugins.addToJMenuAsCheckboxes(feedbackMenu);
269            }
270    
271            /**
272             *  Describe <code>setStrategyPluglets</code> method here.
273             *
274             *@param  loader  The new strategyPluglets value
275             */
276            public void setStrategyPluglets(StrategyLoader loader)
277            {
278                    strategyPluglets = loader;
279                    if(strategyMenu==null){
280                            cat.error("Null strategyMenu: race condition"); 
281                    }
282                    strategyPluglets.addToJMenuAsRadioButtons(strategyMenu);
283                    //HERE
284            }
285    
286    
287            /**
288             *  Describe <code>setToolPluglets</code> method here.
289             *
290             *@param  loader  The new toolPluglets value
291             */
292            public void setToolPluglets(PlugletLoader loader)
293            {
294                    toolPluglets = loader;
295                    if(toolsMenu==null){
296                            cat.error("Null toolsMenu: race condition");    
297                    }
298                    toolPluglets.addToJMenuAsCheckboxes(toolsMenu);
299            }
300            
301            
302            /**
303             *  Apply the current theme.
304             */
305            public void applyTheme()
306            {
307                    if (theme == null && templateKit != null) {
308                            theme = templateKit.getTheme();
309                    }
310                    if (theme != null) {
311                            theme.setTarget(MYFRAME);
312                            theme.applyTheme(MYFRAME);
313                    }
314            }
315    
316    
317            /**
318             *  Describe <code>setTemplateToolkit</code> method here.
319             *
320             *@param  kit  an <code>AbstractTemplateKit</code> value
321             */
322            public void setTemplateToolkit(Skin kit)
323            {
324                    theme = kit.getTheme();
325                    applyTheme();
326    
327                    templateKit = kit;
328                    if (libController != null) {
329                            libController.setTemplate(kit.getLibraryTemplate(), kit.getBaseDir());
330                    }
331                    if (quizController != null) {
332                            quizController.setTemplate(kit.getQuizTemplate(), kit.getBaseDir());
333                    }
334                    applyTheme();
335                    cat.info("Set kit");
336            }
337    
338    
339    
340    
341    
342            /**
343             *  Gets the applicationWidth attribute of the SwingUIFactory object
344             *
345             *@return    The applicationWidth value
346             */
347            private int getApplicationWidth()
348            {
349                    Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
350                    return (int) (dim.getWidth() * .9);
351                    //^
352            }
353    
354    
355            /**
356             *  Gets the applicationHeight attribute of the SwingUIFactory object
357             *
358             *@return    The applicationHeight value
359             */
360            private int getApplicationHeight()
361            {
362                    Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
363                    return (int) (dim.getHeight() * .9);//^
364            }
365    
366    
367            /**
368             *  Describe <code>createQuizSelector</code> method here.
369             *
370             *@param  user     an <code>User</code> value
371             *@param  library  an <code>AbstractLibrary</code> value
372             */
373            public void createQuizSelector(User user, AbstractLibrary library)
374            {
375                    // FIXED: LibraryController replaces quizSelector
376    
377                    libController = null;
378                    String tmplt = null;
379                    String welcomeTmplt = null;
380                    VelocityModel welcomeModel = null;
381                    try {
382                            welcomeTmplt = templateKit.getWelcomeTemplate();
383                            welcomeModel = new VelocityModel(welcomeTmplt);
384                            tmplt = templateKit.getLibraryTemplate();
385                            VelocityModel libMod = new VelocityModel(tmplt);
386                            WidgetView libView = new JPanelView(libMod, JPanelView.HTMLPANE);
387                            libController = new LibraryController(libView, library);
388                            libController.setBaseDirectory(templateKit.getBaseDir());
389                    }
390                    catch (Exception ex) {
391                            error(rb.LIBRARY_ERROR, ex);
392                    }
393                    if (libController == null || tmplt == null) {
394                            error(rb.LIBRARY_ERROR, null);
395                    }
396    
397                    libController.setHyperlinkListener(this);
398    
399                    Dimension libSize = new Dimension((int) (getApplicationWidth() * .25),
400                                    getApplicationHeight());//^
401    
402                    JPanelView libVu = (JPanelView) libController.getView();
403    
404                    JScrollPane libScroll = new JScrollPane(libVu);
405                    libScroll.setPreferredSize(libSize);
406                    libScroll.setMinimumSize(libSize);
407                    SPLITTER.setLeftComponent(libScroll);
408    
409    
410                    HTMLPaneView welcomeVu = new HTMLPaneView(welcomeModel);
411                    setRightView(welcomeVu);
412                    /*
413                     *  JScrollPane sp2 = new JScrollPane(v2);
414                     *  SPLITTER.setRightComponent(sp2);
415                     */
416                    applyTheme();
417            }
418    
419    
420            /**
421             *  Describe <code>startQuiz</code> method here.
422             *
423             *@param  user  an <code>User</code> value
424             *@param  ref   a <code>ChapterReference</code> value
425             */
426            public void startQuiz(User user, ChapterReference ref)
427            {
428            if(user==null) throw new IllegalArgumentException("user is null");
429            if(ref==null) throw new IllegalArgumentException("chapter reference is null");
430    
431                    //assert user != null: rb.getString(rb.USER_NULL);
432                    //assert ref!=null: rb.getString(rb.CHAPTER_NULL);
433                    if (quiz != null) {
434                            saveQuiz();
435                    }
436    
437                    quiz = new Quiz(pavlov, user, ref);
438                    pavlov.setQuiz(quiz);
439    
440                    //==============================================
441                    if (quizController != null) {
442                            quizController.setQuiz(quiz);
443                    }
444                    else {
445                            HTMLPaneView quizVu = (HTMLPaneView) getQuizView();
446                            setRightView(quizVu);
447                            quizVu.setToken(JPanelView.HYPERLINK_LISTENER, this);
448                            //ifv.setAutoDump(true);
449                            try {
450                                    quizController = new QuizController(quiz, quizVu);
451                            }
452                            catch (Exception ex) {
453                                    error(rb.QUIZVIEW_ERROR_CREATE, ex);
454                            }
455                            setTemplateToolkit(templateKit);
456                    }
457                    //==============================================
458                    quiz.newQuestion();
459            }
460    
461    
462            /**
463             *  Sets the rightView attribute of the SwingUIFactory object
464             *
465             *@param  ifv  The new rightView value
466             */
467            private void setRightView(HTMLPaneView ifv)
468            {
469                    Dimension g1 = new Dimension((int) (getApplicationWidth() * .6),
470                                    getApplicationHeight());
471                    ifv.setSize(g1);
472                    ifv.setPreferredSize(g1);
473                    ifv.invalidate();
474                    ifv.doLayout();
475                    JScrollPane sp = new JScrollPane(ifv);
476                    sp.setPreferredSize(g1);
477                    sp.setMinimumSize(g1);
478                    SPLITTER.setRightComponent(sp);
479                    SPLITTER.resetToPreferredSizes();
480            }
481    
482    
483            /**
484             *  Describe <code>getQuizView</code> method here.
485             *
486             *@return    a <code>WidgetView</code> value
487             */
488            private WidgetView getQuizView()
489            {
490                    String tmplt = templateKit.getQuizTemplate();
491                    WidgetModel mod = null;
492                    try {
493                            mod = new VelocityModel(tmplt);
494                    }
495                    catch (Exception ex) {
496                            error(rb.QUIZVIEW_ERROR_CREATE, ex);
497                    }
498                    return new HTMLPaneView(mod);
499            }
500    
501    
502            /**
503             *  Saves the current quiz.
504             */
505            public void saveQuiz()
506            {
507                    try {
508                            quiz.save();
509                    }
510                    catch (Exception ex) {
511                            error(rb.LOGIN_ERROR, ex);
512                    }
513            }
514    
515    
516            /**
517             *  Describe <code>getResourceString</code> method here.
518             *
519             *@param  a  a <code>String</code> value
520             *@param  b  a <code>String</code> value
521             *@return    a <code>String</code> value
522             */
523            public String getResourceString(String a, String b)
524            {
525                    return rb.getString(a, b);
526            }
527    
528    
529            /**
530             *  Null implementation here, since StartupWindow closes itself.
531             */
532            public void closeSplashScreen() { }
533    
534    
535            /**
536             *  The LoginController calls this when the user has finished logging in.
537             *
538             *@param  event  a <code>LogonEvent</code> value
539             */
540            public void logonAttempt(final LogonEvent event)
541            {
542                    if (loginView == null) {
543                            return;
544                    }
545                    loginView.setVisible(false);
546                    loginView = null;
547            }
548    
549    
550            /**
551             *  Sets the message and status number in startup window.
552             *
553             *@param  msg      a <code>String</code> value
554             *@param  percent  The new status value
555             */
556            public void setStatus(int percent, String msg)
557            {
558                    if (lstartup == null) {
559                            return;
560                    }
561                    lstartup.setStatus(percent, msg);
562            }
563    
564    
565            /**
566             *  Creates the login widget and shows it to the user.
567             *
568             *@param  lis  a <code>LogonListener</code> value
569             */
570            public void showLoginController(LogonListener lis)
571            {
572                    try {
573    
574                            VelocityModel loginModel = new VelocityModel(templateKit.getLoginTemplate());
575                            loginView = new JFrameView(loginModel, JPanelView.HTMLPANE);
576                            LoginController lc = new LoginController(loginView);
577    
578                            loginView.pack();
579                            Dimension d = loginView.getSize();
580                            Dimension d2 = Toolkit.getDefaultToolkit().getScreenSize();
581                            int x0 = (d2.width - d.width) / 2;
582                            int y0 = (d2.height - d.height) / 2;
583                            Point p = new Point(x0, y0);
584                            loginView.setLocation(p);
585                            loginView.setVisible(true);
586                            lc.addLogonListener(this);
587                            lc.addLogonListener(lis);
588                    }
589                    catch (Exception ex) {
590                            error(rb.LOGIN_ERROR, ex);
591                            //ex.printStackTrace();
592                            //showErrorDialog("Problem creating login view",ex);
593                    }
594            }
595    
596    
597            /**
598             *  Shows the startup window.
599             *
600             *@param  frame  Description of the Parameter
601             */
602            public void showSplashScreen(JFrame frame)
603            {
604                    // FIXED: this was why the window's not closing
605                    //javax.swing.JFrame frame = pavlov.getFrame();
606                    // FIXME: get startup image from Resources file
607                    try {
608                            lstartup = new StartupWindow(rb.getString(rb.ABOUT_IMAGE), frame);
609                            //"file:resources/AboutPavlov.jpg",frame);
610                    }
611                    catch (Exception ex) {
612                            error(rb.ABOUT_IMAGE, ex);
613                            //FIXME need a message string
614                    }
615            }
616    
617    
618            /**
619             *  Describe <code>showIncorrectDialog</code> method here.
620             *
621             *@param  msg  a <code>String</code> value
622             */
623            public void showIncorrectDialog(String msg)
624            {
625                    if(!incorrectMenuItem.isSelected()){
626                            if(incorrect!=null) incorrect.setVisible(false);
627                            return;
628                    }
629                    //FIXME: rewrite as sillyview
630                    if(incorrect==null){
631                            ict = new Incorrect();
632                            incorrect = ict.createDialog();//new IncorrectDialog(); 
633                    }
634                    incorrect.setVisible(true);
635                    ict.setText(msg);
636                    //System.out.println("set msg to " + msg);
637                    /*
638                    String incor = getResourceString("IncorrectDialogTitle", "Incorrect");
639                    if(incorrect==null){
640                            incorrect = new JDialog(MYFRAME,incor);
641                    }
642                    JLabel lab = new JLabel(msg);
643                    incorrect.getContentPane().add(lab);
644                    incorrect.setVisible(true);
645                    JTextArea bull = new JTextArea(msg,2,40);
646                    bull.setLineWrap(true);
647                    bull.setEditable(false);
648                    JScrollPane sp = new JScrollPane(bull);
649                    showMessageDialog(sp, incor,JOptionPane.INFORMATION_MESSAGE);
650                    */
651            }
652    
653    
654    
655            /**
656             *  Describe <code>showFileChooser</code> method here.
657             *
658             *@param  chooser   a <code>JFileChooser</code> value
659             *@param  openMode  a <code>boolean</code> value
660             *@return           an <code>int</code> value
661             */
662            public int showFileChooser(JFileChooser chooser, boolean openMode)
663            {
664                    // FIXED: remove frame reference
665                    //chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES );
666                    int retVal = JFileChooser.CANCEL_OPTION;
667                    if (chooser == null || MYFRAME == null) {
668                            return retVal;
669                    }
670                    if (openMode) {
671                            return chooser.showOpenDialog(MYFRAME);
672                    }
673                    else {
674                            return chooser.showSaveDialog(MYFRAME);
675                    }
676            }
677    
678    
679            //==================================fixed============================
680    
681            /**
682             *  Describe <code>makeImageIcon</code> method here.
683             *
684             *@param  key   a <code>String</code> value
685             *@param  def   a <code>String</code> value
686             *@param  size  an <code>int</code> value
687             *@return       an <code>ImageIcon</code> value
688             */
689            public ImageIcon makeImageIcon(String key, String def, int size)
690            {
691                    String iFile = rb.getString(key, def);
692                    return ImageIconUtilities.getNamedImageIcon(iFile, size);
693            }
694    
695    
696            /**
697             *  Creates a menu, looking for the text in a properties file, and using the
698             *  default if it can't be found. Also checks for mnemonics (accelerators) and
699             *  tooltips.
700             *
701             *@param  bar  a <code>JMenuBar</code> value
702             *@param  id   a <code>String</code> value
703             *@param  def  a <code>String</code> value
704             *@return      a <code>JMenu</code> value
705             */
706            private JMenu makeMenu(JMenuBar bar, String id, String def)
707            {
708                    JMenu newMenu = new JMenu(getResourceString(id, def));
709                    bar.add(newMenu);
710                    return (newMenu);
711            }
712    
713    
714            /**
715             *  Creates a menu item, looking for the text in a properties file, and using
716             *  the default if it can't be found. Also checks for mnemonics (accelerators)
717             *  and tooltips.
718             *
719             *@param  bar  a <code>JMenu</code> value
720             *@param  id   a <code>String</code> value
721             *@param  def  a <code>String</code> value
722             *@return      a <code>JMenuItem</code> value
723             */
724            private JMenuItem makeMenuItem(JMenu bar, String key, String def)
725            {
726                    JMenuItem item = new JMenuItem(getResourceString(key, def));
727                    String tt = getResourceString(key + ".toolTip", null);
728                    if (tt != null) {
729                            item.setToolTipText(tt);
730                    }
731    
732                    String mnemonic = getResourceString(key + ".mnemonic", null);
733                    if (mnemonic != null) {
734                            // FIXME: mnemonic apparently not be working
735                            char foo[] = new char[1];
736                            mnemonic.getChars(0, 1, foo, 0);
737                            item.setMnemonic(foo[0]);
738                            System.out.println("Set mnemonic to " + foo);
739                    }
740                    bar.add(item);
741                    return (item);
742            }
743    
744    
745            /**
746             *  Describe <code>showConfirmDialog</code> method here.
747             *
748             *@param  message  an <code>Object</code> value
749             *@return          an <code>int</code> value
750             */
751            public int showConfirmDialog(Object message)
752            {
753                    return JOptionPane.showConfirmDialog(MYFRAME, message);
754            }
755    
756    
757            /**
758             *  Describe <code>showMessageDialog</code> method here.
759             *
760             *@param  message      an <code>Object</code> value
761             *@param  title        a <code>String</code> value
762             *@param  messageType  an <code>int</code> value
763             */
764            public void showMessageDialog(Object message, String title, int messageType)
765            {
766                    JOptionPane.showMessageDialog(MYFRAME,
767                                    message,
768                                    title, messageType);
769            }
770    
771    
772            /**
773             *  Describe <code>showInputDialog</code> method here.
774             *
775             *@param  message                an <code>Object</code> value
776             *@param  title                  a <code>String</code> value
777             *@param  messageType            an <code>int</code> value
778             *@return                        a <code>String</code> value
779             *@exception  HeadlessException  if an error occurs
780             */
781            public String showInputDialog(Object message,
782                            String title,
783                            int messageType)
784                    throws HeadlessException
785            {
786                    // FIXED: remove frame reference
787                    return JOptionPane.showInputDialog(MYFRAME,
788                                    message,
789                                    title, messageType);
790            }
791    
792    
793            /**
794             *  Describe <code>showOptionDialog</code> method here.
795             *
796             *@param  message                an <code>Object</code> value
797             *@param  title                  a <code>String</code> value
798             *@param  optionType             an <code>int</code> value
799             *@param  messageType            an <code>int</code> value
800             *@param  icon                   an <code>Icon</code> value
801             *@param  options                an <code>Object[]</code> value
802             *@param  initialValue           an <code>Object</code> value
803             *@return                        an <code>int</code> value
804             *@exception  HeadlessException  if an error occurs
805             */
806            public int showOptionDialog(Object message,
807                            String title,
808                            int optionType,
809                            int messageType,
810                            Icon icon,
811                            Object[] options,
812                            Object initialValue)
813                    throws HeadlessException
814            {
815                    // FIXED: remove frame reference
816                    return JOptionPane.showOptionDialog(MYFRAME,
817                                    message, title,
818                                    optionType, messageType,
819                                    icon, options, initialValue);
820            }
821    
822    
823            /**
824             *  Description of the Method
825             *
826             *@param  message  Description of the Parameter
827             *@param  ex       Description of the Parameter
828             */
829            public void showErrorDialog(String message, Exception ex)
830            {
831                    showMessageDialog(message + ex, "Error", JOptionPane.ERROR_MESSAGE);
832            }
833    
834    
835            /**
836             *  Description of the Method
837             *
838             *@param  msg    Description of the Parameter
839             *@param  title  Description of the Parameter
840             */
841            public void showInfoDialog(String msg, String title)
842            {
843                    showMessageDialog(msg, title, JOptionPane.INFORMATION_MESSAGE);
844            }
845    
846    
847    
848            /**
849             *  Describe <code>createMenuBar</code> method here.
850             *
851             *@param  rootPane  Description of the Parameter
852             *@return           Description of the Return Value
853             */
854            public JMenuBar createMenuBar(JComponent rootPane)
855            {
856                    //JRootPane root = MYFRAME.getRootPane();
857                    //assert root!=null: "RootPane is null";
858                    // FIXED: remove frame reference
859                    JMenuBar menuBar = new JMenuBar();
860                    makeFileMenu(menuBar);
861                    makeFeedbackMenu(menuBar);
862                    makePreferencesMenu(menuBar, rootPane);
863                    makeStrategyMenu(menuBar);
864                    makeToolsMenu(menuBar);
865                    makeHelpMenu(menuBar);
866                    return menuBar;
867                    //root.setJMenuBar(menuBar);
868            }
869    
870    
871            /**
872             *  Describe <code>makeFileMenu</code> method here.
873             *
874             *@param  menuBar  Description of the Parameter
875             */
876            protected void makeFileMenu(JMenuBar menuBar)
877            {
878                    JMenu file = makeMenu(menuBar, "FileMenu", "File");
879                    saveMenuItem = makeMenuItem(file, "SaveMenuItem", "Save");
880                    exitMenuItem = makeMenuItem(file, "ExitMenuItem", "Exit");
881                    exitMenuItem.addActionListener(this);
882                    saveMenuItem.addActionListener(this);
883            }
884    
885    
886            /**
887             *  Describe <code>makeFeedbackMenu</code> method here.
888             *
889             *@param  menuBar  Description of the Parameter
890             */
891            protected void makeFeedbackMenu(JMenuBar menuBar)
892            {
893                    //HERE
894                    feedbackMenu = makeMenu(menuBar, "FeedbackMenu", "Feedback");
895                    /*
896                    if (feedbackPlugins != null) {
897                            feedbackPlugins.addToJMenuAsCheckboxes(feedbackMenu);
898                    }
899                    if (velocityPlugins != null) {
900                            velocityPlugins.addToJMenuAsCheckboxes(feedbackMenu);
901                    }
902                    */
903                    menuBar.add(feedbackMenu);
904            }
905    
906    
907            /**
908             *  Describe <code>makeToolsMenu</code> method here.
909             *
910             *@param  menuBar  Description of the Parameter
911             */
912            protected void makeToolsMenu(JMenuBar menuBar)
913            {
914                    toolsMenu = makeMenu(menuBar, "ToolsMenu", "Tools");
915    
916                    try {
917                            hardcopyQuiz = new HardcopyQuiz(pavlov);
918                            JMenuItem hc = makeMenuItem(toolsMenu, "HardcopyMenuItem", "Hardcopy Quiz");
919                            // FIXME: getResource
920                            hc.addActionListener(hardcopyQuiz);
921                            //toolsMenu.add(hc);
922                    }
923                    catch (Exception ex) {
924                            error(rb.HARDCOPY_ERROR, ex);
925                    }
926                    menuBar.add(toolsMenu);
927            }
928    
929    
930            /**
931             *  Describe <code>makePreferencesMenu</code> method here.
932             *
933             *@param  menuBar  Description of the Parameter
934             *@param  targ     Description of the Parameter
935             */
936            protected void makePreferencesMenu(JMenuBar menuBar, JComponent targ)
937            {
938                    // FIXED: remove frame reference
939                    JMenu preferences = makeMenu(menuBar, "PreferencesMenu", "Preferences");
940                    incorrectMenuItem = new JRadioButtonMenuItem(rb.getString("IncorrectMenuItem"),true);
941                    preferences.add(incorrectMenuItem);
942                    incorrectMenuItem.addActionListener(this);
943                    themeDirectory = rb.getFile(rb.THEMES_DEFAULT_DIR);
944                    applyTheme();
945                    JMenu themeMenu = new ThemeMenu(targ, themeDirectory);
946                    preferences.add(themeMenu);
947                    makeSkinMenu(preferences);
948            }
949    
950    
951            /**
952             *  Describe <code>makeSkinMenu</code> method here.
953             *
954             *@param  men  a <code>JMenu</code> value
955             */
956            protected void makeSkinMenu(JMenu men)
957            {
958                    new SkinSystem(this, men);
959            }
960    
961    
962            /**
963             *  Describe <code>makeStrategyMenu</code> method here.
964             *
965             *@param  menuBar  Description of the Parameter
966             */
967            protected void makeStrategyMenu(JMenuBar menuBar)
968            {
969                    String stratString = getResourceString("strategyLabel", "Strategy");
970                    strategyMenu = new JMenu(stratString);
971                    menuBar.add(strategyMenu);
972            }
973    
974    
975            /**
976             *  Describe <code>makeHelpMenu</code> method here.
977             *
978             *@param  menuBar  Description of the Parameter
979             */
980            protected void makeHelpMenu(JMenuBar menuBar)
981            {
982                    HelpSystem helps = new HelpSystem(MYFRAME);
983                    try {
984                            HelpBroker hb;
985                            HelpSet hs;
986                            JMenuItem helpItem;
987                            //FIXED: get helpset from Resources
988                            URL hsURL = rb.getURL(rb.HELPSET_URL);
989                            hs = new HelpSet(null, hsURL);
990                            hb = hs.createHelpBroker();
991                            helpItem = new JMenuItem("Help Topics...");//^
992                            helpItem.addActionListener(new CSH.DisplayHelpFromSource(hb));
993                            helps.add(helpItem);
994                    }
995                    catch (Exception ee) {
996                            // Say what the exception really is
997                            cat.warn("Error Creating HelpSet ", ee);
998                    }
999                    // FIXME: these all got really, really ugly.  HTMLInternalFrame is almost useless
1000                    //        and displays the pages very, very poorly
1001                    boolean ret;
1002                    //ret = helps.addItem("http://pavlov.sf.net/pavlov_demo/tour1.html", "Pavlov Tutorial (online)");
1003                    ret = helps.addItem("file:resources/aboutPavlov.html", "About Pavlov");
1004                    //helps.addSeparator();
1005                    //ret = helps.addItem("http://pavlov.sf.net/bee_demo/tour1.html", "Bee Tutorial (How To Write Questions - online)");
1006                    //ret = helps.addItem("http://pavlov.sf.net/howto/skins.html", "Writing Custom Skins (online)");
1007                    //ret = helps.addItem("http://pavlov.sf.net/howto/veloplug.html", "Writing Custom Feedback Pluglet Scripts (online)");
1008                    //FIXME: problem with formsubmitevents
1009                    //helps.addSeparator();
1010                    //ret = helps.addItem("http://sourceforge.net/tracker/?atid=629249&group_id=101202&func=browse","Bug Reports (online)");
1011                    //ret = helps.addItem("http://sourceforge.net/tracker/?atid=629252&group_id=101202&func=browse","Feature Requests (online)");
1012                    //helps.addSeparator();
1013                    //ret = helps.addItem("http://pavlov.sf.net", "Pavlov Home Page (online)");
1014                    //ret = helps.addItem("http://sf.net/projects/pavlov", "Pavlov News (online)");
1015                    //ret = helps.addItem("http://freshmeat.net/rate/44809/", "Rate Pavlov at freshmeat.net");
1016    
1017                    menuBar.add(helps);
1018            }
1019    
1020    
1021            /**
1022             *  Describe <code>actionPerformed</code> method here.
1023             *
1024             *@param  e  a <code>java.awt.event.ActionEvent</code> value
1025             */
1026            public void actionPerformed(java.awt.event.ActionEvent e)
1027            {
1028                    if (e == null) {
1029                            return;
1030                    }
1031                    Object src = e.getSource();
1032    
1033                    if (src instanceof JMenuItem) {
1034                            if (src.equals(exitMenuItem)) {
1035                                    pavlov.niceQuit(true);
1036                            }
1037                            else if (src.equals(saveMenuItem)) {
1038                                    saveQuiz();
1039                            }
1040                            else if (src.equals(incorrectMenuItem)){
1041                                    if(incorrect.isVisible() &&
1042                                    !incorrectMenuItem.isSelected()){
1043                                            incorrect.setVisible(false);    
1044                                    }
1045                            }
1046                    }
1047            }
1048    
1049    
1050            //===============================end fixed===============================
1051    
1052            //FIXME: temporary hack only, for widget development
1053            /**
1054             *  Describe <code>hyperlinkUpdate</code> method here.
1055             *
1056             *@param  e  a <code>HyperlinkEvent</code> value
1057             */
1058            public void hyperlinkUpdate(HyperlinkEvent e)
1059            {
1060                    if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
1061                            try {
1062                                    java.net.URL u = e.getURL();
1063                                    //cat.debug("Got url: " + u);
1064                                    String command = URLParser.getFilenameWithoutPath(u);
1065                                    //FIXME: tiger-style this hashtable
1066                                    Hashtable hash = URLParser.parseVariables(u);
1067                                    if (hash == null) {
1068                                            cat.warn("URL hashtable is null");
1069                                            return;
1070                                    }
1071                                    if (QuizController.ANSWER_EVENT.equals(command)) {
1072                                            processAnswer(hash);
1073                                    }
1074                                    else if (LibraryController.START_QUIZ_COMMAND.equals(command)) {
1075                                            processStartQuiz(hash);
1076                                    }
1077                            }
1078                            catch (Exception ex) {
1079                                    cat.error("Error parsing command url", ex);
1080                            }
1081                    }
1082            }
1083    
1084    
1085            /**
1086             *  Description of the Method
1087             *
1088             *@param  h  Description of the Parameter
1089             */
1090            private void processAnswer(Hashtable hash)
1091            {
1092                    boolean right = false;
1093                    if (hash != null) {
1094                            Object mode = hash.get(QuizController.ANSWER_MODE);
1095                            if (mode != null) {
1096                                    String modeStr = mode.toString();
1097                                    right = (QuizController.RIGHT.equals(modeStr));                         
1098                            } else {
1099                                    cat.error("mode is null");                              
1100                            }
1101                    }
1102                    AnswerEvent event = new AnswerEvent(right);
1103                    //FIXME:  in standalone, this works right.  as a servlet,
1104                    // i'm not so sure.  i.e., say the user has two open browsers.
1105                    // then quiz.question is not necessarily this question.  quiz.
1106                    // chapter might not even be right.  so we can parse user,
1107                    // book, chapter, blahblahblah here and forward it to quiz.
1108                    // it seems like a lot of work, though...
1109                    quiz.answerEvent(event);
1110    
1111            }
1112    
1113    
1114            /**
1115             *  Description of the Method
1116             *
1117             *@param  h  Description of the Parameter
1118             */
1119            private void processStartQuiz(Hashtable hash)
1120            {
1121            if(hash==null) throw new IllegalArgumentException("command hashtable is null");
1122                    Object bookName = hash.get(LibraryController.BOOK_NAME);
1123                    Object chapName = hash.get(LibraryController.CHAPTER_NAME);
1124                    if (bookName == null) {
1125                            error(rb.BOOK_NULL, null);
1126                    }
1127                    else if (chapName == null) {
1128                            error(rb.CHAPTER_NULL, null);
1129                    }
1130                    else {
1131                            ChapterReference ref =
1132                                            new ChapterReference(pavlov.getLibrary(),
1133                                            bookName.toString(),
1134                                            chapName.toString());
1135                            startQuiz(pavlov.getUser(), ref);
1136                    }
1137    
1138            }
1139            
1140            private class Incorrect extends JOptionPane {
1141                    private JTextArea msgArea;
1142                    
1143                    public Incorrect(){
1144                            super(null,JOptionPane.INFORMATION_MESSAGE);
1145                            msgArea = new JTextArea(2,40);
1146                            msgArea.setLineWrap(true);
1147                            msgArea.setEditable(false);
1148                            JScrollPane sp = new JScrollPane(msgArea);
1149                            setMessage(sp);                 
1150                    }
1151                    
1152                    public JDialog createDialog(){
1153                            JDialog jd = super.createDialog( SwingUIFactory.this.MYFRAME,
1154                                    getResourceString("IncorrectDialogTitle", "Incorrect"));
1155                            jd.setModal(false);
1156                            jd.setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
1157                            return jd;
1158                    }
1159                    
1160                    public void setText(String txt){
1161                            msgArea.setText(txt);   
1162                    }
1163                    
1164                    
1165            }
1166            
1167    /*      
1168            private class IncorrectDialog extends JDialog {
1169                    private JTextArea msgArea;
1170                    private JOptionPane op;
1171                    
1172                    public IncorrectDialog(){
1173                            this = JOptionPane.createDialog(SwingUIFactory.this.MYFRAME,
1174                                  getResourceString("IncorrectDialogTitle", "Incorrect"));
1175                            setModal(false);  
1176                            
1177                            //super(SwingUIFactory.this.MYFRAME,
1178                            //      getResourceString("IncorrectDialogTitle", "Incorrect"),
1179                            //      false);
1180                            msgArea = new JTextArea(2,40);
1181                            msgArea.setLineWrap(true);
1182                            msgArea.setEditable(false);
1183                            JScrollPane sp = new JScrollPane(msgArea);
1184                            op = new JOptionPane(sp,JOptionPane.INFORMATION_MESSAGE);
1185                            op.addPropertyChangeListener(
1186                            new PropertyChangeListener() {
1187                                    public void propertyChange(PropertyChangeEvent e) {
1188                                            System.out.println("Got PCE:" + e);
1189                                            String prop = e.getPropertyName();
1190    
1191                                            if (IncorrectDialog.this.isVisible() 
1192                                            && (e.getSource() == IncorrectDialog.this.op)){
1193    
1194                                                    IncorrectDialog.this.setVisible(false);
1195                                                    IncorrectDialog.this.op.fireVetoableChange(
1196                                                    e.getPropertyName(),
1197                                                    e.getOldValue(),
1198                                                    e.getNewValue()
1199                                                    );
1200                                            }
1201                                    }
1202                            });
1203                            
1204                            getContentPane().add(op);
1205                            System.out.println("Created incorD");
1206                            setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
1207                            pack();
1208                    }
1209    
1210                    public void setText(String txt){
1211                            msgArea.setText(txt);   
1212                    }
1213    
1214                    
1215            }
1216            */
1217    }
1218