/* Copyright 2006 Eric Hakenholz This file is part of C.a.R. software. C.a.R. is a free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. C.a.R. is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ package eric; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.InputStreamReader; import java.net.URL; import java.util.Enumeration; import java.util.StringTokenizer; import java.util.Vector; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JEditorPane; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.metal.MetalComboBoxUI; import javax.swing.text.html.HTMLDocument; import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.StyleSheet; import rene.gui.Global; /** * Info class. Reads a file "info.txt" or "de_info.txt" etc. that has the * structure * * .subject1 substitute1 substitute2 ... .related subject subject ... Header ... * * .subject2 ... * * and displays the text, starting from header, searching for a subject or any * of its substitutes. The headers of the related subjects are presented in a * choice list. The user can switch to any of it. * * There is a history and a back button. * * Moroever, there is a search button, that displays the first subject * containing a string and presents all other subjects containing the string in * the choice list. */ public class JHelpPanel extends JEricPanel { private static final long serialVersionUID = 1L; MyJTextPane T; public static String Subject = "start"; String Search = null; // MyChoice2 L; Vector Other = null; Vector History = new Vector(); Vector Related; JEricPanel north; MyJTextSearch TSearch; public JHelpPanel() { T = new MyJTextPane(this); final JScrollPane scrollPane = new JScrollPane(T); scrollPane.setBorder(javax.swing.BorderFactory.createEmptyBorder()); setAlignmentX(0.0F); setAlignmentY(0.0F); setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.Y_AXIS)); setFocusable(false); north = new JEricPanel(); north.setLayout(new javax.swing.BoxLayout(north, javax.swing.BoxLayout.Y_AXIS)); north.setOpaque(false); TSearch = new MyJTextSearch(this); Subject = "start"; fill(true); north.add(TSearch); north.add(vmargin(2)); add(north); add(scrollPane); } public void focusTxt() { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { TSearch.JTF.requestFocus(); } }); } static JEricPanel margin(final int w) { final JEricPanel mypan = new JEricPanel(); fixsize(mypan, w, 1); mypan.setLayout(new javax.swing.BoxLayout(mypan, javax.swing.BoxLayout.X_AXIS)); mypan.setAlignmentX(0F); mypan.setAlignmentY(0F); mypan.setOpaque(false); mypan.setFocusable(false); return mypan; } static JEricPanel vmargin(final int h) { final JEricPanel mypan = new JEricPanel(); fixsize(mypan, 1, h); mypan.setLayout(new javax.swing.BoxLayout(mypan, javax.swing.BoxLayout.X_AXIS)); mypan.setAlignmentX(0F); mypan.setAlignmentY(0F); mypan.setOpaque(false); mypan.setFocusable(false); return mypan; } // Only called by LeftPanelContent init method : public void fixPanelSize(final int w, final int h) { fixsize(north, w, 24); north.revalidate(); fixsize(TSearch, w, 24); fixsize(TSearch.wholepanel, w - 20, 18); } public static void fixsize(final JComponent cp, final int w, final int h) { final Dimension d = new Dimension(w, h); cp.setMaximumSize(d); cp.setMinimumSize(d); cp.setPreferredSize(d); cp.setSize(d); } public void clearSearchTxtField() { TSearch.JTF.setText(""); } public final void fill(final boolean WithTextFocus) { final boolean vis = TSearch.ispopupvisible(); if (vis) { TSearch.hidepopup(); } TSearch.L = new MyJPopupMenu(this); T.setText(""); String TXT = ""; boolean Found = false, Appending = false; Related = null; Other = new Vector(); String pair[] = null, lastpair[] = null; String lang = Global.name("language", ""); Vector SearchResults = new Vector(); String SearchResult = ""; String FoundTopic = null; boolean FirstRun = true, FoundHeader = false; String Search1 = Search; if (Search != null && Search.length() > 0) { Search1 = Search.substring(0, 1).toUpperCase() + Search.substring(1); } read: while (true) { try { Global.name("codepage.help", ""); BufferedReader in = null; try { in = new BufferedReader(new InputStreamReader( new FileInputStream(Global.getHomeDirectory() + "docs/" + lang + "info.txt"), "UTF8")); } catch (final Exception ex) { in = new BufferedReader(new InputStreamReader( new FileInputStream(Global.getHomeDirectory() + "docs/" + lang + "info.txt"))); } boolean newline = false; newline: while (true) { String s = in.readLine(); if (s == null) { break newline; } if (s.startsWith("//")) { continue; } s = clear(s); if (!s.startsWith(".") && Search != null && (s.indexOf(Search) >= 0 || s.indexOf(Search1) >= 0)) { if (lastpair != null && pair == null && !SearchResult.equals(lastpair[0])) { SearchResults.addElement(lastpair); SearchResult = lastpair[0]; if (FoundTopic == null) { FoundTopic = lastpair[0]; } } } interpret: while (true) { if (!Appending && s.startsWith(".") && !s.startsWith(".related")) { if (!Found) { if (s.startsWith("." + Subject)) { Found = true; Appending = true; continue newline; } final StringTokenizer t = new StringTokenizer(s); while (t.hasMoreElements()) { final String name = t.nextToken(); if (name.equals(Subject)) { Found = true; Appending = true; continue newline; } } } pair = new String[2]; s = s.substring(1); final int n = s.indexOf(' '); if (n > 0) { s = s.substring(0, n); } pair[0] = s; continue newline; } if (Appending) { if (s.startsWith(".related")) { s = s.substring(".related".length()); Related = new Vector(); final StringTokenizer t = new StringTokenizer(s); while (t.hasMoreElements()) { Related.addElement(t.nextToken()); } continue newline; } if (s.startsWith(".")) { Appending = false; continue interpret; } if (s.trim().equals("")) { if (!newline) { // T.append("

"); TXT += "

"; } newline = true; } else { newline = false; if (s.startsWith(" ")) { TXT += "
"; // T.append("
"); } // T.append(s+" "); TXT += s + " "; } } else if (pair != null && !s.startsWith(".")) { pair[1] = s; Other.addElement(pair); lastpair = pair; pair = null; if (Search != null && (s.indexOf(Search) >= 0 || s.indexOf(Search1) >= 0)) { if (!SearchResult.equals(lastpair[0])) { SearchResults.addElement(lastpair); SearchResult = lastpair[0]; if (!FoundHeader) { FoundTopic = lastpair[0]; } FoundHeader = true; } } } continue newline; } } // T.append("
"); TXT += "
"; in.close(); } catch (final Exception e) { if (!lang.equals("")) { lang = ""; continue read; } else { // V.appendLine( // Global.name("help.error","Could not find the help file!")); // T.append(Global.name("help.error", // "Could not find the help file!")); TXT += Global.name("help.error", "Could not find the help file!"); } } if (FoundTopic != null && FirstRun) { Subject = FoundTopic; SearchResults = new Vector(); SearchResult = ""; pair = null; lastpair = null; Found = false; TXT = ""; // T.setText(""); // V.setText(""); FirstRun = false; continue read; } else { break read; } } // if (!Found) // V.appendLine(Global.name("info.notfound","Topic not found!")); if (!Found) { // T.append(Global.name("info.notfound", "Topic not found!")); TXT += Global.name("info.notfound", "Topic not found!"); } if (Search != null) { if (SearchResults.size() > 0) { TSearch.L.addI(Global.name("info.searchresults")); } else { TSearch.L.addI(Global.name("info.noresults")); } } else { TSearch.L.addI(Global.name("info.select")); } if (Search == null && Related != null) { final Enumeration e = Related.elements(); while (e.hasMoreElements()) { final String topic = (String) e.nextElement(); final Enumeration ev = Other.elements(); while (ev.hasMoreElements()) { final String s[] = (String[]) ev.nextElement(); if (s[0].equals(topic)) { TSearch.L.addI(s[1]); break; } } } } if (Search != null) { final Enumeration e = SearchResults.elements(); while (e.hasMoreElements()) { final String s[] = (String[]) e.nextElement(); TSearch.L.addI(s[1]); } } T.setContent(TXT); if (WithTextFocus) { focusTxt(); } T.setCaretPosition(0); History.addElement(Subject); TSearch.setCount(); if (vis) { TSearch.showpopup(); } } public String clear(String s) { s = s.replace('ß', ' '); s = s.replaceAll("__", ""); return s; } } class MyComboBoxUI extends MetalComboBoxUI { public static ComponentUI createUI(final JComponent c) { return new MyComboBoxUI(); } } class MyJTextSearch extends JEricPanel { private static final long serialVersionUID = 1L; JLabel cntLBL = new JLabel(); JTextField JTF; JHelpPanel JP; JButton popBTN, clearBTN; JEricPanel txtpanel; JEricPanel wholepanel; MyJPopupMenu L; String old = ""; @Override public void paintComponent(final java.awt.Graphics g) { super.paintComponent(g); final ImageIcon OffImage = new ImageIcon(getClass().getResource("/eric/GUI/icons/themes/common/helpsearchback.png")); final java.awt.Dimension d = this.getSize(); g.drawImage(OffImage.getImage(), 0, 0, d.width, d.height, this); } public MyJTextSearch(final JHelpPanel jp) { super(); setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.Y_AXIS)); setOpaque(false); JP = jp; L = new MyJPopupMenu(JP); txtpanel = new JEricPanel(); txtpanel.setLayout(new javax.swing.BoxLayout(txtpanel, javax.swing.BoxLayout.X_AXIS)); txtpanel.setBorder(BorderFactory.createLineBorder(new Color(150, 150, 150), 1)); txtpanel.setOpaque(true); txtpanel.setBackground(Color.WHITE); wholepanel = new JEricPanel(); wholepanel.setLayout(new javax.swing.BoxLayout(wholepanel, javax.swing.BoxLayout.X_AXIS)); wholepanel.setBorder(BorderFactory.createEmptyBorder()); wholepanel.setOpaque(false); cntLBL.setOpaque(false); cntLBL.setText(""); cntLBL.setFont(new Font("System", 0, 9)); cntLBL.setHorizontalAlignment(SwingConstants.CENTER); cntLBL.setVerticalAlignment(SwingConstants.CENTER); JHelpPanel.fixsize(cntLBL, 20, 18); JTF = new JTextField(); JTF.setFont(new Font("System", 0, 11)); JTF.setForeground(new Color(50, 50, 50)); JTF.setBackground(new Color(255, 255, 255)); JTF.setBorder(BorderFactory.createEmptyBorder()); JTF.setMargin(new java.awt.Insets(0, 0, 1, 0)); JTF.addKeyListener(new KeyAdapter() { @Override public void keyReleased(final KeyEvent e) { if (JTF.getText().equals("")) { clearBTN.setVisible(false); } else { clearBTN.setVisible(true); } if ((e.getKeyCode() == KeyEvent.VK_ENTER) || (e.getKeyCode() == KeyEvent.VK_UP)) { hidepopup(); } else if (e.getKeyCode() == KeyEvent.VK_DOWN) { showpopup(); } else { doSearch(JTF.getText()); } } }); // JTF.setFocusable(true); JTF.addFocusListener(new FocusAdapter() { @Override public void focusGained(final FocusEvent e) { JTF.selectAll(); } }); final ImageIcon carimg = new ImageIcon(getClass().getResource("/eric/GUI/icons/themes/common/helpsearch1.gif")); popBTN = new JButton(carimg); popBTN.setBorder(BorderFactory.createEmptyBorder()); popBTN.setOpaque(true); popBTN.setContentAreaFilled(true); popBTN.setFocusable(false); popBTN.addMouseListener(new MouseAdapter() { @Override public void mousePressed(final MouseEvent e) { showpopup(); } }); final ImageIcon carimg2 = new ImageIcon(getClass().getResource("/eric/GUI/icons/themes/common/helpsearch2.png")); final JButton backBTN = new JButton(carimg2); backBTN.setBorder(BorderFactory.createEmptyBorder()); backBTN.setOpaque(false); backBTN.setContentAreaFilled(false); backBTN.setFocusable(false); backBTN.addMouseListener(new MouseAdapter() { @Override public void mousePressed(final MouseEvent e) { final int n = JP.History.size(); if (n < 2) { return; } JP.History.removeElementAt(n - 1); JHelpPanel.Subject = (String) JP.History.elementAt(n - 2); JP.History.removeElementAt(n - 2); JP.fill(true); } }); final ImageIcon carimg3 = new ImageIcon(getClass().getResource("/eric/GUI/icons/themes/common/helpsearch3.gif")); clearBTN = new JButton(carimg3); clearBTN.setBorder(BorderFactory.createEmptyBorder()); clearBTN.setOpaque(true); clearBTN.setContentAreaFilled(true); clearBTN.setFocusable(false); clearBTN.addMouseListener(new MouseAdapter() { @Override public void mousePressed(final MouseEvent e) { JTF.setText(""); clearBTN.setVisible(false); doSearch(""); } }); clearBTN.setVisible(false); JHelpPanel.fixsize(popBTN, carimg.getIconWidth(), carimg.getIconHeight()); JHelpPanel.fixsize(clearBTN, carimg3.getIconWidth(), carimg3.getIconHeight()); txtpanel.add(cntLBL); txtpanel.add(popBTN); txtpanel.add(JHelpPanel.margin(3)); txtpanel.add(JTF); txtpanel.add(clearBTN); wholepanel.add(JHelpPanel.margin(5)); wholepanel.add(txtpanel); wholepanel.add(JHelpPanel.margin(3)); wholepanel.add(backBTN); this.add(JHelpPanel.vmargin(3)); this.add(wholepanel); } void showpopup() { if (L.Count > 1) { L.show(popBTN, 10, 20); } } void hidepopup() { L.setVisible(false); } boolean ispopupvisible() { return L.isVisible(); } void doSearch(final String s) { if (s.equals("")) { JP.Search = null; } else { JP.Search = s; } JHelpPanel.Subject = "start"; JP.fill(true); JP.Search = null; } void setCount() { if (L.Count > 1) { cntLBL.setText("(" + (L.Count - 1) + ")"); } else { cntLBL.setText(""); } } } class MyJPopupMenuItem extends JMenuItem { private static final long serialVersionUID = 1L; String STR; JHelpPanel JP; public MyJPopupMenuItem(final JHelpPanel jp, final String s) { super(s.replaceAll("", "").replace("
", ", ")); STR = s; JP = jp; setFont(new Font("System", 0, 11)); addMouseListener(new MouseAdapter() { @Override public void mousePressed(final MouseEvent arg0) { JP.TSearch.L.setVisible(false); JP.TSearch.JTF.setText(""); JP.TSearch.clearBTN.setVisible(false); final String s = STR; final Enumeration ev = JP.Other.elements(); while (ev.hasMoreElements()) { final String p[] = (String[]) ev.nextElement(); if (p[1].equals(s)) { JHelpPanel.Subject = p[0]; JP.fill(true); break; } } } }); } } class MyJPopupMenu extends JPopupMenu { private static final long serialVersionUID = 1L; int Count = 0; JHelpPanel JP; public MyJPopupMenu(final JHelpPanel jp) { super(); JP = jp; setFont(new Font("System", 0, 11)); setFocusable(false); } public void addI(final String s) { Count++; add(new MyJPopupMenuItem(JP, s)); } } class MyJTextPane extends JEditorPane implements HyperlinkListener { private static final long serialVersionUID = 1L; String content; HTMLEditorKit editorkit; HTMLDocument doc; JHelpPanel JP; public MyJTextPane(final JHelpPanel jp) { super(); JP = jp; this.setContentType("text/html;"); editorkit = (HTMLEditorKit) getEditorKit(); doc = (HTMLDocument) getDocument(); final StyleSheet myStyleSheet = new StyleSheet(); myStyleSheet.addRule("body {color: #333333;font-family: Verdana, Arial, Helvetica, sans-serif;" + "text-align: justify;font-size: 10px;background-color: #F1F3F9;}"); myStyleSheet.addRule("ul {text-align: left;list-style-type: disc;padding-left: 15px;list-style-position: outside;}"); myStyleSheet.addRule(".pfooter {text-align: center;font-size: 10px;padding-bottom: 5px;" + "padding-top: 5px;background-color: #eaedf3;}"); myStyleSheet.addRule(".pfootertitle {font-weight: bold;text-align: center;font-size: 10px;padding-bottom: 5px;" + "padding-top: 5px;background-color: #DBDDE3;}"); myStyleSheet.addRule("a {color: #0a9100;text-decoration: none;}"); myStyleSheet.addRule(".aanchor {color: #CC6633;}"); myStyleSheet.addRule(".centered {text-align: center;}"); myStyleSheet.addRule(".comment {text-align: center;font-size: 9px;font-style: oblique;font-weight: normal;}"); myStyleSheet.addRule(".comment2 {text-align: justify;font-size: 9px;font-style: oblique;font-weight: normal;}"); myStyleSheet.addRule(".ttle {color: #000000;font-family: Verdana, Arial, Helvetica, sans-serif;" + "text-align: center;font-weight: bold;font-size: 11px;background-color: #DBDDE3;}"); myStyleSheet.addRule(".tab {border-width: 1px;border-color: #909090;border-style:solid;}"); myStyleSheet.addRule(".tds {border-width: 1px;border-color: #909090;border-style:solid;background-color: #DBDDE3;}"); myStyleSheet.addRule(".image {border-width: 1px;border-color: #909090;border-style:solid;background-color: #F1F3F9;}"); editorkit.setStyleSheet(myStyleSheet); this.setEditable(false); addHyperlinkListener(this); } void fixImagePath() { final String SP = System.getProperty("file.separator"); // Check if there are external images (inside the docs folder of // carmetal_config directory Pattern p = Pattern.compile("(]*src=\")(images/)([^\"]+)(\"[^>]*>)", Pattern.CASE_INSENSITIVE); Matcher m = p.matcher(content); StringBuffer sb = new StringBuffer(); while (m.find()) { String myst = "

" + m.group(1) + "file:///"; myst += Global.getHomeDirectory().replace("\\", "\\\\"); myst += "docs/images/".replace("/", SP).replace("\\", "\\\\"); myst += m.group(3).replace("/", SP).replace("\\", "\\\\"); // on windows, the img tag seems to need the width and height // attribute : final ImageIcon myimg = new ImageIcon(Global.getHomeDirectory() + "docs/images/" + m.group(3)); if (myimg != null) { myst += "\" width=\"" + myimg.getIconWidth() + "\" height=\"" + myimg.getIconHeight() + "\""; } myst += m.group(4); myst += "

"; m.appendReplacement(sb, myst); } m.appendTail(sb); m.reset(); content = sb.toString(); // Check if there are internal images (inside the jar archive with root // eric/GUI/icons/ : p = Pattern.compile("(]*src=\")(#)([^\"]+)(\"[^>]*>)", Pattern.CASE_INSENSITIVE); m = p.matcher(content); sb = new StringBuffer(); while (m.find()) { final URL myIMG = Global.getPath("eric/GUI/icons/" + m.group(3)); if (myIMG == null) continue; String myst = m.group(1); myst += myIMG.toString(); myst += m.group(4); m.appendReplacement(sb, myst); } m.appendTail(sb); m.reset(); content = sb.toString(); } void setTitle() { if (content.equals("")) { return; } content = content.replace("href=\"#", "class=\"aanchor\" href=\"#"); final URL myIMG = Global.getPath("eric/GUI/icons/palette/" + JHelpPanel.Subject + ".png"); final String mytitle = content.substring(0, content.indexOf("
")); String tag = ""; if (myIMG == null) { tag = ""; tag += ""; tag += "
" + mytitle + "
"; } else { tag = ""; tag += ""; tag += ""; tag += "
" + mytitle + "

"; } content = content.replaceFirst(mytitle, tag); } void setFooter() { final Enumeration e = JP.Related.elements(); String tag = ""; while (e.hasMoreElements()) { final String topic = (String) e.nextElement(); String topicTitle = ""; final Enumeration ev = JP.Other.elements(); while (ev.hasMoreElements()) { final String s[] = (String[]) ev.nextElement(); if (s[0].equals(topic)) { topicTitle = s[1]; break; } } tag += "" + topicTitle + ""; } if (!tag.equals("")) { content += ""; content += ""; content += tag; content += "
" + Global.Loc("contexthelp.seealso") + "
"; } } void setContent(final String s) { try { content = s; fixImagePath(); setTitle(); setFooter(); editorkit.insertHTML(doc, doc.getLength(), content, 0, 0, null); } catch (final Exception ex) { } // JP.focusTxt(); } @Override public void hyperlinkUpdate(final HyperlinkEvent r) { if (r.getEventType() == javax.swing.event.HyperlinkEvent.EventType.ACTIVATED) { String lnk = ""; if (r.getDescription().startsWith("#")) { lnk = r.getDescription().substring(1); JHelpPanel.Subject = lnk; JP.clearSearchTxtField(); JP.fill(true); return; } else { lnk = r.getDescription(); if (!lnk.startsWith("http://")) { lnk = "http://" + lnk; } JBrowserLauncher.openURL(lnk); return; } } } }