1 package be
.nikiroo
.fanfix
.reader
.ui
;
3 import java
.awt
.BorderLayout
;
6 import java
.awt
.Toolkit
;
7 import java
.awt
.datatransfer
.DataFlavor
;
8 import java
.awt
.event
.ActionEvent
;
9 import java
.awt
.event
.ActionListener
;
10 import java
.awt
.event
.KeyEvent
;
11 import java
.awt
.event
.MouseEvent
;
12 import java
.awt
.event
.WindowEvent
;
14 import java
.io
.IOException
;
16 import java
.net
.UnknownHostException
;
17 import java
.util
.ArrayList
;
18 import java
.util
.HashMap
;
19 import java
.util
.List
;
21 import java
.util
.Map
.Entry
;
23 import javax
.swing
.BoxLayout
;
24 import javax
.swing
.JFileChooser
;
25 import javax
.swing
.JFrame
;
26 import javax
.swing
.JLabel
;
27 import javax
.swing
.JMenu
;
28 import javax
.swing
.JMenuBar
;
29 import javax
.swing
.JMenuItem
;
30 import javax
.swing
.JOptionPane
;
31 import javax
.swing
.JPanel
;
32 import javax
.swing
.JPopupMenu
;
33 import javax
.swing
.JScrollPane
;
34 import javax
.swing
.SwingConstants
;
35 import javax
.swing
.SwingUtilities
;
36 import javax
.swing
.filechooser
.FileFilter
;
37 import javax
.swing
.filechooser
.FileNameExtensionFilter
;
39 import be
.nikiroo
.fanfix
.Instance
;
40 import be
.nikiroo
.fanfix
.bundles
.Config
;
41 import be
.nikiroo
.fanfix
.bundles
.UiConfig
;
42 import be
.nikiroo
.fanfix
.data
.MetaData
;
43 import be
.nikiroo
.fanfix
.data
.Story
;
44 import be
.nikiroo
.fanfix
.library
.BasicLibrary
;
45 import be
.nikiroo
.fanfix
.library
.BasicLibrary
.Status
;
46 import be
.nikiroo
.fanfix
.library
.LocalLibrary
;
47 import be
.nikiroo
.fanfix
.output
.BasicOutput
.OutputType
;
48 import be
.nikiroo
.fanfix
.reader
.BasicReader
;
49 import be
.nikiroo
.fanfix
.reader
.ui
.GuiReaderBook
.BookActionListener
;
50 import be
.nikiroo
.utils
.Progress
;
51 import be
.nikiroo
.utils
.Version
;
52 import be
.nikiroo
.utils
.ui
.ConfigEditor
;
53 import be
.nikiroo
.utils
.ui
.ProgressBar
;
56 * A {@link Frame} that will show a {@link GuiReaderBook} item for each
57 * {@link Story} in the main cache ({@link Instance#getCache()}), and offer a
58 * way to copy them to the {@link GuiReader} cache (
59 * {@link BasicReader#getLibrary()}), read them, delete them...
63 class GuiReaderFrame
extends JFrame
{
64 private static final long serialVersionUID
= 1L;
65 private GuiReader reader
;
66 private Map
<GuiReaderGroup
, String
> booksByType
;
67 private Map
<GuiReaderGroup
, String
> booksByAuthor
;
70 private ProgressBar pgBar
;
72 private GuiReaderBook selectedBook
;
73 private boolean words
; // words or authors (secondary info on books)
76 * A {@link Runnable} with a {@link Story} parameter.
80 private interface StoryRunnable
{
87 public void run(Story story
);
91 * Create a new {@link GuiReaderFrame}.
94 * the associated {@link GuiReader} to forward some commands and
95 * access its {@link LocalLibrary}
97 * the type of {@link Story} to load, or NULL for all types
99 public GuiReaderFrame(GuiReader reader
, String type
) {
100 super(String
.format("Fanfix %s Library", Version
.getCurrentVersion()));
102 this.reader
= reader
;
104 setDefaultCloseOperation(JFrame
.EXIT_ON_CLOSE
);
106 setLayout(new BorderLayout());
109 pane
.setLayout(new BoxLayout(pane
, BoxLayout
.PAGE_AXIS
));
111 Integer icolor
= Instance
.getUiConfig().getColor(
112 UiConfig
.BACKGROUND_COLOR
);
113 if (icolor
!= null) {
114 color
= new Color(icolor
);
115 setBackground(color
);
116 pane
.setBackground(color
);
119 JScrollPane scroll
= new JScrollPane(pane
);
120 scroll
.getVerticalScrollBar().setUnitIncrement(16);
121 add(scroll
, BorderLayout
.CENTER
);
123 String message
= reader
.getLibrary().getLibraryName();
124 if (!message
.isEmpty()) {
125 JLabel name
= new JLabel(message
, SwingConstants
.CENTER
);
126 add(name
, BorderLayout
.NORTH
);
129 pgBar
= new ProgressBar();
130 add(pgBar
, BorderLayout
.SOUTH
);
132 pgBar
.addActionListener(new ActionListener() {
134 public void actionPerformed(ActionEvent e
) {
136 pgBar
.setProgress(null);
142 pgBar
.addUpdateListener(new ActionListener() {
144 public void actionPerformed(ActionEvent e
) {
151 booksByType
= new HashMap
<GuiReaderGroup
, String
>();
152 booksByAuthor
= new HashMap
<GuiReaderGroup
, String
>();
154 pane
.setVisible(false);
155 final Progress pg
= new Progress();
156 final String typeF
= type
;
157 outOfUi(pg
, new Runnable() {
160 BasicLibrary lib
= GuiReaderFrame
.this.reader
.getLibrary();
161 Status status
= lib
.getStatus();
163 if (status
== Status
.READY
) {
166 setJMenuBar(createMenu(true));
167 addBookPane(typeF
, true);
170 pane
.setVisible(true);
173 setJMenuBar(createMenu(false));
176 String err
= lib
.getLibraryName() + "\n";
179 err
+= "Library not valid";
183 err
+= "You are not allowed to access this library";
187 err
+= "Library currently unavilable";
191 err
+= "An error occured when contacting the library";
195 error(err
, "Library error", null);
203 private void addSourcePanes() {
205 GuiReaderGroup bookPane
= new GuiReaderGroup(reader
, "Sources", color
);
207 List
<MetaData
> sources
= new ArrayList
<MetaData
>();
208 for (String source
: reader
.getLibrary().getSources()) {
209 MetaData mSource
= new MetaData();
210 mSource
.setLuid(null);
211 mSource
.setTitle(source
);
212 mSource
.setSource(source
);
213 sources
.add(mSource
);
216 bookPane
.refreshBooks(sources
, false);
224 bookPane
.setActionListener(new BookActionListener() {
226 public void select(GuiReaderBook book
) {
231 public void popupRequested(GuiReaderBook book
, MouseEvent e
) {
232 JPopupMenu popup
= new JPopupMenu();
233 popup
.add(createMenuItemOpenBook());
234 popup
.show(e
.getComponent(), e
.getX(), e
.getY());
238 public void action(final GuiReaderBook book
) {
240 addBookPane(book
.getMeta().getSource(), true);
247 * Add a new {@link GuiReaderGroup} on the frame to display the books of the
248 * selected type or author.
251 * the author or the type, or NULL to get all the
254 * TRUE for type, FALSE for author
256 private void addBookPane(String value
, boolean type
) {
259 if (Instance
.getUiConfig().getBoolean(UiConfig
.SOURCE_PAGE
,
263 for (String tt
: reader
.getLibrary().getSources()) {
265 addBookPane(tt
, type
);
270 for (String tt
: reader
.getLibrary().getAuthors()) {
272 addBookPane(tt
, type
);
280 GuiReaderGroup bookPane
= new GuiReaderGroup(reader
, value
, color
);
282 booksByType
.put(bookPane
, value
);
284 booksByAuthor
.put(bookPane
, value
);
293 bookPane
.setActionListener(new BookActionListener() {
295 public void select(GuiReaderBook book
) {
300 public void popupRequested(GuiReaderBook book
, MouseEvent e
) {
301 JPopupMenu popup
= new JPopupMenu();
302 popup
.add(createMenuItemOpenBook());
303 popup
.addSeparator();
304 popup
.add(createMenuItemExport());
305 popup
.add(createMenuItemMove(true));
306 popup
.add(createMenuItemSetCover());
307 popup
.add(createMenuItemClearCache());
308 popup
.add(createMenuItemRedownload());
309 popup
.addSeparator();
310 popup
.add(createMenuItemDelete());
311 popup
.show(e
.getComponent(), e
.getX(), e
.getY());
315 public void action(final GuiReaderBook book
) {
321 private void removeBookPanes() {
323 booksByAuthor
.clear();
332 * Refresh the list of {@link GuiReaderBook}s from disk.
335 private void refreshBooks() {
336 for (GuiReaderGroup group
: booksByType
.keySet()) {
337 List
<MetaData
> stories
= reader
.getLibrary().getListBySource(
338 booksByType
.get(group
));
339 group
.refreshBooks(stories
, words
);
342 for (GuiReaderGroup group
: booksByAuthor
.keySet()) {
343 List
<MetaData
> stories
= reader
.getLibrary().getListByAuthor(
344 booksByAuthor
.get(group
));
345 group
.refreshBooks(stories
, words
);
353 * Create the main menu bar.
356 * the library can be queried
360 private JMenuBar
createMenu(boolean libOk
) {
361 bar
= new JMenuBar();
363 JMenu file
= new JMenu("File");
364 file
.setMnemonic(KeyEvent
.VK_F
);
366 JMenuItem imprt
= new JMenuItem("Import URL...", KeyEvent
.VK_U
);
367 imprt
.addActionListener(new ActionListener() {
369 public void actionPerformed(ActionEvent e
) {
373 JMenuItem imprtF
= new JMenuItem("Import File...", KeyEvent
.VK_F
);
374 imprtF
.addActionListener(new ActionListener() {
376 public void actionPerformed(ActionEvent e
) {
380 JMenuItem exit
= new JMenuItem("Exit", KeyEvent
.VK_X
);
381 exit
.addActionListener(new ActionListener() {
383 public void actionPerformed(ActionEvent e
) {
384 GuiReaderFrame
.this.dispatchEvent(new WindowEvent(
385 GuiReaderFrame
.this, WindowEvent
.WINDOW_CLOSING
));
389 file
.add(createMenuItemOpenBook());
390 file
.add(createMenuItemExport());
391 file
.add(createMenuItemMove(libOk
));
400 JMenu edit
= new JMenu("Edit");
401 edit
.setMnemonic(KeyEvent
.VK_E
);
403 edit
.add(createMenuItemClearCache());
404 edit
.add(createMenuItemRedownload());
406 edit
.add(createMenuItemDelete());
410 JMenu view
= new JMenu("View");
411 view
.setMnemonic(KeyEvent
.VK_V
);
412 JMenuItem vauthors
= new JMenuItem("Author");
413 vauthors
.setMnemonic(KeyEvent
.VK_A
);
414 vauthors
.addActionListener(new ActionListener() {
416 public void actionPerformed(ActionEvent e
) {
422 JMenuItem vwords
= new JMenuItem("Word count");
423 vwords
.setMnemonic(KeyEvent
.VK_W
);
424 vwords
.addActionListener(new ActionListener() {
426 public void actionPerformed(ActionEvent e
) {
434 JMenu sources
= new JMenu("Sources");
435 sources
.setMnemonic(KeyEvent
.VK_S
);
437 List
<String
> tt
= new ArrayList
<String
>();
439 tt
.addAll(reader
.getLibrary().getSources());
443 for (final String type
: tt
) {
444 JMenuItem item
= new JMenuItem(type
== null ?
"All" : type
);
445 item
.addActionListener(new ActionListener() {
447 public void actionPerformed(ActionEvent e
) {
449 addBookPane(type
, true);
456 sources
.addSeparator();
462 JMenu authors
= new JMenu("Authors");
463 authors
.setMnemonic(KeyEvent
.VK_A
);
465 List
<String
> aa
= new ArrayList
<String
>();
467 aa
.addAll(reader
.getLibrary().getAuthors());
470 for (final String author
: aa
) {
471 JMenuItem item
= new JMenuItem(author
== null ?
"All"
472 : author
.isEmpty() ?
"[unknown]" : author
);
473 item
.addActionListener(new ActionListener() {
475 public void actionPerformed(ActionEvent e
) {
477 addBookPane(author
, false);
483 if (author
== null || author
.isEmpty()) {
484 authors
.addSeparator();
490 JMenu options
= new JMenu("Options");
491 options
.setMnemonic(KeyEvent
.VK_O
);
492 options
.add(createMenuItemConfig());
493 options
.add(createMenuItemUiConfig());
500 * Create the Fanfix Configuration menu item.
504 private JMenuItem
createMenuItemConfig() {
505 final String title
= "Fanfix Configuration";
506 JMenuItem item
= new JMenuItem(title
);
507 item
.setMnemonic(KeyEvent
.VK_F
);
509 item
.addActionListener(new ActionListener() {
511 public void actionPerformed(ActionEvent e
) {
512 ConfigEditor
<Config
> ed
= new ConfigEditor
<Config
>(
513 Config
.class, Instance
.getConfig(),
514 "This is where you configure the options of the program.");
515 JFrame frame
= new JFrame(title
);
517 frame
.setSize(800, 600);
518 frame
.setVisible(true);
526 * Create the UI Configuration menu item.
530 private JMenuItem
createMenuItemUiConfig() {
531 final String title
= "UI Configuration";
532 JMenuItem item
= new JMenuItem(title
);
533 item
.setMnemonic(KeyEvent
.VK_U
);
535 item
.addActionListener(new ActionListener() {
537 public void actionPerformed(ActionEvent e
) {
538 ConfigEditor
<UiConfig
> ed
= new ConfigEditor
<UiConfig
>(
539 UiConfig
.class, Instance
.getUiConfig(),
540 "This is where you configure the graphical appearence of the program.");
541 JFrame frame
= new JFrame(title
);
543 frame
.setSize(800, 600);
544 frame
.setVisible(true);
552 * Create the export menu item.
556 private JMenuItem
createMenuItemExport() {
557 final JFileChooser fc
= new JFileChooser();
558 fc
.setAcceptAllFileFilterUsed(false);
560 final Map
<FileFilter
, OutputType
> filters
= new HashMap
<FileFilter
, OutputType
>();
561 for (OutputType type
: OutputType
.values()) {
562 String ext
= type
.getDefaultExtension(false);
563 String desc
= type
.getDesc(false);
565 if (ext
== null || ext
.isEmpty()) {
566 filters
.put(createAllFilter(desc
), type
);
568 filters
.put(new FileNameExtensionFilter(desc
, ext
), type
);
572 // First the "ALL" filters, then, the extension filters
573 for (Entry
<FileFilter
, OutputType
> entry
: filters
.entrySet()) {
574 if (!(entry
.getKey() instanceof FileNameExtensionFilter
)) {
575 fc
.addChoosableFileFilter(entry
.getKey());
578 for (Entry
<FileFilter
, OutputType
> entry
: filters
.entrySet()) {
579 if (entry
.getKey() instanceof FileNameExtensionFilter
) {
580 fc
.addChoosableFileFilter(entry
.getKey());
585 JMenuItem export
= new JMenuItem("Save as...", KeyEvent
.VK_S
);
586 export
.addActionListener(new ActionListener() {
588 public void actionPerformed(ActionEvent e
) {
589 if (selectedBook
!= null) {
590 fc
.showDialog(GuiReaderFrame
.this, "Save");
591 if (fc
.getSelectedFile() != null) {
592 final OutputType type
= filters
.get(fc
.getFileFilter());
593 final String path
= fc
.getSelectedFile()
595 + type
.getDefaultExtension(false);
596 final Progress pg
= new Progress();
597 outOfUi(pg
, new Runnable() {
601 reader
.getLibrary().export(
602 selectedBook
.getMeta().getLuid(),
604 } catch (IOException e
) {
605 Instance
.getTraceHandler().error(e
);
618 * Create a {@link FileFilter} that accepts all files and return the given
626 private FileFilter
createAllFilter(final String desc
) {
627 return new FileFilter() {
629 public String
getDescription() {
634 public boolean accept(File f
) {
641 * Create the refresh (delete cache) menu item.
645 private JMenuItem
createMenuItemClearCache() {
646 JMenuItem refresh
= new JMenuItem("Clear cache", KeyEvent
.VK_C
);
647 refresh
.addActionListener(new ActionListener() {
649 public void actionPerformed(ActionEvent e
) {
650 if (selectedBook
!= null) {
651 outOfUi(null, new Runnable() {
654 reader
.clearLocalReaderCache(selectedBook
.getMeta()
656 selectedBook
.setCached(false);
657 GuiReaderBook
.clearIcon(selectedBook
.getMeta());
658 SwingUtilities
.invokeLater(new Runnable() {
661 selectedBook
.repaint();
674 * Create the delete menu item.
677 * the library can be queried
681 private JMenuItem
createMenuItemMove(boolean libOk
) {
682 JMenu moveTo
= new JMenu("Move to...");
683 moveTo
.setMnemonic(KeyEvent
.VK_M
);
685 List
<String
> types
= new ArrayList
<String
>();
688 types
.addAll(reader
.getLibrary().getSources());
691 for (String type
: types
) {
692 JMenuItem item
= new JMenuItem(type
== null ?
"New type..." : type
);
696 moveTo
.addSeparator();
699 final String ftype
= type
;
700 item
.addActionListener(new ActionListener() {
702 public void actionPerformed(ActionEvent e
) {
703 if (selectedBook
!= null) {
706 Object rep
= JOptionPane
.showInputDialog(
707 GuiReaderFrame
.this, "Move to:",
709 JOptionPane
.QUESTION_MESSAGE
, null, null,
710 selectedBook
.getMeta().getSource());
716 type
= rep
.toString();
719 final String ftype
= type
;
720 outOfUi(null, new Runnable() {
723 reader
.changeType(selectedBook
.getMeta()
728 SwingUtilities
.invokeLater(new Runnable() {
731 setJMenuBar(createMenu(true));
745 * Create the redownload (then delete original) menu item.
749 private JMenuItem
createMenuItemRedownload() {
750 JMenuItem refresh
= new JMenuItem("Redownload", KeyEvent
.VK_R
);
751 refresh
.addActionListener(new ActionListener() {
753 public void actionPerformed(ActionEvent e
) {
754 if (selectedBook
!= null) {
755 final MetaData meta
= selectedBook
.getMeta();
756 imprt(meta
.getUrl(), new StoryRunnable() {
758 public void run(Story story
) {
759 reader
.delete(meta
.getLuid());
760 GuiReaderFrame
.this.selectedBook
= null;
761 MetaData newMeta
= story
.getMeta();
762 if (!newMeta
.getSource().equals(meta
.getSource())) {
763 reader
.changeType(newMeta
.getLuid(),
767 }, "Removing old copy");
776 * Create the delete menu item.
780 private JMenuItem
createMenuItemDelete() {
781 JMenuItem delete
= new JMenuItem("Delete", KeyEvent
.VK_D
);
782 delete
.addActionListener(new ActionListener() {
784 public void actionPerformed(ActionEvent e
) {
785 if (selectedBook
!= null) {
786 outOfUi(null, new Runnable() {
789 reader
.delete(selectedBook
.getMeta().getLuid());
801 * Create the open menu item for a book or a source (no LUID).
805 private JMenuItem
createMenuItemOpenBook() {
806 JMenuItem open
= new JMenuItem("Open", KeyEvent
.VK_O
);
807 open
.addActionListener(new ActionListener() {
809 public void actionPerformed(ActionEvent e
) {
810 if (selectedBook
!= null) {
811 if (selectedBook
.getMeta().getLuid() == null) {
813 addBookPane(selectedBook
.getMeta().getSource(), true);
816 openBook(selectedBook
);
826 * Create the SetCover menu item for a book to change the linked source
831 private JMenuItem
createMenuItemSetCover() {
832 JMenuItem open
= new JMenuItem("Set as cover for source", KeyEvent
.VK_C
);
833 open
.addActionListener(new ActionListener() {
835 public void actionPerformed(ActionEvent e
) {
836 if (selectedBook
!= null) {
837 reader
.getLibrary().setSourceCover(
838 selectedBook
.getMeta().getSource(),
839 selectedBook
.getMeta().getLuid());
840 MetaData source
= selectedBook
.getMeta().clone();
841 source
.setLuid(null);
842 GuiReaderBook
.clearIcon(source
);
851 * Open a {@link GuiReaderBook} item.
854 * the {@link GuiReaderBook} to open
856 private void openBook(final GuiReaderBook book
) {
857 final Progress pg
= new Progress();
858 outOfUi(pg
, new Runnable() {
862 reader
.read(book
.getMeta().getLuid(), pg
);
863 SwingUtilities
.invokeLater(new Runnable() {
866 book
.setCached(true);
869 } catch (IOException e
) {
870 // TODO: error message?
871 Instance
.getTraceHandler().error(e
);
878 * Process the given action out of the Swing UI thread and link the given
879 * {@link ProgressBar} to the action.
881 * The code will make sure that the {@link ProgressBar} (if not NULL) is set
882 * to done when the action is done.
885 * the {@link ProgressBar} or NULL
889 private void outOfUi(Progress progress
, final Runnable run
) {
890 final Progress pg
= new Progress();
891 final Progress reload
= new Progress("Reload books");
892 if (progress
== null) {
893 progress
= new Progress();
896 pg
.addProgress(progress
, 90);
897 pg
.addProgress(reload
, 10);
900 pgBar
.setProgress(pg
);
904 new Thread(new Runnable() {
913 // will trigger pgBar ActionListener:
918 }, "outOfUi thread").start();
922 * Import a {@link Story} into the main {@link LocalLibrary}.
924 * Should be called inside the UI thread.
927 * TRUE for an {@link URL}, false for a {@link File}
929 private void imprt(boolean askUrl
) {
930 JFileChooser fc
= new JFileChooser();
934 String clipboard
= "";
936 clipboard
= ("" + Toolkit
.getDefaultToolkit()
937 .getSystemClipboard().getData(DataFlavor
.stringFlavor
))
939 } catch (Exception e
) {
940 // No data will be handled
943 if (clipboard
== null || !clipboard
.startsWith("http")) {
947 url
= JOptionPane
.showInputDialog(GuiReaderFrame
.this,
948 "url of the story to import?", "Importing from URL",
949 JOptionPane
.QUESTION_MESSAGE
, null, null, clipboard
);
950 } else if (fc
.showOpenDialog(this) != JFileChooser
.CANCEL_OPTION
) {
951 url
= fc
.getSelectedFile().getAbsolutePath();
956 if (url
!= null && !url
.toString().isEmpty()) {
957 imprt(url
.toString(), null, null);
962 * Actually import the {@link Story} into the main {@link LocalLibrary}.
964 * Should be called inside the UI thread.
967 * the {@link Story} to import by {@link URL}
969 * Action to execute on success
971 private void imprt(final String url
, final StoryRunnable onSuccess
,
972 String onSuccessPgName
) {
973 final Progress pg
= new Progress();
974 final Progress pgImprt
= new Progress();
975 final Progress pgOnSuccess
= new Progress(onSuccessPgName
);
976 pg
.addProgress(pgImprt
, 95);
977 pg
.addProgress(pgOnSuccess
, 5);
979 outOfUi(pg
, new Runnable() {
985 story
= reader
.getLibrary().imprt(BasicReader
.getUrl(url
),
987 } catch (IOException e
) {
991 final Exception e
= ex
;
993 final boolean ok
= (e
== null);
995 pgOnSuccess
.setProgress(0);
997 if (e
instanceof UnknownHostException
) {
998 error("URL not supported: " + url
, "Cannot import URL",
1001 error("Failed to import " + url
+ ": \n"
1002 + e
.getMessage(), "Cannot import URL", e
);
1005 if (onSuccess
!= null) {
1006 onSuccess
.run(story
);
1015 * Enables or disables this component, depending on the value of the
1016 * parameter <code>b</code>. An enabled component can respond to user input
1017 * and generate events. Components are enabled initially by default.
1019 * Disabling this component will also affect its children.
1022 * If <code>true</code>, this component is enabled; otherwise
1023 * this component is disabled
1026 public void setEnabled(boolean b
) {
1031 for (GuiReaderGroup group
: booksByType
.keySet()) {
1032 group
.setEnabled(b
);
1034 for (GuiReaderGroup group
: booksByAuthor
.keySet()) {
1035 group
.setEnabled(b
);
1037 super.setEnabled(b
);
1042 * Display an error message and log the linked {@link Exception}.
1047 * the title of the error message
1049 * the exception to log if any
1051 private void error(final String message
, final String title
, Exception e
) {
1052 Instance
.getTraceHandler().error(title
+ ": " + message
);
1054 Instance
.getTraceHandler().error(e
);
1057 SwingUtilities
.invokeLater(new Runnable() {
1060 JOptionPane
.showMessageDialog(GuiReaderFrame
.this, message
,
1061 title
, JOptionPane
.ERROR_MESSAGE
);