package org.geogebra.desktop.gui.menubar; import java.awt.BorderLayout; import java.awt.Font; import java.awt.SystemColor; import java.awt.Toolkit; import java.awt.datatransfer.StringSelection; import java.awt.event.ActionEvent; import java.awt.print.PageFormat; import java.io.File; import java.io.FileNotFoundException; import java.util.Scanner; import javax.swing.AbstractAction; import javax.swing.AbstractButton; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.MenuElement; import javax.swing.ScrollPaneConstants; import javax.swing.SwingUtilities; import org.geogebra.common.GeoGebraConstants; import org.geogebra.common.cas.singularws.SingularWebService; import org.geogebra.common.main.App; import org.geogebra.common.main.Localization; import org.geogebra.common.move.events.BaseEvent; import org.geogebra.common.move.ggtapi.TubeAvailabilityCheckEvent; import org.geogebra.common.move.ggtapi.events.LogOutEvent; import org.geogebra.common.move.ggtapi.events.LoginAttemptEvent; import org.geogebra.common.move.ggtapi.events.LoginEvent; import org.geogebra.common.move.ggtapi.models.GeoGebraTubeUser; import org.geogebra.common.move.ggtapi.operations.LogInOperation; import org.geogebra.common.move.views.EventRenderable; import org.geogebra.common.util.Charsets; import org.geogebra.common.util.debug.Log; import org.geogebra.common.util.lang.Unicode; import org.geogebra.desktop.export.PrintPreviewD; import org.geogebra.desktop.gui.GuiManagerD; import org.geogebra.desktop.gui.dialog.DialogManagerD; import org.geogebra.desktop.gui.layout.DockManagerD; import org.geogebra.desktop.gui.layout.LayoutD; import org.geogebra.desktop.main.AppD; import org.geogebra.desktop.main.GeoGebraPreferencesD; import org.geogebra.desktop.main.LocalizationD; import org.geogebra.desktop.move.ggtapi.models.LoginOperationD; public class GeoGebraMenuBar extends JMenuBar implements EventRenderable { private static final long serialVersionUID = 1736020764918189176L; private BaseMenu fileMenu, editMenu, optionsMenu, toolsMenu, windowMenu, helpMenu; ViewMenuApplicationD viewMenu; private final AppD app; private LayoutD layout; private AbstractButton signInButton; private AbstractAction signInAction, signInInProgressAction, signOutAction; /** * Creates new menubar * * @param app * Application * @param layout * Layout */ public GeoGebraMenuBar(AppD app, LayoutD layout) { this.layout = layout; /** * A nasty workaround to prevent any borders from being drawn. All other * elements will have a border at the top to prevent visual conflicts * while moving the toolbar / algebra input to the top / bottom. The * JMenuBar *always* draws a border at the bottom however, even if the * border set via setBorder() is empty. By drawing an one pixel border * with the color of the background we can prevent this. */ setBorder(BorderFactory.createCompoundBorder( BorderFactory.createMatteBorder(0, 0, 1, 0, SystemColor.control), BorderFactory.createEmptyBorder(1, 1, 0, 1))); this.app = app; } /** * Tells if the 3D View is shown in the current window * * @return whether 3D View is switched on */ public boolean is3DViewShown() { return viewMenu.is3DViewShown(); } /** * Initialize the menubar. No update is required after initialization. */ public void initMenubar() { removeAll(); // "File" fileMenu = new FileMenuD(app); add(fileMenu); // "Edit" editMenu = new EditMenuD(app); add(editMenu); // "View" // #3711 viewMenu = app.isApplet()? new ViewMenu(app, layout) : new // ViewMenuApplicationD(app, layout); viewMenu = new ViewMenuApplicationD(app, layout); add(viewMenu); // "Perspectives" // if(!app.isApplet()) { // perspectivesMenu = new PerspectivesMenu(app, layout); // add(perspectivesMenu); // } // "Options" optionsMenu = new OptionsMenuD(app); add(optionsMenu); // "Tools" toolsMenu = new ToolsMenuD(app); add(toolsMenu); // "Window" windowMenu = new WindowMenuD(app); if (!app.isApplet()) // just add the menu if this is not an applet we're { add(windowMenu); } // "Help" helpMenu = new HelpMenuD(app); add(helpMenu); // applets might be running in Java 6 (no JavaFX) // and not wanted for applets anyway if (!app.isApplet()) { // Add the Sign in button (force it to the far right) boolean javaFx22Available = false; try { this.getClass().getClassLoader() .loadClass("javafx.embed.swing.JFXPanel"); javaFx22Available = true; } catch (Throwable e) { Log.error("JavaFX 2.2 not available"); } // JavaFX 2.2 available by default only on Java 7u6 or higher // http://www.oracle.com/us/corporate/press/1735645 if (javaFx22Available) { // try needed for eg OSX 10.6 with fake jfxrt.jar try { add(Box.createHorizontalGlue()); addSignIn(); } catch (Exception e) { Log.error("problem starting JavaFX"); } } } // "flag" to select language // addFlag(); // support for right-to-left languages app.setComponentOrientation(this); } /** * Creates and adds the sign in button */ @SuppressWarnings("serial") private void addSignIn() { Localization loc = app.getLocalization(); signInAction = new AbstractAction(loc.getMenu("SignIn")) { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { app.getGuiManager().login(); } }; signInInProgressAction = new AbstractAction( loc.getMenu("SignInProgress")) { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { // do nothing } }; signOutAction = new AbstractAction(loc.getMenu("SignOut")) { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { app.getGuiManager().logout(); } }; if (app.isMacOS()) { signInButton = new JMenuItem(signInAction); JMenu m = new BaseMenu(app, "GeoGebraMaterials") { @Override public void update() { // TODO Auto-generated method stub } @Override protected void initActions() { // TODO Auto-generated method stub } @Override protected void initItems() { // TODO Auto-generated method stub add(signInButton); } // ); }; // m.add(signInButton); add(m); } else { signInButton = new JButton(signInAction); add(signInButton); } signInButton.setContentAreaFilled(false); signInButton.setFocusPainted(false); signInButton.setToolTipText(loc.getMenuTooltip("SignIn.Help")); // Add the menu bar as a listener for login/logout operations LogInOperation signIn = app.getLoginOperation(); signIn.getView().add(this); if (signIn.isLoggedIn()) { onLogin(true, signIn.getModel().getLoggedInUser(), true); } else if (!((LoginOperationD) signIn).isTubeAvailable()) { signInButton.setVisible(false); } } /** * Display the result of login events */ @Override public void renderEvent(final BaseEvent event) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { doRenderEvent(event); } }); } protected void doRenderEvent(BaseEvent event) { if (event instanceof LoginAttemptEvent) { signInButton.setAction(signInInProgressAction); signInButton.setVisible(true); } else if (event instanceof LogOutEvent) { signInButton.setAction(signInAction); signInButton.setVisible(true); } else if (event instanceof LoginEvent) { LoginEvent loginEvent = (LoginEvent) event; onLogin(loginEvent.isSuccessful(), loginEvent.getUser(), loginEvent.isAutomatic()); signInButton.setVisible(true); } else if (event instanceof TubeAvailabilityCheckEvent) { TubeAvailabilityCheckEvent checkEvent = (TubeAvailabilityCheckEvent) event; onTubeAvailable(checkEvent.isAvailable()); } } private void onTubeAvailable(boolean available) { if (available) { signInButton.setVisible(true); app.showPopUps(); } } private void onLogin(boolean successful, GeoGebraTubeUser user, boolean automatic) { Localization loc = app.getLocalization(); if (successful) { // Show the username in the menu signInButton.setAction(signOutAction); String username = user.getUserName(); if (app.isMacOS()) { username = app.getNormalizer().transform(username); } signInButton.setText(loc.getPlain("SignedInAsA", username)); // Show a login success message if (!automatic) { Object[] options = { loc.getMenu("OpenFromGeoGebraTube") + Unicode.ellipsis, loc.getMenu("OK") }; int n = JOptionPane.showOptionDialog(app.getMainComponent(), loc.getMenu("ThanksForSigningIn"), loc.getMenu("SignInSuccessful"), JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null, options, options[0]); if (n == 0) { ((DialogManagerD) app.getDialogManager()) .showOpenFromGGTDialog(); } else { // open perspective popup after logging in if (app.isShowDockBar()) { app.getDockBar().showPopup(); } } } } else { signInButton.setAction(signInAction); signInButton.setText(loc.getMenu("SignInError")); } } /** * Update the menubar. */ public void updateMenubar() { Log.debug("update menu"); fileMenu.update(); editMenu.update(); viewMenu.update(); optionsMenu.update(); toolsMenu.update(); // if (perspectivesMenu != null) // perspectivesMenu.update(); if (!app.isApplet()) { windowMenu.update(); } helpMenu.update(); // updateSelection(); //it's redundant here, look at editMenu.update(); } /** * Checkbox of Construction protocol view will be checked in view menu if * visible is true. Otherwise won't be checked. * * @param visible */ public void updateCPView(boolean visible) { viewMenu.updateCPView(visible); } /** * Update the selection. */ public void updateSelection() { ((EditMenuD) editMenu).updateSelection(); } /** * Update the file menu without being forced to updated the other menus as * well. */ public void updateMenuFile() { if (fileMenu != null) { fileMenu.update(); } } /** * Update the window menu without having to update the other menus as well. */ public void updateMenuWindow() { windowMenu.update(); } /** * Update the menu fonts. */ public void updateFonts() { for (int i = 0; i < this.getMenuCount(); i++) { JMenu m; if ((m = getMenu(i)) != null) { // old method // problem with keyboard shortcuts // setMenuFontRecursive(m, app.getPlainFont()); // force rebuild next time menu is opened // see BaseMenu.menuSelected() m.removeAll(); // update title (always visible) m.setFont(app.getPlainFont()); } } // Update the font of the sign in button if (signInButton != null && signInButton instanceof JButton) { signInButton.setFont(app.getPlainFont()); } } /** * @param m * @param font */ public static void setMenuFontRecursive(JMenuItem m, Font font) { // Log.debug(m.getClass()); if (m instanceof JMenu) { JPopupMenu pm = ((JMenu) m).getPopupMenu(); MenuElement[] components = pm.getSubElements(); // Log.debug(components.length); for (MenuElement com : components) { // System.out.println(m.getText()); if (com instanceof LanguageRadioButtonMenuItem) { // do nothing } else if (com instanceof JComponent) { ((JComponent) com).setFont(font); } if (com instanceof JMenuItem) { setMenuFontRecursive((JMenuItem) com, font); } // Log.debug(com.getClass()); } } if (m instanceof LanguageRadioButtonMenuItem) { m.setFont(((LanguageRadioButtonMenuItem) m).getFont() .deriveFont(font.getSize2D())); } else { m.setFont(font); } } /** * Show the print preview dialog. * * @param app */ public static void showPrintPreview(final AppD app) { try { Thread runner = new Thread() { @Override public void run() { try { app.setWaitCursor(); // use reflection for // new geogebra.export.PrintPreview(app, // app.getEuclidianView(), PageFormat.LANDSCAPE); // Class classObject = // Class.forName("geogebra.export.PrintPreview"); // Object[] args = new Object[] { app , // app.getEuclidianView(), new // Integer(PageFormat.LANDSCAPE)}; // Class [] types = new Class[] {Application.class, // Printable.class, int.class}; // Constructor constructor = // classObject.getDeclaredConstructor(types); // constructor.newInstance(args); /* * old code boolean printCAS=false; if * (((GuiManagerD)app.getGuiManager()).hasCasView()){ * DockManager * dm=((GuiManagerD)app.getGuiManager()).getLayout * ().getDockManager(); //if CAS-view has Focus, print * CAS if * (dm.getFocusedPanel()==dm.getPanel(Application. * VIEW_CAS)){ new geogebra.export.PrintPreview(app, * ((GuiManagerD)app.getGuiManager()).getCasView(), * PageFormat.LANDSCAPE); printCAS=true; } } * * if (!printCAS) new geogebra.export.PrintPreview(app, * app .getEuclidianView(), PageFormat.LANDSCAPE); */ GuiManagerD gui = (GuiManagerD) app.getGuiManager(); DockManagerD dm = gui.getLayout().getDockManager(); int viewId = (dm.getFocusedPanel() == null) ? -1 : dm.getFocusedPanel().getViewId(); PrintPreviewD pre = PrintPreviewD.get(app, viewId, PageFormat.LANDSCAPE); pre.setVisible(true); } catch (Exception e) { e.printStackTrace(); Log.debug("Print preview not available"); } finally { app.setDefaultCursor(); } } }; runner.start(); } catch (java.lang.NoClassDefFoundError ee) { app.showError(app.getLocalization().getError("ExportJarMissing")); ee.printStackTrace(); } } private static String glVersion = null; private static String glCard = null; /** * Show the "About" dialog. * * @param app */ public static void showAboutDialog(final AppD app) { final LocalizationD loc = app.getLocalization(); StringBuilder sb = new StringBuilder(); sb.append("<html><b>"); appendVersion(sb, app); sb.append("</b> ("); sb.append("Java "); AppD.appendJavaVersion(sb); sb.append(", "); // needed by the user for logging // if (!app.isApplet()) { // sb.append("IP: "); // try { // sb.append(InetAddress.getLocalHost().getHostAddress()); // sb.append(':'); // sb.append(SensorLogger.port); // } catch (UnknownHostException e) { // sb.append("<unknown>"); // } // sb.append(", "); // } sb.append(app.getHeapSize() / 1024 / 1024); sb.append("MB, "); sb.append(App.getCASVersionString()); String v; SingularWebService singularWS = app.getSingularWS(); if (singularWS != null && (v = singularWS.getSingularVersionString()) != null) { sb.append(",<br>" + v); } sb.append(")<br>"); sb.append(GeoGebraConstants.BUILD_DATE); // license String text = app.loadTextFile(AppD.LICENSE_FILE); // We may want to modify the window size when the license file changes: JTextArea textArea = new JTextArea(26, 72); // window size fine tuning // (rows, cols) JScrollPane scrollPane = new JScrollPane(textArea, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); textArea.setEditable(false); // not sure if Monospaced is installed everywhere: textArea.setFont(new Font("Monospaced", Font.PLAIN, 12)); textArea.setText(text); textArea.setCaretPosition(0); JPanel systemInfoPanel = new JPanel(new BorderLayout(5, 5)); systemInfoPanel.add(new JLabel(sb.toString()), BorderLayout.CENTER); // copy system information to clipboard systemInfoPanel.add(new JButton( new AbstractAction(loc.getPlain("SystemInformation")) { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent arg0) { copyDebugInfoToClipboard(app); app.showMessage( loc.getPlain("SystemInformationMessage")); } }), loc.borderEast()); JPanel panel = new JPanel(new BorderLayout(5, 5)); panel.add(systemInfoPanel, BorderLayout.NORTH); panel.add(scrollPane, BorderLayout.SOUTH); JOptionPane infoPane = new JOptionPane(panel, JOptionPane.PLAIN_MESSAGE, JOptionPane.DEFAULT_OPTION); final JDialog dialog = infoPane.createDialog(app.getMainComponent(), loc.getMenu("AboutLicense")); dialog.setVisible(true); } private static void appendVersion(StringBuilder sb, AppD app) { sb.append(GeoGebraConstants.APPLICATION_NAME); sb.append(" "); sb.append(app.getVersionString()); if (app.getApplet() != null) { sb.append(" Applet"); } } public static void copyDebugInfoToClipboard(AppD app) { StringBuilder sb = new StringBuilder(); sb.append("[co]"); appendVersion(sb, app); sb.append(" ("); sb.append(GeoGebraConstants.BUILD_DATE); sb.append(")\nJava: "); sb.append(System.getProperty("java.version")); sb.append("\nCodebase: "); sb.append(AppD.getCodeBase()); sb.append("\nOS: "); sb.append(System.getProperty("os.name")); sb.append("\nArchitecture: "); sb.append(System.getProperty("os.arch")); // tells us 32 or 64 bit // (Java) sb.append(" / "); sb.append(System.getenv("PROCESSOR_ARCHITECTURE")); // tells us 32 or 64 // bit (Java) sb.append("\nHeap: "); sb.append(app.getHeapSize() / 1024 / 1024); sb.append("MB\nCAS: "); sb.append(App.getCASVersionString()); if (glCard != null) { sb.append("\nGraphics Card: " + glCard); } if (glVersion != null) { sb.append("\nGL Version: " + glVersion); } sb.append("\n\n"); if (Log.getLogger() != null) { // copy the entire log to systemInfo (maybe not required at all) sb.append("GeoGebraLogger log:\n"); sb.append(Log.getEntireLog()); sb.append("\n"); } // copy file log if (app.logFile != null) { sb.append("File log from " + app.logFile.toString() + ":\n"); String NL = System.getProperty("line.separator"); Scanner scanner = null; try { scanner = new Scanner(new File(app.logFile.toString()), Charsets.UTF_8); while (scanner.hasNextLine()) { sb.append(scanner.nextLine() + NL); } } catch (FileNotFoundException e) { app.showMessage( app.getLocalization().getMenu("CannotOpenLogFile")); } finally { if (scanner != null) { scanner.close(); } } sb.append("\n"); } // append ggb file (except images) sb.append("GGB file content:\n"); sb.append(app.getXML()); sb.append("\n\n"); sb.append(app.getMacroXML()); sb.append("\n\nLibraryJavaScript:\n"); sb.append(app.getKernel().getLibraryJavaScript()); sb.append("\n\nPreferences:\n"); sb.append(GeoGebraPreferencesD.getPref().getXMLPreferences()); sb.append("[/co]"); Toolkit.getDefaultToolkit().getSystemClipboard() .setContents(new StringSelection(sb.toString()), null); } public static void setGlCard(String s) { glCard = s; } public static void setGlVersion(String s) { glVersion = s; } }