mgarin / weblaf

WebLaF is a fully open-source Look & Feel and component library written in pure Java for cross-platform desktop Swing applications.
http://weblookandfeel.com
GNU General Public License v3.0
1.13k stars 234 forks source link

issues with PrinterJob #612

Closed Abu-Abdullah closed 4 years ago

Abu-Abdullah commented 4 years ago

Hi,

i was trying to use weblaf with PrinterJob but it gives me the following:

Exception in thread "Thread-3" com.alee.managers.style.StyleException: Unable to apply style 'panel' to component: javax.swing.JSpinner$NumberEditor[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.0,border=,flags=16777225,maximumSize=,minimumSize=,preferredSize=]
    at com.alee.managers.style.data.ComponentStyle.apply(ComponentStyle.java:361)
    at com.alee.managers.style.AbstractSkin.applySkin(AbstractSkin.java:66)
    at com.alee.managers.style.StyleData.applySkin(StyleData.java:297)
    at com.alee.managers.style.StyleData.install(StyleData.java:124)
    at com.alee.managers.style.StyleManager.installSkin(StyleManager.java:1198)
    at com.alee.laf.panel.WebPanelUI.installUI(WebPanelUI.java:57)
    at java.desktop/javax.swing.JComponent.setUI(JComponent.java:685)
    at java.desktop/javax.swing.JPanel.setUI(JPanel.java:150)
    at java.desktop/javax.swing.JPanel.updateUI(JPanel.java:126)
    at java.desktop/javax.swing.JPanel.<init>(JPanel.java:86)
    at java.desktop/javax.swing.JPanel.<init>(JPanel.java:95)
    at java.desktop/javax.swing.JSpinner$DefaultEditor.<init>(JSpinner.java:621)
    at java.desktop/javax.swing.JSpinner$NumberEditor.<init>(JSpinner.java:1230)
    at java.desktop/javax.swing.JSpinner$NumberEditor.<init>(JSpinner.java:1206)
    at java.desktop/javax.swing.JSpinner$NumberEditor.<init>(JSpinner.java:1181)
    at java.desktop/javax.swing.JSpinner.createEditor(JSpinner.java:252)
    at java.desktop/javax.swing.JSpinner.<init>(JSpinner.java:157)
    at java.desktop/sun.print.ServiceDialog$CopiesPanel.<init>(ServiceDialog.java:1215)
    at java.desktop/sun.print.ServiceDialog$GeneralPanel.<init>(ServiceDialog.java:684)
    at java.desktop/sun.print.ServiceDialog.initPrintDialog(ServiceDialog.java:193)
    at java.desktop/sun.print.ServiceDialog.<init>(ServiceDialog.java:143)
    at java.desktop/javax.print.ServiceUI.printDialog(ServiceUI.java:194)
    at java.desktop/sun.print.RasterPrinterJob.printDialog(RasterPrinterJob.java:1063)
    at classes.appSystem$appSystemResults.printData(appSystem.java:1978)
    at classes.appSystem$appSystemResults$2$1.run(appSystem.java:1939)
Caused by: java.lang.ArrayIndexOutOfBoundsException: No such child: 0
    at java.desktop/java.awt.Container.getComponent(Container.java:350)
    at java.desktop/javax.swing.JSpinner$DefaultEditor.getTextField(JSpinner.java:702)
    at java.desktop/javax.swing.JSpinner$NumberEditor.setComponentOrientation(JSpinner.java:1295)
    at com.alee.laf.WebLookAndFeel.setOrientation(WebLookAndFeel.java:1526)
    at com.alee.painter.AbstractPainter.updateOrientation(AbstractPainter.java:515)
    at com.alee.painter.AbstractPainter.installPropertiesAndListeners(AbstractPainter.java:406)
    at com.alee.painter.decoration.AbstractDecorationPainter.installPropertiesAndListeners(AbstractDecorationPainter.java:159)
    at com.alee.painter.AbstractPainter.install(AbstractPainter.java:99)
    at com.alee.painter.PainterSupport.setPainter(PainterSupport.java:135)
    at com.alee.managers.style.data.ComponentStyle.apply(ComponentStyle.java:357)
    ... 24 more

code snapshot

final PrinterJob prnJob = PrinterJob.getPrinterJob();
prnJob.setPrintable(this);
final PrintRequestAttributeSet attr = new HashPrintRequestAttributeSet();
if(prnJob.printDialog(attr)) prnJob.print(attr);
mgarin commented 4 years ago

Can you provide a small executable code example which displays this issue? Because code you provided doesn't really show what exactly you're trying to print and what UI elements are involved.

Abu-Abdullah commented 4 years ago

the following is a sample code (sorry it is not clean at all)

package classes;

import com.alee.laf.WebLookAndFeel;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.net.URI;
import java.util.Vector;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.swing.*;
import javax.swing.border.TitledBorder;
import javax.swing.table.DefaultTableCellRenderer;

public class SwingDemo
{
    JDesktopPane desktopPane;
    SwingDemo()
    {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        desktopPane = new JDesktopPane();
        mySystemResults intFrame = new mySystemResults();
        intFrame.setBounds(50, 90, 200, 250);
        frame.add(desktopPane);
        frame.setSize(600, 500);
        frame.setVisible(true);

        final JPopupMenu popup = new JPopupMenu();
        final JMenuItem menuItem1 = new JMenuItem("تجربة");

        menuItem1.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
        popup.add(menuItem1);

        desktopPane.addMouseListener(new MouseAdapter()
        {
            public void mousePressed(MouseEvent e)
            {
                maybeShowPopup(e);
            }

            public void mouseReleased(MouseEvent e)
            {
                maybeShowPopup(e);
            }

            private void maybeShowPopup(MouseEvent e)
            {
                if (e.isPopupTrigger())
                {
                    if (MaknoonIslamicEncyclopedia.language)
                    {
                        popup.updateUI();
                        popup.show(e.getComponent(), e.getX() - popup.getPreferredSize().width + 2, e.getY());
                    }
                    else
                        popup.show(e.getComponent(), e.getX(), e.getY());
                }
            }
        });
    }

    public static void main(final String[] args)
    {
        WebLookAndFeel.install();
        WebLookAndFeel.setLeftToRightOrientation(false);
        new SwingDemo();
    }

    class mySystemResults extends JInternalFrame implements Printable
    {
        final JTable resultsTable;
        mySystemResults()
        {
            final Vector<Vector> resultsDataVector = new Vector<Vector>(10, 10);

            resultsDataVector.addElement(toVector("", "تجربة", "<html><font color=red>تجربة"));
            resultsDataVector.addElement(toVector("", "", ""));

            setTitle("تجربة");
            setLayout(new BorderLayout());
            setMaximizable(true);
            setResizable(true);

            final Vector<String> columnNames = new Vector<>();
            columnNames.add("1");
            columnNames.add("3");
            columnNames.add("3");

            resultsTable = new JTable(resultsDataVector, columnNames){public boolean isCellEditable(int row, int col){return false;}};

            final DefaultTableCellRenderer renderer = new DefaultTableCellRenderer();
            renderer.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);

            final JPanel zakatMainPanel= new JPanel(new BorderLayout());
            zakatMainPanel.add(new JScrollPane(resultsTable));

            final JPanel selectedZakatPanel = new JPanel(new BorderLayout());
            selectedZakatPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "test", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_JUSTIFICATION, null, Color.red));

            final JPanel mainSelectedZakatPanel = new JPanel();
            mainSelectedZakatPanel.setLayout(new BoxLayout(mainSelectedZakatPanel, BoxLayout.Y_AXIS));
            selectedZakatPanel.add(mainSelectedZakatPanel, BorderLayout.CENTER);

            final JPanel decoratePanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
            decoratePanel.add(new JLabel("<html><font color=maroon>تجربة"));
            mainSelectedZakatPanel.add(decoratePanel);

            final JPanel printPanel = new JPanel(new BorderLayout());
            final JButton printButton = new JButton ("print");
            printButton.addActionListener(new ActionListener()
            {
                public void actionPerformed(ActionEvent e)
                {
                    Thread runner = new Thread() {public void run() {printData();}};
                    runner.start();
                }
            });

            printPanel.add(printButton, BorderLayout.SOUTH);
            selectedZakatPanel.add(printPanel, BorderLayout.WEST);

            zakatMainPanel.add(selectedZakatPanel, BorderLayout.NORTH);
            getContentPane().add(zakatMainPanel);
            desktopPane.add(this);
            pack();

            getContentPane().applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
            applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);

            setVisible(true);
        }

        private Vector<String> toVector(String e1, String e2, String e3)
        {
            Vector<String> v = new Vector<String>();
            v.addElement(e1);
            v.addElement(e2);
            v.addElement(e3);

            return v;
        }

        public void printData()
        {
            try
            {
                final PrinterJob prnJob = PrinterJob.getPrinterJob();
                prnJob.setPrintable(this);
                final PrintRequestAttributeSet attr = new HashPrintRequestAttributeSet();
                if(prnJob.printDialog(attr)) prnJob.print(attr);
            }
            catch (PrinterException e)
            {
                e.printStackTrace();
            }
        }

        public int print(Graphics pg, PageFormat pageFormat, int pageIndex)
        {
            return PAGE_EXISTS;
        }
    }
}

the issue is with using:

WebLookAndFeel.setLeftToRightOrientation(false);

in normal LTR, it works just fine

i have another small issue with popup and pointer location in RTL. just right click on JDesktopPane and the list is not aligned with the pointer. i believe it is the popup shadow size that is causing the pointer to be away from the list. any hint here is appreciated.

mgarin commented 4 years ago

Thanks for the example! I'll look into this quite soon.


Regarding the popup menu offset - there is already an issue added for it - #292 - I'm not yet sure about how or when I will be fixing this problem, so for now - you can add a custom offset if you're only using WebLaF. And yes, you are correct - offset is caused by the menu decoration that is painted on the popup window root pane, the actual window is positioned exactly at the point you've specified.


A bit unrelated to the issue, but I saw you've been using updateUI() on popup menu in the example - I strongly recommend not to do that. And that applies to all UI elements, not just the menu, for a few reasons:

  1. First and foremost - using updateUI() is a wrong approach for updating your component, use revalidate() and repaint() instead, together if necessary. In case of data components like JTable making a correct model will always ensure that component stays visually up-to-date. In this particular case with menu it is completely unnecessary and might actually cause issues, so I recommend removing it.

  2. Calling updateUI() will always (or sometimes, depending on component used) completely reset UI implementation and all related resources and create it again from a scratch. This is very ineffective and will certainly cause UI performance issues when used on complex components or used often across multiple components.

  3. As mentioned before - UI update method exists for fully resetting component's UI implementation and often used by developers as a band-aid to either fix UI element bugs or incorrect use of element models.

In case you ever see any issues with any WebLaF UI elements - I recommend reporting it as an issue here instead of trying to fix it with updateUI(), even if it does seem to work.

mgarin commented 4 years ago

I've tried the code example and it works correctly for me (with both RTL and LTR). Tested it on a few JDK6 and JDK8 versions under Windows 8 and Windows 10.

So a few more questions:

Abu-Abdullah commented 4 years ago

you are right, it seems the issue is with the JDK 10 and above. i tried it with oracle JDK8 and it works fine. it gives me this error with jdk10/jdk13/jdk14

Abu-Abdullah commented 4 years ago

WebLaF 1.2.12 latest from maven on windows 10

mgarin commented 4 years ago

I'll try it on newer JDKs.

mgarin commented 4 years ago

It does indeed reproduce on JDK9 and later and only on RTL orientation.

I also found the cause of the issue, it's this piece of code added to JSpinner.NumberEditor:

        /**
         * {@inheritDoc}
         */
        @Override
        public void setComponentOrientation(ComponentOrientation o) {
            super.setComponentOrientation(o);
            getTextField().setHorizontalAlignment(
                    o.isLeftToRight() ? JTextField.RIGHT : JTextField.LEFT);
        }

It appeared in JDK9 first time.

The problem with this method is that call to getTextField() causes the exception you're seeing because it is called in the NumberEditor (which is a JPanel) UI initialization cycle, at that point the actual editor field simply doesn't exist.

I guess this issue haven't been raised with other L&Fs yet because they either do not have a global orientation setting or simply because no one seen that issue with later JDK versions on RTL orientation.

I'll add a dirty fix for this in the orientation update method which will simply skip NumberEditor on JDK9 and later. This will result in NumberEditor not having correct orientation internally, but it doesn't really affect anything visually.

mgarin commented 4 years ago

I've pushed the fix, it will be shortly available in snapshot.

mgarin commented 4 years ago

Fix is now available in snapshot version and will be included in v1.3.0 update.

mgarin commented 4 years ago

Just a small note - this will be available in v1.2.13 update that will be going live shortly. I will take some more time to polish v1.3.0 and meanwhile will release a few smaller updates.