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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Swing

SwingはJavaに標準で付属するグラフィック関連のクラスライブラリを指します。

Q&A

解決済

2回答

3311閲覧

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

zekterra

総合スコア30

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Swing

SwingはJavaに標準で付属するグラフィック関連のクラスライブラリを指します。

0グッド

0クリップ

投稿2016/10/28 02:28

お世話になります。

タイトルの通り、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

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2016/10/28 02:49

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

回答2

0

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

  1. パッチのトリガー

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

  1. パッチ可能対象の特定

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

  1. プロパティボタンの探し方

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

  1. パッチ機能のクラス

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

以下実装サンプルです。

java

1package test5; 2 3import java.awt.BorderLayout; 4import java.awt.Component; 5import java.awt.Container; 6import java.awt.Dimension; 7import java.awt.GraphicsConfiguration; 8import java.awt.GraphicsDevice; 9import java.awt.GraphicsEnvironment; 10import java.awt.Window; 11import java.awt.event.ActionEvent; 12import java.awt.event.ActionListener; 13import java.awt.event.FocusEvent; 14import java.awt.event.FocusListener; 15import java.lang.reflect.Field; 16 17import javax.print.DocFlavor; 18import javax.print.PrintService; 19import javax.print.PrintServiceLookup; 20import javax.print.ServiceUI; 21import javax.print.attribute.HashPrintRequestAttributeSet; 22import javax.print.attribute.PrintRequestAttributeSet; 23import javax.print.attribute.standard.MediaSizeName; 24import javax.swing.AbstractAction; 25import javax.swing.JButton; 26import javax.swing.JFrame; 27import javax.swing.JPanel; 28import javax.swing.Timer; 29 30public class Test5 extends JFrame { 31 public static void main(String[] args) { 32 new Test5().setVisible(true); 33 } 34 35 JButton printButton; 36 37 Test5() { 38 super("Test5"); 39 JPanel mainPanel = (JPanel)getContentPane(); 40 printButton = new JButton(new AbstractAction("PRINT") { 41 @Override 42 public void actionPerformed(ActionEvent e) { 43 doPrint(e); 44 } 45 }); 46 printButton.setPreferredSize(new Dimension(200, 100)); 47 mainPanel.add(printButton, BorderLayout.CENTER); 48 49 setDefaultCloseOperation(EXIT_ON_CLOSE); 50 pack(); 51 } 52 53 private void doPrint(ActionEvent e) { 54 Component control = (Component)e.getSource(); 55 GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 56 GraphicsDevice[] gds = ge.getScreenDevices(); 57 GraphicsConfiguration gc = gds[0].getDefaultConfiguration(); 58 DocFlavor flavor = null; 59 PrintRequestAttributeSet attributes = new HashPrintRequestAttributeSet(); 60 attributes.add(MediaSizeName.ISO_A4); 61 PrintService[] services = PrintServiceLookup.lookupPrintServices(flavor, attributes); 62 63 DialogWatcher.watch(true, control); 64 try { 65 ServiceUI.printDialog(gc, 100, 100, services, services[0], flavor, attributes); 66 } finally { 67 DialogWatcher.watch(false, control); 68 } 69 } 70 71 static class DialogWatcher implements FocusListener, ActionListener { 72 static DialogWatcher singleton = new DialogWatcher(); 73 74 static void watch(boolean watching, Component c) { 75 singleton._watch(watching, c); 76 } 77 78 boolean watching = false; 79 Component component; 80 Timer timer; 81 82 void _watch(boolean watching, Component c) { 83 if (this.watching != watching) { 84 this.watching = watching; 85 if (watching) { 86 System.out.println("timer start"); 87 timer = new Timer(500, this); 88 timer.setRepeats(true); 89 timer.start(); 90 c.addFocusListener(this); 91 this.component = c; 92 } else { 93 System.out.println("timer stop"); 94 timer.stop(); 95 timer = null; 96 c.removeFocusListener(this); 97 } 98 } 99 } 100 101 @Override 102 public void focusGained(FocusEvent e) { 103 System.out.format("focus gained src=%s\n", e.getSource().getClass()); 104 System.out.flush(); 105 } 106 107 @Override 108 public void focusLost(FocusEvent e) { 109 System.out.format("focus lost src=%s\n", e.getSource().getClass()); 110 System.out.flush(); 111 checkWindows(); 112 } 113 114 @Override 115 public void actionPerformed(ActionEvent e) { 116 checkWindows(); 117 } 118 119 synchronized void checkWindows() { 120 if (!watching) 121 return; 122 sun.print.ServiceDialog dialog = null; 123 Window[] windows = Window.getWindows(); 124 for (Window win : windows) { 125 if (win.isDisplayable() && win instanceof sun.print.ServiceDialog) { 126 dialog = (sun.print.ServiceDialog)win; 127 System.out.format("we found the print dialog : %s\n", dialog.getClass()); 128 patch(dialog.getContentPane()); 129 _watch(false, component); 130 break; 131 } 132 } 133 } 134 135 static String ClassNameToPatch = "sun.print.ServiceDialog.PrintServicePanel"; 136 137 void patch(Container container) { 138 if (!container.getClass().getCanonicalName().equals(ClassNameToPatch)) { 139 for (Component c : container.getComponents()) { 140 if (c instanceof Container) { 141 patch((Container)c); 142 } 143 } 144 } else { 145 System.out.println("we found the JPanel to patch"); 146 Class<?> clz = container.getClass(); 147 try { 148 Field field = clz.getDeclaredField("btnProperties"); 149 field.setAccessible(true); 150 JButton button = (JButton)field.get(container); 151 button.setEnabled(false); 152 System.out.println("patch completed"); 153 } catch (NoSuchFieldException | SecurityException 154 | ClassCastException 155 | IllegalArgumentException | IllegalAccessException ex) { 156 System.err.println("reflection failed : " + ex.getMessage()); 157 } 158 } 159 } 160 } 161}

投稿2016/11/02 09:55

編集2016/11/02 09:59
KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KSwordOfHaste

2016/11/03 02:37

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

2016/11/07 00:28

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

0

ベストアンサー

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

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

投稿2016/10/28 09:26

KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

zekterra

2016/10/31 02:38

リフレクションを使用しないといけないですか・・・ printDialogのイベントを取得したりなどかなり複雑な処理になりそうですね 一度その方法で試してみます。
KSwordOfHaste

2016/10/31 02:52

リフレクションの使用は必須ではなく、ダイアログのインスタンスが生成された後であれば2つの方法のどちらでも可能と思います。 1) Component階層を辿って目的のボタンを探す 2) リフレクションでフィールドに設定されているボタンを取り出す 1)は表示されているボタン名を頼りに探すのでボタン名がユニークであるかどうかを気にする必要があります。2)はクラス上のフィールドにボタンが設定されていることがソースから明らかなので表示名のような間接的な情報を頼りにする必要がありません。ゆえに1)よりは「調べることが少なくてすむ」という程の意味合いで「リフレクションを使ってしまってもよい」と表現しました。
zekterra

2016/11/01 06:43

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

2016/11/01 07:52

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

2016/11/02 05:14

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問