質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.55%

javaの印刷ダイアログを表示した際のプロパティボタンを非表示にしたい

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 1,858

zekterra

score 14

お世話になります。

タイトルの通り、javaの機能で印刷ダイアログを表示させる機能があるのですが、そのダイアログの中にあるプロパティボタンを非表示、または使用不可にしたいです。

ダイアログを表示する際に使用しているメソッドはServiceUI.printDialogになります。

ServiceDialogのメソッドの一覧を見てもプロパティボタンを操作するようなメソッドもなく、ボタンがprivateで宣言されていることも確認しました。

無理なら無理でかまわないのですが、出来たらどうにかしたいと思っております。

ServiceUIとServiceDialogのソースを丸ごとコピーして、そのクラスを使用するという方法も試しましたがエラーが出てダイアログが表示されませんでした。

以下実行した際のエラー文になります。
Exception in thread "AWT-EventQueue-0" java.lang.Error: Fatal: Resource for ServiceUI is broken; there is no tab.general.vkMnemonic key in resource

すみませんがどなたかお力添えを頂ければと思います。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • 退会済みユーザー

    退会済みユーザー

    2016/10/28 11:49

    java から始まるパッケージ名にはその手法は対応できません。 対象のボタンがわかっているならば リフレクションで処理をしてみてはいかがでしょうか?

    キャンセル

回答 2

checkベストアンサー

0

ソースからボタン名の決め方などがわかるのでComponent階層を辿ってボタン名を頼りに探すという方法も使えそうですがswingにもう手が入らないであろうことを考えるとxa051さんがコメントされているようにリフレクションで直接フィールドをアクセスして求めてしまってもよいのかも知れません。その辺の判断はご自分で・・・

printDialogはダイアログを生成してshowまでやってしまうので、これをそのまま使うならボタンに対するパッチ処理は何かのイベントを契機にして行うなど若干トリッキーなことをしないといけないように思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/11/01 15:43

    回答ありがとうございます。
    リフレクションでやる場合はイベントを契機にして行うとのことでしたが、WindowEventを実装する場合、表示する画面のクラスに埋め込まないとイベントが発生しないと思いますがどういった方法で行うのでしょうか?
    printDialogを実行した1秒後にリフレクションを行うみたいな感じでしょうか?
    申し訳ございませんがやり方を教えていただければ幸いです。

    キャンセル

  • 2016/11/01 16:52

    例えばボタンを押して印刷するといった作りならばボタンを押した画面のlost focusイベントを補足するなどが考えられます。イベント補足時にはダイアログが表示されているものと期待し、自アプリケーション内部のWindowを調べてそこから辿るといった方法が使えると思います。java.awt.WindowクラスのAPIを調べてみて下さい。

    キャンセル

  • 2016/11/02 14:14

    回答ありがとうございます。
    リフレクションを調べて色々やってみましたが上手くできませんでした。
    しかし、これ以外に方法が考えられないのでベストアンサーにさせていただきます。

    キャンセル

0

質問はクローズされてしまいましたが、実装例を載せてみますのでよかったら参考にしてください。ただトリッキーな実装ですので不用意に使わない方がよいです。(質問者の方はその点おわかりと思いますが)実装に不備などありましたらご容赦ください。

  1. パッチのトリガー
    基本的には印刷を行うアクションソース(サンプルコードの例では"PRINT"ボタン)のfocusLostイベントを捕捉して、その契機でパッチを試みます。しかし印刷ダイアログが表示される前に他のウィンドウをアクティベートするとfocusLost発火時点ではまだ印刷ダイアログがないかも知れません。よって安全のためパッチが完了するまでは0.5秒おきに繰り返しパッチを試みます。

  2. パッチ可能対象の特定
    印刷ダイアログはJRE内部で生成されるため質問者さんが悩んでおられたように自然な形でのパッチはできません。そこでWindowクラスを利用してJavaで生成した全てのWindowを調べ、その中にdisplayableかつ目的のクラスのwindowを探すという方法でダイアログのインスタンスを特定します。displayableをチェックすることは重要です。peerがない状態のWindowインスタンスはそれが本当に現在表示中のダイアログのインスタンスとは限らないからです。

  3. プロパティボタンの探し方
    パッチ対象のボタンはsun.print.ServiceDialog内のタブコントロールの一つにぶら下がるsun.print.ServiceDialog.PrintServicePanelの下にあります。前者はpublicなクラスなのでisinstanceofで簡単に同定できますが、後者はprivateクラスなのでServiceDialogのComponent階層を下に辿りながらクラスのCanonicalNameを比較することで同定します。PrintServicePanelが見つかったらリフレクションで特定のフィールドにアクセスしボタンの実体が得られます。

  4. パッチ機能のクラス
    本体のアプリケーションのあちこちに実装を分散させるとよくないのでサンプルではDialogWatcherクラスの中に全てのパッチ関連機能を閉じ込めてあります。印刷ダイアログを表示する前後でこのクラスに対してパッチ開始とパッチ終了を指示するような作りです。パッチ終了の指示は通常必要ないはずですが念のため行っています。

以下実装サンプルです。

package test5;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.lang.reflect.Field;

import javax.print.DocFlavor;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.ServiceUI;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.standard.MediaSizeName;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Test5 extends JFrame {
    public static void main(String[] args) {
        new Test5().setVisible(true);
    }

    JButton printButton;

    Test5() {
        super("Test5");
        JPanel mainPanel = (JPanel)getContentPane();
        printButton = new JButton(new AbstractAction("PRINT") {
            @Override
            public void actionPerformed(ActionEvent e) {
                doPrint(e);
            }
        });
        printButton.setPreferredSize(new Dimension(200, 100));
        mainPanel.add(printButton, BorderLayout.CENTER);

        setDefaultCloseOperation(EXIT_ON_CLOSE);
        pack();
    }

    private void doPrint(ActionEvent e) {
        Component control = (Component)e.getSource();
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice[] gds = ge.getScreenDevices();
        GraphicsConfiguration gc = gds[0].getDefaultConfiguration();
        DocFlavor flavor = null;
        PrintRequestAttributeSet attributes = new HashPrintRequestAttributeSet();
        attributes.add(MediaSizeName.ISO_A4);
        PrintService[] services = PrintServiceLookup.lookupPrintServices(flavor, attributes);

        DialogWatcher.watch(true, control);
        try {
            ServiceUI.printDialog(gc, 100, 100, services, services[0], flavor, attributes);
        } finally {
            DialogWatcher.watch(false, control);
        }
    }

    static class DialogWatcher implements FocusListener, ActionListener {
        static DialogWatcher singleton = new DialogWatcher();

        static void watch(boolean watching, Component c) {
            singleton._watch(watching, c);
        }

        boolean watching = false;
        Component component;
        Timer timer;

        void _watch(boolean watching, Component c) {
            if (this.watching != watching) {
                this.watching = watching;
                if (watching) {
                    System.out.println("timer start");
                    timer = new Timer(500, this);
                    timer.setRepeats(true);
                    timer.start();
                    c.addFocusListener(this);
                    this.component = c;
                } else {
                    System.out.println("timer stop");
                    timer.stop();
                    timer = null;
                    c.removeFocusListener(this);
                }
            }
        }

        @Override
        public void focusGained(FocusEvent e) {
            System.out.format("focus gained src=%s\n", e.getSource().getClass());
            System.out.flush();
        }

        @Override
        public void focusLost(FocusEvent e) {
            System.out.format("focus lost src=%s\n", e.getSource().getClass());
            System.out.flush();
            checkWindows();
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            checkWindows();
        }

        synchronized void checkWindows() {
            if (!watching)
                return;
            sun.print.ServiceDialog dialog = null;
            Window[] windows = Window.getWindows();
            for (Window win : windows) {
                if (win.isDisplayable() && win instanceof sun.print.ServiceDialog) {
                    dialog = (sun.print.ServiceDialog)win;
                    System.out.format("we found the print dialog : %s\n", dialog.getClass());
                    patch(dialog.getContentPane());
                    _watch(false, component);
                    break;
                }
            }
        }

        static String ClassNameToPatch = "sun.print.ServiceDialog.PrintServicePanel";

        void patch(Container container) {
            if (!container.getClass().getCanonicalName().equals(ClassNameToPatch)) {
                for (Component c : container.getComponents()) {
                    if (c instanceof Container) {
                        patch((Container)c);
                    }
                }
            } else {
                System.out.println("we found the JPanel to patch");
                Class<?> clz = container.getClass();
                try {
                    Field field = clz.getDeclaredField("btnProperties");
                    field.setAccessible(true);
                    JButton button = (JButton)field.get(container);
                    button.setEnabled(false);
                    System.out.println("patch completed");
                } catch (NoSuchFieldException | SecurityException
                        | ClassCastException
                        | IllegalArgumentException | IllegalAccessException ex) {
                    System.err.println("reflection failed : " + ex.getMessage());
                }
            }
        }
    }
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/11/03 11:37

    とてもおかしいところがあるのでコメントします。synchronizedは不要です・・・試しつつ実装していたコードをまんま載せたので不格好な点があちこちに残ってます。ご容赦。

    キャンセル

  • 2016/11/07 09:28

    コードも載せていただきありがとうございます!
    とても助かりましたし、とても勉強になりました!
    今の画面に組み込めるかどうかを調査してみようと思います!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.55%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる