Chapter 16. The Style menu of SimpleEditor

Table of Contents

1. Populating the Style menu with items
2. StyleSheetInfo
3. Changing the style sheet of a StyledDocumentView

1. Populating the Style menu with items

Figure 16.1. Style menu

Style menu

The Style menu is dynamically populated with items at document load time:

    private void updateStyleMenu() {
        JMenu menu = menuBar.getMenu(STYLE_MENU);
        // [0] No Style, [1] -
        for (int i = menu.getItemCount() - 1; i >= 1; --i) {
            menu.remove(i);
        }

        if (styleSheets != null) {
            menu.addSeparator();

            for (int i = 0; i < styleSheets.length; ++i) {
                StyleSheetInfo info = styleSheets[i];

                JMenuItem item = new JMenuItem(info.getLabel());
                item.setActionCommand("setStyle" + i);
                item.addActionListener(this);
                menu.add(item);
            }
        }
    }

It contains at least one entry to display document without a style sheet (tree view).

If the document being displayed has CSS style sheets associated to it (see below), this menu also has one entry per style sheet. These entries allows the user to switch from one style sheet to another at any time.

2. StyleSheetInfo

If the XML document being loaded contains <?xml-stylesheet?> processing instructions, we need to add to it a property that represents these processing instructions. Such property is called STYLE_SHEET_INFO_PROPERTY. Adding this property is done as follows:

    private Document loadDocument(File file) {
        .
        .
        .
            StyleSheetInfo[] prop = StyleSheetInfo.loadStyleSheetPI(doc);
            if (prop != null) {
                doc.putProperty(STYLE_SHEET_INFO_PROPERTY, prop);
            }
        .
        .
        .
   }

The value of this property is an array of StyleSheetInfo objects. A StyleSheetInfo is, to make it simple, the parsed form of the <?xml-stylesheet?> PI.

This array of StyleSheetInfo, if any, is sorted in installDocument():

    private void installDocument(Document doc) {
        .
        .
        .
        if (installStyleSheets(doc)) {
            setStyle(0);
        }
    }

    private boolean installStyleSheets(Document doc) {
        StyleSheetInfo[] prop = 
            (StyleSheetInfo[]) doc.getProperty(STYLE_SHEET_INFO_PROPERTY);

        StyleSheetInfo[] list = null;
        int count = 0;
        int firstNonAlternate = -1;

        if (prop != null) {
            list = new StyleSheetInfo[prop.length];

            for (int i = 0; i < prop.length; ++i) {
                StyleSheetInfo info = prop[i];

                if (info.type.equals("text/css") &&
                    (info.media == null || info.media.equals("screen"))) {
                    if (!info.alternate && firstNonAlternate < 0) {
                        firstNonAlternate = count;
                    }
                    list[count++] = info;
                }
            }
        }
        if (count == 0) {
            return false;
        }

        if (count > 10) {
            count = 10;
        }
        if (count == list.length) {
            styleSheets = list;
        } else {
            styleSheets = new StyleSheetInfo[count];
            System.arraycopy(list, 0, styleSheets, 0, count);
        }

        if (firstNonAlternate < 0) {
            firstNonAlternate = 0;
        }
        if (firstNonAlternate != 0) {
            StyleSheetInfo swap = list[firstNonAlternate];
            if (firstNonAlternate < styleSheets.length) {
                styleSheets[firstNonAlternate] = styleSheets[0];
            }
            styleSheets[0] = swap;
        }

        updateStyleMenu();
        getMenuItem(STYLE_MENU, NO_STYLE_ITEM).setEnabled(true);

        return true;
    }

3. Changing the style sheet of a StyledDocumentView

Changing the style sheet of a StyledDocumentView is done by invoking StyledViewFactory.setStyleSheet, passing a StyleSheet object to the StyledViewFactory. Passing null is allowed in order to display a tree view.

    private void setStyle(int index) {
        StyleSheet styleSheet = null;

        if (index >= 0) {
            StyleSheetInfo info = styleSheets[index];

            styleSheet = loadStyleSheet(info.url);
            if (styleSheet == null) {
                JMenuItem item = getMenuItem(STYLE_MENU, 2+index);
                item.setEnabled(false);
            }
        }

        StyledViewFactory viewFactory = 
            (StyledViewFactory) docView.getViewFactory();
        if (styleSheet != null || viewFactory.getStyleSheet() != null) {
            setStyleSheet(viewFactory, styleSheet);
        }
    }

Note that the StyleSheet object is rebuilt from its CSS source each time the user switches from a style sheet to another. Creating a StyleSheet object from a CSS source is the job of the StyleSheetLoader.

    private StyleSheet loadStyleSheet(URL url) {
        StyleSheetLoader loader = new StyleSheetLoader();
        StyleSheet styleSheet = null;
        String error = null;

        try {
            styleSheet = loader.load(url, /*media*/ null);
        } catch (Exception e) {
            error = ThrowableUtil.reason(e);
        }

        if (styleSheet == null) {
            Alert.showError(frame, 
                           "Cannot load CSS style sheet '" + url + "':\n" + 
                            error);
        } else {
            StyleSheetLoader.Warning[] warnings = loader.getWarnings();

            if (warnings.length > 0) {
                StringBuffer messages = new StringBuffer();

                for (int j = 0; j < warnings.length; ++j) {
                    StyleSheetLoader.Warning warn = warnings[j];

                    messages.append(warn.url);
                    messages.append(':');
                    messages.append(warn.lineNumber);
                    messages.append(':');
                    messages.append(warn.columnNumber);
                    messages.append(": ");
                    messages.append(warn.message);
                    messages.append("\n\n");
                }

                Alert.showWarning(frame, 
                                  "CSS style sheet '" + url + "' has errors:",
                                  messages.toString(), true, 10, 40);
            }
        }

        return styleSheet;
    }

setStyleSheet() is equivalent to StyledViewFactory.setStyleSheet.