/*
* Copyright (c) 2012, Fromentin Xavier, Schnell Michaël, Dervin Cyrielle, Brabant Quentin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * The names of its contributors may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Fromentin Xavier, Schnell Michaël, Dervin Cyrielle OR Brabant Quentin
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package kameleon.gui.view;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import kameleon.exception.FileReadingException;
import kameleon.exception.KameleonException;
import kameleon.gui.exception.InvalidLookAndFeelException;
import kameleon.gui.exception.UnknownKeyException;
import kameleon.gui.language.GuiLanguage;
import kameleon.gui.language.SwitchLanguage;
import kameleon.gui.model.Model;
import kameleon.gui.model.Observer;
import kameleon.gui.util.FileConstants;
import kameleon.gui.util.ImageUtility;
import kameleon.gui.util.LanguageConstants;
/**
* Menu bar of the Kameleon graphical interface.
*
* <p>The menu contains the following entries:
* <ul>
* <li>settings
* <li>plug-ins
* </ul>
*
* <p>The settings menu contains the following entries:
* <ul>
* <li>sub menu language
* <li>sub menu look and feel
* </ul>
*
* <p>The language sub-menu contains the following entries:
* <ul>
* <li>French
* <li>English
* </ul>
* Each entry is displayed using the country flag and the language
* name. This menu is used to change the display language of the
* graphical interface.
*
* <p>The look and feel contains a entry for each supported look
* and feel (displayed using the look and feel
* {@link LookAndFeelInfo#getName() name}). This menu is used to
* change the look and feel used by the graphical interface.
*
* <p>The plug-ins menu contains the following entries:
* <ul>
* <li>delete an analyzer
* <li>delete a generator
* </ul>
*
* @author Fromentin Xavier, Schnel Michaël
* @version 1.0
*/
public class Menu extends JMenuBar
implements Observer, LanguageConstants, FileConstants {
/**
* Needed to serialize this class.
*
* @see java.io.Serializable
*/
private static final long serialVersionUID = -8853187770406547093L ;
/**
* Model for this view.
*/
private Model model ;
/**
* {@code JFrame} using this menu.
*/
private JFrame container ;
/**
* Settings menu.
*/
private JMenu settings ;
/**
* Languages sub-menu.
*/
private JMenu languages ;
/**
* French language menu item.
*/
private JMenuItem frenchLang ;
/**
* English language menu item.
*/
private JMenuItem englishLang ;
/**
* Look and feel sub-menu.
*/
private JMenu lookAndFeel ;
/**
* Plug-ins menu.
*/
private JMenu plugIns ;
/**
* Menu item used to delete an analyzer.
*/
private JMenuItem deleteAnalyzer ;
/**
* Menu item used to delete a generator.
*/
private JMenuItem deleteGenerator ;
/**
* Builds an instance with the given model and container.
*
* @param model
* model for this view
*
* @param container
* component containing this menu
*
* @throws KameleonException
* if the menu could not be built
*/
public Menu(Model model, JFrame container)
throws KameleonException {
super() ;
this.model = model ;
this.container = container ;
this.model.addObserver(this) ;
this.build() ;
this.update() ;
this.reloadLanguage() ;
this.setVisible(true) ;
}// Menu(Model, JFrame)
/**
* Updates the menu. Disables the menu item the current look and
* feel and enables the previously disabled menu items.
*/
@Override
public void update() {
//TODO Render the selected look and field un-clickable
this.deleteAnalyzer.setEnabled(
!this.model.getKnownAnalyzers().isEmpty()) ;
this.deleteGenerator.setEnabled(
!this.model.getKnownGenerators().isEmpty()) ;
}// update()
/**
* Updates the displayed text for this menu and disables the menu
* item for the current display language.
*
* @throws UnknownKeyException
* if the new language file doesn't support a needed key
*/
@Override
public void reloadLanguage() throws UnknownKeyException {
SwitchLanguage sl = SwitchLanguage.getInstance() ;
// Disable only the menu item of the current language
//TODO Add access to the previously selected language to boost efficiency
GuiLanguage currentLang = sl.getCurrentLanguage() ;
this.frenchLang.setEnabled(
!GuiLanguage.FRENCH.equals(currentLang)) ;
this.englishLang.setEnabled(
!GuiLanguage.ENGLISH.equals(currentLang)) ;
// Update displaeyd text
this.settings.setText(sl.getText(CONFIGURATION_MENU)) ;
this.settings.setToolTipText(
sl.getText(CONFIGURATION_MENU_TOOLTIP)) ;
this.languages.setText(sl.getText(LANGUAGE_SELECTION_MENU)) ;
this.languages.setToolTipText(
sl.getText(LANGUAGE_SELECTION_MENU_TOOLTIP)) ;
this.frenchLang.setText(
sl.getText(LANGUAGE_SELECTION_MENU_FR)) ;
this.englishLang.setText(
sl.getText(LANGUAGE_SELECTION_MENU_EN)) ;
this.lookAndFeel.setText(sl.getText(LOOK_AND_FEEL_MENU)) ;
this.plugIns.setText(sl.getText(TOOLS_MENU)) ;
this.plugIns.setToolTipText(sl.getText(TOOLS_MENU_TOOLTIP)) ;
this.deleteAnalyzer.setText(sl.getText(DELETE_ANALYZER_MENU)) ;
this.deleteAnalyzer.setToolTipText(
sl.getText(DELETE_ANALYZER_MENU_TOOLTIP)) ;
this.deleteGenerator.setText(sl.getText(DELETE_GENERATOR_MENU)) ;
this.deleteGenerator.setToolTipText(
sl.getText(DELETE_GENERATOR_MENU_TOOLTIP)) ;
}// reloadLanguage()
/**
* Builds the menu.
*
* @throws FileReadingException
* if a menu icon could not be read
*/
private void build() throws FileReadingException {
this.buildSettingsMenu() ;
this.buildPlugInsMenu() ;
}
/**
* Builds the settings menu and adds it to this instance.
*
* @throws FileReadingException
* if a menu icon could not be read
*/
private void buildSettingsMenu() throws FileReadingException {
// Menu Configuration
this.settings = new JMenu() ;
this.settings.setMnemonic(KeyEvent.VK_N) ;
this.add(this.settings) ;
// Sub-menu : language choice
this.languages = new JMenu() ;
this.languages.setMnemonic(KeyEvent.VK_L) ;
this.settings.add(this.languages) ;
// Language item: French
this.frenchLang = this.buildLanguageItem(
KeyEvent.VK_F, GuiLanguage.FRENCH) ;
this.languages.add(this.frenchLang) ;
// Language item: English
this.englishLang = this.buildLanguageItem(
KeyEvent.VK_E, GuiLanguage.ENGLISH) ;
this.languages.add(this.englishLang) ;
// Look and feel sub-menu
this.lookAndFeel = new JMenu() ;
this.lookAndFeel.setMnemonic(KeyEvent.VK_A) ;
this.buildLookAndFeelItems() ;
this.settings.add(this.lookAndFeel) ;
}// buildSettingsMenu()
/**
* Builds and returns a language menu item.
*
* @param mnemonic
* code of the mnemonic used by the built menu item
*
* @param targetLang
* language which will be set by clicking this menu item
*
* @return Language menu item with the given values
*
* @throws FileReadingException
* if the flag icon could not be read
*/
private JMenuItem buildLanguageItem(int mnemonic,
GuiLanguage targetLang)
throws FileReadingException {
String filePath = null ;
ImageIcon icon = null ;
final Class<?> thisClass = this.getClass() ;
// Retrieve flag icon
filePath = String.format(
GUI_FLAG_ICON_FILE,
targetLang.getCountry().getCountry().toLowerCase()) ;
InputStream src = thisClass.getResourceAsStream(filePath) ;
File flagFile = new File(filePath) ;
if (src == null) {
throw new FileReadingException(flagFile) ;
}// if
icon = new ImageIcon(ImageUtility.getImageBytes(
new BufferedInputStream(src))) ;
try {
src.close() ;
} catch (IOException e) {
throw new FileReadingException(flagFile) ;
}// try
// Build menu item
JMenuItem item = new JMenuItem(icon) ;
item.setMnemonic(mnemonic) ;
item.addActionListener(new LanguageChangeListener(
this.model,
targetLang)) ;
return item ;
}// buildLanguageItem(int, GuiLanguage)
/**
* Builds all the look and feel menu items.
*/
private void buildLookAndFeelItems() {
final LookAndFeelInfo[] lafs =
UIManager.getInstalledLookAndFeels() ;
for(final LookAndFeelInfo lafInfo : lafs) {
final JMenuItem item = new JMenuItem(lafInfo.getName()) ;
this.lookAndFeel.add(item) ;
item.addActionListener(new LookAndFeelChangeListener(
lafInfo.getClassName(),
this.container,
this.model)) ;
}// for
}// buildLookAndFeelItems()
/**
* Builds the plug-in menu and adds it to this instance.
*/
private void buildPlugInsMenu() {
// Menu plug-ins
this.plugIns = new JMenu() ;
this.plugIns.setMnemonic(KeyEvent.VK_P) ;
this.add(this.plugIns) ;
// Menu item: delete analyzer
this.deleteAnalyzer = new JMenuItem() ;
this.deleteAnalyzer.setMnemonic(KeyEvent.VK_A) ;
this.deleteAnalyzer.addActionListener(
new DeleteAnalyzerListener(
this.model, this.container)) ;
this.plugIns.add(this.deleteAnalyzer) ;
// Menu item: delete generator
this.deleteGenerator = new JMenuItem() ;
this.deleteGenerator.setMnemonic(KeyEvent.VK_G) ;
this.deleteGenerator.addActionListener(
new DeleteGeneratorListener(
this.model, this.container)) ;
this.plugIns.add(this.deleteGenerator) ;
}// buildPlugInsMenu()
/**
* Listener handling the clicks on language menu items. Tells the
* model to change the current language.
*
* @author Schnell Michaël
* @version 1.0
*/
private class LanguageChangeListener implements ActionListener {
/**
* Model for this listener.
*/
private Model menuModel ;
/**
* Target for the language switch for this listener.
*/
private GuiLanguage targetLanguage ;
/**
* Builds an instance with the given values.
*
* @param menuModel
* model for this listene
*
* @param targetLanguage
* target for the language switch for this listener
*/
public LanguageChangeListener(Model menuModel,
GuiLanguage targetLanguage) {
super() ;
this.menuModel = menuModel ;
this.targetLanguage = targetLanguage ;
}// LanguageChangeListener(Model, GuiLanguage)
/**
* Causes the model to change the current language.
*
* @param e
* event fetched by this listener
*/
@Override
public void actionPerformed(ActionEvent e) {
this.menuModel.changeLanguage(this.targetLanguage) ;
}// actionPerformed(ActionEvent)
}// class LanguageChangeListener
/**
* Listener handling the clicks on look and feel menu items. Tells
* the graphical interface to change the current look and feel.
*
* @author Schnell Michaël
* @version 1.0
*/
private class LookAndFeelChangeListener implements ActionListener {
/**
* Target {@code JFrame} for the look and feel change.
*/
private JFrame menuContainer ;
/**
* Model for this listener.
*/
private Model menuModel ;
/**
* Complete class name of the target look and feel for the
* change.
*/
private String targetLookAndFeelName ;
/**
* Builds an instance with the given values.
*
* @param targetLookAndFeelName
* complete class name of the target look and feel
* for the change
*
* @param menuContainer
* target {@code JFrame} for the look and feel change
*
* @param menuModel
* model for this listener
*/
public LookAndFeelChangeListener(String targetLookAndFeelName,
JFrame menuContainer, Model menuModel) {
super();
this.targetLookAndFeelName = targetLookAndFeelName ;
this.menuContainer = menuContainer ;
this.menuModel = menuModel ;
}// LookAndFeelChangeListener(String, JFrame, Model)
/**
* Causes the container to change the current look and feel.
*
* @param e
* event fetched by this listener
*/
@Override
public void actionPerformed(ActionEvent e) {
try {
UIManager.setLookAndFeel(this.targetLookAndFeelName) ;
SwingUtilities.updateComponentTreeUI(
this.menuContainer) ;
} catch (Exception ex) {
this.menuModel.displayDebugInformation(
new InvalidLookAndFeelException(
this.targetLookAndFeelName, ex)) ;
}// try
}// actionPerformed(ActionEvent)
}// class LookAndFeelChangeListener
/**
* Listener handling the clicks on the delete an analyzer menu item.
*
* @author Schnell Michaël
* @version 1.0
*/
private class DeleteAnalyzerListener implements ActionListener {
/**
* Model for this listener.
*/
private Model menuModel ;
/**
* {@code JFrame} containing the menu attached to this
* listener.
*/
private JFrame menuContainer ;
/**
* Builds an instance with the given values.
*
* @param menuModel
* model for this listener
*
* @param menuContainer
* {@code JFrame} containing the menu attached to
* this listener
*/
public DeleteAnalyzerListener(Model menuModel,
JFrame menuContainer) {
super() ;
this.menuModel = menuModel ;
this.menuContainer = menuContainer ;
}// DeleteAnalyzerListener(Model, JFrame)
/**
* Opens a new frame which allows the user to delete an analyzer.
*
* @param e
* event fetched by this listener
*
* @see DeleteAnalyzerFrame
*/
@Override
public void actionPerformed(ActionEvent e) {
try {
new DeleteAnalyzerFrame(
this.menuContainer, this.menuModel)
.setVisible(true) ;
} catch (KameleonException ke) {
this.menuModel.displayDebugInformation(ke) ;
}// try
}// actonPerformed(ActionEvent)
}// class DeleteAnalyzerListener
/**
* Listener handling the clicks on the delete a generator menu item.
*
* @author Schnell Michaël
* @version 1.0
*/
private class DeleteGeneratorListener implements ActionListener {
/**
* Model for this listener.
*/
private Model menuModel ;
/**
* {@code JFrame} containing the menu attached to this
* listener.
*/
private JFrame menuContainer ;
/**
* Builds an instance with the given values.
*
* @param menuModel
* model for this listener
*
* @param menuContainer
* {@code JFrame} containing the menu attached to
* this listener
*/
public DeleteGeneratorListener(Model menuModel,
JFrame menuContainer) {
super() ;
this.menuModel = menuModel ;
this.menuContainer = menuContainer ;
}// DeleteGeneratorListener(Model, JFrame)
/**
* Opens a new frame which allows the user to delete a generator.
*
* @param e
* event fetched by this listener
*
* @see DeleteGeneratorFrame
*/
@Override
public void actionPerformed(ActionEvent e) {
try {
new DeleteGeneratorFrame(
this.menuContainer, this.menuModel)
.setVisible(true) ;
} catch (KameleonException ke) {
this.menuModel.displayDebugInformation(ke) ;
}// try
}// actionPerformed(ActionEvent)
}// class DeleteGeneratorListener
}// class Menu