2022-06-02 18:28:54 +02:00
|
|
|
/* copyright
|
|
|
|
|
|
|
|
This file is part of JKomasto2.
|
|
|
|
Written in 2022 by Usawashi <usawashi16@yahoo.co.jp>
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
copyright */
|
2021-07-16 01:07:23 +02:00
|
|
|
|
|
|
|
import javax.swing.JFrame;
|
|
|
|
import javax.swing.JPanel;
|
2021-07-17 11:06:50 +02:00
|
|
|
import javax.swing.JTextArea;
|
2022-04-12 08:37:39 +02:00
|
|
|
import javax.swing.JTextField;
|
|
|
|
import javax.swing.JLabel;
|
2021-07-28 22:23:42 +02:00
|
|
|
import javax.swing.JComboBox;
|
2021-07-17 11:06:50 +02:00
|
|
|
import javax.swing.JButton;
|
2022-05-22 05:14:21 +02:00
|
|
|
import javax.swing.JSeparator;
|
2021-07-17 11:06:50 +02:00
|
|
|
import javax.swing.Box;
|
|
|
|
import javax.swing.BorderFactory;
|
2022-04-12 08:37:39 +02:00
|
|
|
import javax.swing.JOptionPane;
|
2022-05-27 14:28:18 +02:00
|
|
|
import javax.swing.JScrollPane;
|
|
|
|
import javax.swing.JTabbedPane;
|
|
|
|
import javax.swing.UIManager;
|
|
|
|
import javax.swing.JToggleButton;
|
2022-05-01 08:46:10 +02:00
|
|
|
import javax.swing.border.Border;
|
2022-05-30 09:56:09 +02:00
|
|
|
import javax.swing.JPopupMenu;
|
|
|
|
import javax.swing.JMenuItem;
|
|
|
|
import javax.swing.JFileChooser;
|
|
|
|
import javax.swing.ImageIcon;
|
|
|
|
import javax.swing.text.JTextComponent;
|
2022-06-02 15:16:34 +02:00
|
|
|
import javax.swing.undo.UndoableEdit;
|
|
|
|
import javax.swing.undo.UndoManager;
|
|
|
|
import javax.swing.event.UndoableEditListener;
|
|
|
|
import javax.swing.event.UndoableEditEvent;
|
2022-05-30 09:56:09 +02:00
|
|
|
import java.nio.file.Files;
|
2022-04-14 06:38:49 +02:00
|
|
|
import java.awt.GridLayout;
|
2021-07-17 11:06:50 +02:00
|
|
|
import java.awt.BorderLayout;
|
|
|
|
import java.awt.Dimension;
|
2021-07-28 22:23:42 +02:00
|
|
|
import java.awt.event.ActionListener;
|
|
|
|
import java.awt.event.ActionEvent;
|
2022-05-06 09:05:01 +02:00
|
|
|
import java.awt.event.KeyListener;
|
|
|
|
import java.awt.event.KeyEvent;
|
2022-05-30 09:56:09 +02:00
|
|
|
import java.awt.event.MouseListener;
|
|
|
|
import java.awt.event.MouseEvent;
|
2022-06-02 15:16:34 +02:00
|
|
|
import java.awt.event.ComponentListener;
|
|
|
|
import java.awt.event.ComponentEvent;
|
2021-07-28 22:23:42 +02:00
|
|
|
import java.awt.Cursor;
|
2022-05-01 08:46:10 +02:00
|
|
|
import java.awt.Color;
|
2022-05-22 03:41:46 +02:00
|
|
|
import java.awt.Font;
|
|
|
|
import java.awt.Insets;
|
2022-05-27 14:28:18 +02:00
|
|
|
import java.awt.FlowLayout;
|
|
|
|
import java.awt.Component;
|
|
|
|
import java.awt.Container;
|
2022-05-30 09:56:09 +02:00
|
|
|
import java.awt.Image;
|
|
|
|
import java.io.File;
|
2022-05-06 09:05:01 +02:00
|
|
|
import javax.swing.event.CaretListener;
|
|
|
|
import javax.swing.event.CaretEvent;
|
2022-05-27 14:28:18 +02:00
|
|
|
import java.util.List;
|
|
|
|
import java.util.ArrayList;
|
2021-07-16 01:07:23 +02:00
|
|
|
|
2022-04-12 08:37:39 +02:00
|
|
|
import cafe.biskuteri.hinoki.Tree;
|
|
|
|
import java.io.IOException;
|
|
|
|
|
2021-07-16 01:07:23 +02:00
|
|
|
class
|
|
|
|
ComposeWindow extends JFrame {
|
|
|
|
|
2022-04-12 08:37:39 +02:00
|
|
|
private JKomasto
|
|
|
|
primaire;
|
|
|
|
|
|
|
|
private MastodonApi
|
|
|
|
api;
|
|
|
|
|
|
|
|
// - -%- -
|
|
|
|
|
2021-07-17 11:46:27 +02:00
|
|
|
private Composition
|
|
|
|
composition;
|
|
|
|
|
2021-07-16 01:07:23 +02:00
|
|
|
private ComposeComponent
|
2022-05-22 03:41:46 +02:00
|
|
|
contentsDisplay;
|
|
|
|
|
|
|
|
private AttachmentsComponent
|
|
|
|
attachmentsDisplay;
|
2021-07-16 01:07:23 +02:00
|
|
|
|
2022-05-28 03:41:12 +02:00
|
|
|
private JTabbedPane
|
|
|
|
tabs;
|
2022-05-27 14:28:18 +02:00
|
|
|
|
2021-07-17 11:46:27 +02:00
|
|
|
// ---%-@-%---
|
|
|
|
|
2022-05-06 22:34:22 +02:00
|
|
|
public synchronized void
|
2021-07-29 10:03:32 +02:00
|
|
|
setComposition(Composition composition)
|
2021-07-28 22:23:42 +02:00
|
|
|
{
|
2022-04-12 08:37:39 +02:00
|
|
|
assert composition != null;
|
2021-07-29 10:03:32 +02:00
|
|
|
this.composition = composition;
|
2021-07-28 23:55:30 +02:00
|
|
|
syncDisplayToComposition();
|
2021-07-28 22:23:42 +02:00
|
|
|
}
|
|
|
|
|
2022-05-06 22:34:22 +02:00
|
|
|
public synchronized void
|
2022-04-12 08:37:39 +02:00
|
|
|
newComposition()
|
|
|
|
{
|
|
|
|
composition = new Composition();
|
|
|
|
composition.text = "";
|
2022-04-28 03:56:25 +02:00
|
|
|
composition.visibility = PostVisibility.UNLISTED;
|
2022-04-12 08:37:39 +02:00
|
|
|
composition.replyToPostId = null;
|
2022-04-14 06:38:49 +02:00
|
|
|
composition.contentWarning = null;
|
2022-04-12 08:37:39 +02:00
|
|
|
syncDisplayToComposition();
|
|
|
|
}
|
|
|
|
|
2022-05-06 22:34:22 +02:00
|
|
|
public synchronized void
|
2021-07-28 22:23:42 +02:00
|
|
|
submit()
|
2021-07-17 11:46:27 +02:00
|
|
|
{
|
2021-07-28 23:55:30 +02:00
|
|
|
syncCompositionToDisplay();
|
2022-04-14 06:38:49 +02:00
|
|
|
|
|
|
|
if (composition.replyToPostId != null)
|
|
|
|
assert !composition.replyToPostId.trim().isEmpty();
|
|
|
|
if (composition.contentWarning != null)
|
|
|
|
assert !composition.contentWarning.trim().isEmpty();
|
|
|
|
|
2022-06-02 20:41:59 +02:00
|
|
|
//tabs.setCursor(new Cursor(Cursor.WAIT_CURSOR));
|
|
|
|
/*
|
|
|
|
* setCursor only works for components that are enabled.
|
|
|
|
* I don't think there's any technical reason for this,
|
|
|
|
* but it's what it is.. rely on the enablement visuals
|
|
|
|
* or a statusbar to indicate to user.
|
|
|
|
*
|
|
|
|
* If we really wanted it, I suspect we could use a glass
|
|
|
|
* pane (or a scroll pane with no scrolling) to have an
|
|
|
|
* enabled pass-through on top.
|
|
|
|
*
|
|
|
|
* Technically contentsDisplay and attachmentsDisplay
|
|
|
|
* themselves aren't disabled. But disabling the tab pane
|
|
|
|
* covers both the tab area and content area. We can't
|
|
|
|
* just the tab area, except maybe by disabling the
|
|
|
|
* individual tabs.
|
|
|
|
*/
|
|
|
|
tabs.setEnabled(false);
|
|
|
|
tabs.setSelectedComponent(contentsDisplay);
|
|
|
|
contentsDisplay.setSubmitting(true);
|
|
|
|
tabs.paintImmediately(tabs.getBounds());
|
|
|
|
|
|
|
|
boolean uploadsOkay = true;
|
|
|
|
for (Attachment a: composition.attachments)
|
|
|
|
{
|
|
|
|
if (a.id != null) continue;
|
|
|
|
// Assume it had already been uploaded.
|
|
|
|
|
|
|
|
api.uploadFile(
|
|
|
|
a.uploadee, a.description,
|
|
|
|
new RequestListener() {
|
|
|
|
|
|
|
|
public void
|
|
|
|
connectionFailed(IOException eIo)
|
|
|
|
{
|
|
|
|
JOptionPane.showMessageDialog(
|
2022-06-01 12:54:02 +02:00
|
|
|
ComposeWindow.this,
|
|
|
|
"Tried to upload attachment, failed..."
|
|
|
|
+ "\n" + eIo.getMessage()
|
|
|
|
);
|
2022-06-02 20:41:59 +02:00
|
|
|
}
|
2022-05-30 09:56:09 +02:00
|
|
|
|
2022-06-02 20:41:59 +02:00
|
|
|
public void
|
|
|
|
requestFailed(int httpCode, Tree<String> json)
|
|
|
|
{
|
|
|
|
JOptionPane.showMessageDialog(
|
2022-06-01 12:54:02 +02:00
|
|
|
ComposeWindow.this,
|
|
|
|
"Tried to upload attachment, failed..."
|
2022-06-02 20:41:59 +02:00
|
|
|
+ "\n" + json.get("error").value
|
2022-06-01 12:54:02 +02:00
|
|
|
+ "\n(HTTP code: " + httpCode + ")"
|
|
|
|
);
|
2022-06-02 20:41:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
|
|
|
requestSucceeded(Tree<String> json)
|
|
|
|
{
|
|
|
|
a.id = json.get("id").value;
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
uploadsOkay &= a.id != null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!uploadsOkay)
|
|
|
|
{
|
|
|
|
contentsDisplay.setSubmitting(false);
|
|
|
|
tabs.setEnabled(true);
|
|
|
|
//tabs.setCursor(null);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int amt = composition.attachments.length;
|
|
|
|
String[] mediaIDs = new String[amt];
|
|
|
|
for (int o = 0; o < mediaIDs.length; ++o)
|
|
|
|
mediaIDs[o] = composition.attachments[o].id;
|
|
|
|
|
|
|
|
api.submit(
|
2022-04-12 08:37:39 +02:00
|
|
|
composition.text, composition.visibility,
|
2022-04-14 06:38:49 +02:00
|
|
|
composition.replyToPostId, composition.contentWarning,
|
2022-06-02 20:41:59 +02:00
|
|
|
mediaIDs,
|
2022-04-12 08:37:39 +02:00
|
|
|
new RequestListener() {
|
|
|
|
|
|
|
|
public void
|
|
|
|
connectionFailed(IOException eIo)
|
|
|
|
{
|
|
|
|
JOptionPane.showMessageDialog(
|
|
|
|
ComposeWindow.this,
|
|
|
|
"Tried to submit post, failed..."
|
|
|
|
+ "\n" + eIo.getMessage()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
|
|
|
requestFailed(int httpCode, Tree<String> json)
|
|
|
|
{
|
|
|
|
JOptionPane.showMessageDialog(
|
|
|
|
ComposeWindow.this,
|
|
|
|
"Tried to submit post, failed..."
|
|
|
|
+ "\n" + json.get("error").value
|
2022-05-06 09:05:01 +02:00
|
|
|
+ "\n(HTTP error code: " + httpCode + ")"
|
2022-04-12 08:37:39 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
|
|
|
requestSucceeded(Tree<String> json)
|
|
|
|
{
|
|
|
|
newComposition();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
);
|
2022-06-01 12:54:02 +02:00
|
|
|
|
2022-05-22 03:41:46 +02:00
|
|
|
contentsDisplay.setSubmitting(false);
|
2022-06-02 20:41:59 +02:00
|
|
|
tabs.setEnabled(true);
|
|
|
|
tabs.setCursor(null);
|
2022-05-22 03:41:46 +02:00
|
|
|
}
|
|
|
|
|
2021-07-28 23:55:30 +02:00
|
|
|
// - -%- -
|
2021-07-28 22:23:42 +02:00
|
|
|
|
2022-05-06 22:34:22 +02:00
|
|
|
private synchronized void
|
2021-07-28 23:55:30 +02:00
|
|
|
syncDisplayToComposition()
|
|
|
|
{
|
2022-05-30 09:56:09 +02:00
|
|
|
ComposeComponent d1 = contentsDisplay;
|
|
|
|
d1.setText(composition.text);
|
|
|
|
d1.setReplyToPostId(composition.replyToPostId);
|
|
|
|
d1.setVisibility(stringFor(composition.visibility));
|
|
|
|
d1.setContentWarning(composition.contentWarning);
|
|
|
|
|
2022-06-02 20:41:59 +02:00
|
|
|
AttachmentsComponent d2 = attachmentsDisplay;
|
|
|
|
d2.setAttachments(composition.attachments);
|
2021-07-28 23:55:30 +02:00
|
|
|
}
|
|
|
|
|
2022-05-06 22:34:22 +02:00
|
|
|
private synchronized void
|
2021-07-28 23:55:30 +02:00
|
|
|
syncCompositionToDisplay()
|
|
|
|
{
|
2022-06-02 20:41:59 +02:00
|
|
|
Composition c = composition;
|
2022-05-30 09:56:09 +02:00
|
|
|
|
|
|
|
ComposeComponent d1 = contentsDisplay;
|
|
|
|
c.text = d1.getText();
|
|
|
|
c.visibility = visibilityFrom(d1.getVisibility());
|
|
|
|
c.replyToPostId = nonEmpty(d1.getReplyToPostId());
|
|
|
|
c.contentWarning = nonEmpty(d1.getContentWarning());
|
|
|
|
|
2022-06-02 20:41:59 +02:00
|
|
|
AttachmentsComponent d2 = attachmentsDisplay;
|
|
|
|
c.attachments = d2.getAttachments();
|
2022-04-14 06:38:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// - -%- -
|
|
|
|
|
|
|
|
private static String
|
|
|
|
nonEmpty(String s)
|
|
|
|
{
|
|
|
|
if (s.trim().isEmpty()) return null;
|
|
|
|
return s;
|
2021-07-17 11:46:27 +02:00
|
|
|
}
|
|
|
|
|
2021-07-16 01:07:23 +02:00
|
|
|
// ---%-@-%---
|
|
|
|
|
2022-04-12 08:37:39 +02:00
|
|
|
ComposeWindow(JKomasto primaire)
|
2021-07-16 01:07:23 +02:00
|
|
|
{
|
2022-04-12 08:37:39 +02:00
|
|
|
super("Submit a new post");
|
|
|
|
this.primaire = primaire;
|
|
|
|
this.api = primaire.getMastodonApi();
|
|
|
|
|
2022-05-27 14:28:18 +02:00
|
|
|
Dimension sz = new Dimension(360, 270);
|
2022-05-22 03:41:46 +02:00
|
|
|
|
2021-07-16 11:46:17 +02:00
|
|
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
|
|
|
|
2022-05-22 03:41:46 +02:00
|
|
|
contentsDisplay = new ComposeComponent(this);
|
|
|
|
attachmentsDisplay = new AttachmentsComponent(this);
|
2022-04-12 08:37:39 +02:00
|
|
|
newComposition();
|
2021-07-17 12:03:59 +02:00
|
|
|
|
2022-05-28 03:41:12 +02:00
|
|
|
tabs = new JTabbedPane();
|
|
|
|
contentsDisplay.setPreferredSize(sz);
|
|
|
|
tabs.addTab("Text", contentsDisplay);
|
|
|
|
tabs.addTab("Media", attachmentsDisplay);
|
|
|
|
|
|
|
|
setBackground((Color)UIManager.get("TabbedPane.tabAreaBackground"));
|
|
|
|
setContentPane(tabs);
|
|
|
|
pack();
|
|
|
|
setIconImage(primaire.getProgramIcon());
|
2021-07-16 01:07:23 +02:00
|
|
|
}
|
|
|
|
|
2021-07-28 23:55:30 +02:00
|
|
|
// - -%- -
|
|
|
|
|
|
|
|
private static final String
|
|
|
|
stringFor(PostVisibility visibility)
|
|
|
|
{
|
|
|
|
switch (visibility)
|
|
|
|
{
|
|
|
|
case PUBLIC: return "Public";
|
2022-04-12 08:37:39 +02:00
|
|
|
case UNLISTED: return "Unlisted";
|
2021-07-28 23:55:30 +02:00
|
|
|
case FOLLOWERS: return "Followers";
|
|
|
|
case MENTIONED: return "Mentioned";
|
|
|
|
}
|
|
|
|
|
2021-07-29 10:03:32 +02:00
|
|
|
assert false; return null;
|
2021-07-28 23:55:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private static final PostVisibility
|
|
|
|
visibilityFrom(String string)
|
|
|
|
{
|
|
|
|
if (string.equals("Public"))
|
|
|
|
return PostVisibility.PUBLIC;
|
2022-04-12 08:37:39 +02:00
|
|
|
if (string.equals("Unlisted"))
|
|
|
|
return PostVisibility.UNLISTED;
|
2021-07-28 23:55:30 +02:00
|
|
|
if (string.equals("Followers"))
|
|
|
|
return PostVisibility.FOLLOWERS;
|
|
|
|
if (string.equals("Mentioned"))
|
|
|
|
return PostVisibility.MENTIONED;
|
|
|
|
|
2021-07-29 10:03:32 +02:00
|
|
|
assert false; return null;
|
2021-07-28 23:55:30 +02:00
|
|
|
}
|
|
|
|
|
2021-07-16 01:07:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class
|
2021-07-28 22:23:42 +02:00
|
|
|
ComposeComponent extends JPanel
|
2022-05-06 09:05:01 +02:00
|
|
|
implements ActionListener, CaretListener, KeyListener {
|
2021-07-28 22:23:42 +02:00
|
|
|
|
|
|
|
private ComposeWindow
|
|
|
|
primaire;
|
|
|
|
|
|
|
|
// - -%- -
|
2021-07-17 11:06:50 +02:00
|
|
|
|
|
|
|
private JTextArea
|
|
|
|
text;
|
|
|
|
|
2022-04-12 08:37:39 +02:00
|
|
|
private JTextField
|
2022-04-14 06:38:49 +02:00
|
|
|
reply, contentWarning;
|
2022-04-12 08:37:39 +02:00
|
|
|
|
2022-05-06 09:05:01 +02:00
|
|
|
private JLabel
|
|
|
|
textLength;
|
|
|
|
|
2021-07-28 22:23:42 +02:00
|
|
|
private JComboBox<String>
|
|
|
|
visibility;
|
|
|
|
|
2021-07-17 11:06:50 +02:00
|
|
|
private JButton
|
|
|
|
submit;
|
|
|
|
|
|
|
|
// ---%-@-%---
|
|
|
|
|
2021-07-28 22:23:42 +02:00
|
|
|
public void
|
2021-07-28 23:55:30 +02:00
|
|
|
setText(String text)
|
2021-07-17 11:06:50 +02:00
|
|
|
{
|
2021-07-28 23:55:30 +02:00
|
|
|
this.text.setText(text);
|
|
|
|
}
|
2021-07-28 22:23:42 +02:00
|
|
|
|
2022-04-12 08:37:39 +02:00
|
|
|
public void
|
|
|
|
setReplyToPostId(String postId)
|
|
|
|
{
|
|
|
|
this.reply.setText(postId);
|
|
|
|
}
|
|
|
|
|
2021-07-28 23:55:30 +02:00
|
|
|
public void
|
|
|
|
setVisibility(String visibility)
|
|
|
|
{
|
|
|
|
if (visibility.equals("Public"))
|
|
|
|
this.visibility.setSelectedIndex(0);
|
2022-04-12 08:37:39 +02:00
|
|
|
else if (visibility.equals("Unlisted"))
|
2021-07-28 23:55:30 +02:00
|
|
|
this.visibility.setSelectedIndex(1);
|
|
|
|
else if (visibility.equals("Followers"))
|
|
|
|
this.visibility.setSelectedIndex(2);
|
|
|
|
else if (visibility.equals("Mentioned"))
|
|
|
|
this.visibility.setSelectedIndex(3);
|
2021-07-28 22:23:42 +02:00
|
|
|
}
|
|
|
|
|
2022-04-14 06:38:49 +02:00
|
|
|
public void
|
|
|
|
setContentWarning(String contentWarning)
|
|
|
|
{
|
|
|
|
this.contentWarning.setText(contentWarning);
|
|
|
|
}
|
|
|
|
|
2021-07-28 22:23:42 +02:00
|
|
|
public String
|
2021-07-28 23:55:30 +02:00
|
|
|
getText()
|
|
|
|
{
|
|
|
|
return text.getText();
|
|
|
|
}
|
2021-07-28 22:23:42 +02:00
|
|
|
|
2022-04-12 08:37:39 +02:00
|
|
|
public String
|
|
|
|
getReplyToPostId()
|
|
|
|
{
|
|
|
|
return reply.getText();
|
|
|
|
}
|
|
|
|
|
2022-04-14 06:38:49 +02:00
|
|
|
public String
|
|
|
|
getContentWarning()
|
|
|
|
{
|
|
|
|
return contentWarning.getText();
|
|
|
|
}
|
|
|
|
|
2021-07-28 22:23:42 +02:00
|
|
|
public String
|
2021-07-28 23:55:30 +02:00
|
|
|
getVisibility()
|
2021-07-28 22:23:42 +02:00
|
|
|
{
|
|
|
|
return (String)visibility.getSelectedItem();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
2021-07-28 23:55:30 +02:00
|
|
|
setSubmitting(boolean submitting)
|
2021-07-28 22:23:42 +02:00
|
|
|
{
|
2021-07-28 23:55:30 +02:00
|
|
|
if (submitting)
|
|
|
|
{
|
|
|
|
text.setEnabled(false);
|
|
|
|
visibility.setEnabled(false);
|
|
|
|
submit.setEnabled(false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
text.setEnabled(true);
|
|
|
|
visibility.setEnabled(true);
|
|
|
|
submit.setEnabled(true);
|
|
|
|
}
|
2021-07-28 22:23:42 +02:00
|
|
|
}
|
|
|
|
|
2021-07-28 23:55:30 +02:00
|
|
|
// - -%- -
|
|
|
|
|
2021-07-28 22:23:42 +02:00
|
|
|
public void
|
2022-05-22 03:41:46 +02:00
|
|
|
actionPerformed(ActionEvent eA)
|
|
|
|
{
|
|
|
|
if (eA.getSource() == submit)
|
|
|
|
primaire.submit();
|
|
|
|
}
|
2021-07-28 22:23:42 +02:00
|
|
|
|
2022-05-06 09:05:01 +02:00
|
|
|
public void
|
|
|
|
caretUpdate(CaretEvent eCa) { updateTextLength(); }
|
|
|
|
|
|
|
|
public void
|
2022-05-22 05:14:21 +02:00
|
|
|
keyPressed(KeyEvent eK)
|
|
|
|
{
|
2022-05-28 03:41:12 +02:00
|
|
|
boolean esc = eK.getKeyCode() == KeyEvent.VK_ESCAPE;
|
2022-05-27 14:28:18 +02:00
|
|
|
if (esc)
|
2022-05-28 03:41:12 +02:00
|
|
|
{
|
|
|
|
Container fcr = getFocusCycleRootAncestor();
|
|
|
|
fcr.getFocusTraversalPolicy()
|
|
|
|
.getComponentAfter(fcr, text)
|
|
|
|
.requestFocusInWindow();
|
|
|
|
}
|
2022-05-22 05:14:21 +02:00
|
|
|
else updateTextLength();
|
|
|
|
}
|
2022-05-06 09:05:01 +02:00
|
|
|
|
|
|
|
public void
|
|
|
|
keyReleased(KeyEvent eK) { }
|
|
|
|
|
|
|
|
public void
|
|
|
|
keyTyped(KeyEvent eK) { }
|
|
|
|
|
|
|
|
private void
|
|
|
|
updateTextLength()
|
|
|
|
{
|
|
|
|
int length = text.getText().length();
|
|
|
|
/*
|
|
|
|
* The web interface doesn't do this expensive thing.
|
|
|
|
* It has an upwards counter, incremented by I'm not
|
|
|
|
* sure what. Presumably they have some control over
|
|
|
|
* the text input. I'd rather not, cause I use a
|
|
|
|
* Japanese IME, I'm going to see how laggy this is.
|
|
|
|
* It raises our app's system requirements, but, I was
|
|
|
|
* going to transition it to multithreading anyways,
|
|
|
|
* I don't think we're going to be very cheap.. Which
|
|
|
|
* sucks, but the Mastodon API is not helping us here.
|
|
|
|
*/
|
|
|
|
textLength.setText(Integer.toString(length));
|
|
|
|
/*
|
|
|
|
* Another thing I could do is temporarily move the
|
|
|
|
* caret to the end and then find its position, then
|
|
|
|
* seek back. Not sure how much that would help, but
|
|
|
|
* if this is too laggy, that's what I'd try next.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2021-07-28 22:23:42 +02:00
|
|
|
// ---%-@-%---
|
|
|
|
|
|
|
|
ComposeComponent(ComposeWindow primaire)
|
|
|
|
{
|
|
|
|
this.primaire = primaire;
|
|
|
|
|
2022-05-01 08:46:10 +02:00
|
|
|
Border b1 = BorderFactory.createEmptyBorder(8, 8, 8, 8);
|
|
|
|
Border b2 = BorderFactory.createEmptyBorder(4, 4, 4, 4);
|
|
|
|
Border b3 = BorderFactory.createLineBorder(Color.GRAY);
|
|
|
|
Border bc = BorderFactory.createCompoundBorder(b3, b2);
|
|
|
|
|
2022-06-02 20:41:59 +02:00
|
|
|
TextActionPopupMenu textActionPopup;
|
|
|
|
textActionPopup = new TextActionPopupMenu();
|
2022-05-30 09:56:09 +02:00
|
|
|
|
2022-04-12 08:37:39 +02:00
|
|
|
reply = new JTextField();
|
2022-04-14 06:38:49 +02:00
|
|
|
JLabel replyLabel = new JLabel("In reply to: ");
|
|
|
|
replyLabel.setLabelFor(reply);
|
2022-06-02 20:41:59 +02:00
|
|
|
reply.addMouseListener(textActionPopup);
|
2022-04-14 06:38:49 +02:00
|
|
|
|
|
|
|
contentWarning = new JTextField();
|
|
|
|
JLabel cwLabel = new JLabel("Content warning: ");
|
|
|
|
cwLabel.setLabelFor(contentWarning);
|
2022-06-02 20:41:59 +02:00
|
|
|
contentWarning.addMouseListener(textActionPopup);
|
2022-04-14 06:38:49 +02:00
|
|
|
|
|
|
|
JPanel top = new JPanel();
|
|
|
|
top.setOpaque(false);
|
|
|
|
top.setLayout(new GridLayout(2, 2, 8, 0));
|
|
|
|
top.add(replyLabel);
|
|
|
|
top.add(reply);
|
|
|
|
top.add(cwLabel);
|
|
|
|
top.add(contentWarning);
|
2021-07-17 11:06:50 +02:00
|
|
|
|
2022-05-06 09:05:01 +02:00
|
|
|
textLength = new JLabel("0");
|
|
|
|
textLength.setFont(textLength.getFont().deriveFont(14f));
|
|
|
|
|
2021-07-28 22:23:42 +02:00
|
|
|
visibility = new JComboBox<>(new String[] {
|
|
|
|
"Public",
|
2022-04-12 08:37:39 +02:00
|
|
|
"Unlisted",
|
2021-07-28 22:23:42 +02:00
|
|
|
"Followers",
|
|
|
|
"Mentioned"
|
2021-07-28 23:55:30 +02:00
|
|
|
// Where should we be saving strings..
|
2021-07-28 22:23:42 +02:00
|
|
|
});
|
2022-05-22 03:41:46 +02:00
|
|
|
visibility.setPreferredSize(new Dimension(48, 24));
|
2021-07-28 22:23:42 +02:00
|
|
|
|
2021-07-17 11:06:50 +02:00
|
|
|
submit = new JButton("Submit");
|
2021-07-28 22:23:42 +02:00
|
|
|
submit.addActionListener(this);
|
2021-07-17 11:06:50 +02:00
|
|
|
|
|
|
|
Box bottom = Box.createHorizontalBox();
|
|
|
|
bottom.add(Box.createGlue());
|
2022-05-06 09:05:01 +02:00
|
|
|
bottom.add(textLength);
|
|
|
|
bottom.add(Box.createHorizontalStrut(12));
|
2021-07-28 22:23:42 +02:00
|
|
|
bottom.add(visibility);
|
2022-05-06 09:05:01 +02:00
|
|
|
bottom.add(Box.createHorizontalStrut(12));
|
2021-07-17 11:06:50 +02:00
|
|
|
bottom.add(submit);
|
|
|
|
|
2022-04-14 06:38:49 +02:00
|
|
|
text = new JTextArea();
|
|
|
|
text.setLineWrap(true);
|
|
|
|
text.setWrapStyleWord(true);
|
2022-05-01 08:46:10 +02:00
|
|
|
text.setFont(text.getFont().deriveFont(16f));
|
|
|
|
text.setBorder(bc);
|
2022-05-06 09:05:01 +02:00
|
|
|
text.addCaretListener(this);
|
|
|
|
text.addKeyListener(this);
|
2022-06-02 20:41:59 +02:00
|
|
|
text.addMouseListener(textActionPopup);
|
2022-04-12 08:37:39 +02:00
|
|
|
|
2021-07-17 11:06:50 +02:00
|
|
|
setLayout(new BorderLayout(0, 8));
|
2022-04-12 08:37:39 +02:00
|
|
|
add(top, BorderLayout.NORTH);
|
2021-07-17 11:06:50 +02:00
|
|
|
add(text, BorderLayout.CENTER);
|
|
|
|
add(bottom, BorderLayout.SOUTH);
|
|
|
|
|
2022-05-01 08:46:10 +02:00
|
|
|
setBorder(b1);
|
2021-07-17 11:06:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2022-05-22 03:41:46 +02:00
|
|
|
|
|
|
|
class
|
|
|
|
AttachmentsComponent extends JPanel
|
2022-06-02 15:16:34 +02:00
|
|
|
implements ComponentListener, ActionListener {
|
2022-05-22 03:41:46 +02:00
|
|
|
|
|
|
|
private ComposeWindow
|
|
|
|
primaire;
|
|
|
|
|
|
|
|
// - -%- -
|
|
|
|
|
2022-05-28 03:41:12 +02:00
|
|
|
private List<Attachment>
|
|
|
|
working;
|
2022-05-22 03:41:46 +02:00
|
|
|
|
2022-05-28 03:41:12 +02:00
|
|
|
private JPanel
|
|
|
|
selections;
|
2022-05-27 14:28:18 +02:00
|
|
|
|
2022-05-28 03:41:12 +02:00
|
|
|
private JToggleButton
|
|
|
|
attachment1,
|
|
|
|
attachment2,
|
|
|
|
attachment3,
|
2022-06-02 15:16:34 +02:00
|
|
|
attachment4,
|
2022-06-02 20:41:59 +02:00
|
|
|
selected;
|
2022-05-28 03:41:12 +02:00
|
|
|
|
|
|
|
private JButton
|
|
|
|
add;
|
2022-05-22 03:41:46 +02:00
|
|
|
|
|
|
|
private JButton
|
|
|
|
delete,
|
|
|
|
revert;
|
|
|
|
|
|
|
|
private JLabel
|
|
|
|
descriptionLabel;
|
|
|
|
|
|
|
|
private JTextArea
|
|
|
|
description;
|
|
|
|
|
2022-06-02 20:41:59 +02:00
|
|
|
private JFileChooser
|
|
|
|
chooser;
|
2022-05-30 09:56:09 +02:00
|
|
|
|
2022-06-02 20:41:59 +02:00
|
|
|
private UndoManager
|
|
|
|
descriptionUndos;
|
2022-06-02 15:16:34 +02:00
|
|
|
|
2022-05-22 03:41:46 +02:00
|
|
|
// ---%-@-%---
|
|
|
|
|
2022-06-02 20:41:59 +02:00
|
|
|
public void
|
|
|
|
setAttachments(Attachment[] n)
|
|
|
|
{
|
|
|
|
working.clear();
|
|
|
|
if (n != null) for (Attachment attachment: n)
|
|
|
|
{
|
|
|
|
working.add(attachment);
|
|
|
|
}
|
|
|
|
updateButtons();
|
|
|
|
}
|
|
|
|
|
|
|
|
public Attachment[]
|
|
|
|
getAttachments()
|
|
|
|
{
|
|
|
|
return working.toArray(new Attachment[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// - -%- -
|
2022-05-30 09:56:09 +02:00
|
|
|
|
2022-05-28 03:41:12 +02:00
|
|
|
private void
|
|
|
|
updateButtons()
|
|
|
|
{
|
|
|
|
Dimension sz = add.getPreferredSize();
|
|
|
|
|
2022-06-02 20:41:59 +02:00
|
|
|
selections.removeAll();
|
|
|
|
selected = null;
|
2022-06-02 15:16:34 +02:00
|
|
|
if (working.size() > 0)
|
2022-06-02 20:41:59 +02:00
|
|
|
{
|
|
|
|
selections.add(attachment1);
|
|
|
|
Image i = working.get(0).image;
|
|
|
|
attachment1.setIcon(new ImageIcon(i));
|
|
|
|
}
|
|
|
|
if (working.size() > 1)
|
|
|
|
{
|
|
|
|
selections.add(attachment2);
|
|
|
|
Image i = working.get(1).image;
|
|
|
|
attachment2.setIcon(new ImageIcon(i));
|
|
|
|
}
|
|
|
|
if (working.size() > 2)
|
|
|
|
{
|
|
|
|
selections.add(attachment3);
|
|
|
|
Image i = working.get(2).image;
|
|
|
|
attachment3.setIcon(new ImageIcon(i));
|
|
|
|
}
|
2022-06-02 15:16:34 +02:00
|
|
|
if (working.size() > 3)
|
2022-06-02 20:41:59 +02:00
|
|
|
{
|
|
|
|
selections.add(attachment4);
|
|
|
|
Image i = working.get(3).image;
|
|
|
|
attachment4.setIcon(new ImageIcon(i));
|
|
|
|
}
|
|
|
|
if (working.size() < 4) selections.add(add);
|
|
|
|
|
|
|
|
if (working.size() > 3) open(attachment4);
|
|
|
|
else if (working.size() > 2) open(attachment3);
|
|
|
|
else if (working.size() > 1) open(attachment2);
|
|
|
|
else if (working.size() > 0) open(attachment1);
|
|
|
|
|
|
|
|
int bw = sz.width;
|
2022-05-28 03:41:12 +02:00
|
|
|
int hgap = 4;
|
|
|
|
int count = selections.getComponents().length;
|
|
|
|
int w = count * bw + (count - 1) * hgap;
|
|
|
|
int h = bw;
|
|
|
|
selections.setPreferredSize(new Dimension(w, h));
|
|
|
|
selections.setMaximumSize(new Dimension(w, h));
|
2022-06-02 20:41:59 +02:00
|
|
|
selections.revalidate();
|
2022-05-28 03:41:12 +02:00
|
|
|
|
2022-06-02 20:41:59 +02:00
|
|
|
delete.setEnabled(selected != null);
|
|
|
|
revert.setEnabled(selected != null);
|
|
|
|
description.setEnabled(selected != null);
|
2022-05-28 03:41:12 +02:00
|
|
|
}
|
2022-05-27 14:28:18 +02:00
|
|
|
|
2022-05-22 03:41:46 +02:00
|
|
|
public void
|
|
|
|
actionPerformed(ActionEvent eA)
|
|
|
|
{
|
|
|
|
Object src = eA.getSource();
|
|
|
|
|
2022-05-28 03:41:12 +02:00
|
|
|
if (src == add)
|
2022-05-22 03:41:46 +02:00
|
|
|
{
|
2022-06-02 20:41:59 +02:00
|
|
|
int r = chooser.showOpenDialog(this);
|
|
|
|
if (r != JFileChooser.APPROVE_OPTION) return;
|
|
|
|
|
|
|
|
File f = chooser.getSelectedFile();
|
|
|
|
|
|
|
|
Attachment a = new Attachment();
|
|
|
|
a.uploadee = f;
|
|
|
|
a.description = "";
|
|
|
|
a.type = "unknown";
|
|
|
|
|
|
|
|
String mime = "", primary = "";
|
|
|
|
try
|
|
|
|
{
|
|
|
|
mime = Files.probeContentType(f.toPath());
|
|
|
|
primary = mime.split("/")[0];
|
|
|
|
// Too lazy to instantiate a
|
|
|
|
// javax.activation.MimeType.
|
|
|
|
}
|
|
|
|
catch (IOException eIo) { }
|
|
|
|
if (primary.equals("image"))
|
|
|
|
{
|
|
|
|
String urlr = f.toURI().toString();
|
|
|
|
a.image = ImageApi.remote(urlr);
|
|
|
|
a.type = "image";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selected != null) open(selected);
|
|
|
|
// Save first before resetting
|
2022-06-02 15:16:34 +02:00
|
|
|
|
2022-05-30 09:56:09 +02:00
|
|
|
working.add(a);
|
2022-05-28 03:41:12 +02:00
|
|
|
updateButtons();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (src == delete)
|
|
|
|
{
|
2022-06-02 20:41:59 +02:00
|
|
|
assert selected != null;
|
|
|
|
working.remove(getAttachmentFor(selected));
|
|
|
|
updateButtons();
|
2022-05-22 03:41:46 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-06-02 20:41:59 +02:00
|
|
|
if (src != add && selections.isAncestorOf((Component)src))
|
|
|
|
{
|
|
|
|
assert src instanceof JToggleButton;
|
|
|
|
if (src == selected) preview(getAttachmentFor(src));
|
|
|
|
else open((JToggleButton)src);
|
|
|
|
return;
|
|
|
|
}
|
2022-06-02 02:44:06 +02:00
|
|
|
|
2022-05-22 03:41:46 +02:00
|
|
|
if (src == revert)
|
|
|
|
{
|
2022-06-02 20:41:59 +02:00
|
|
|
while (descriptionUndos.canUndo())
|
|
|
|
descriptionUndos.undo();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private Attachment
|
|
|
|
getAttachmentFor(Object button)
|
|
|
|
{
|
|
|
|
if (button == null) return null;
|
|
|
|
assert button instanceof JToggleButton;
|
|
|
|
assert selections.isAncestorOf((Component)button);
|
|
|
|
|
|
|
|
int index = 0;
|
|
|
|
if (button == attachment4) index = 4;
|
|
|
|
if (button == attachment3) index = 3;
|
|
|
|
if (button == attachment2) index = 2;
|
|
|
|
if (button == attachment1) index = 1;
|
|
|
|
|
|
|
|
assert index != 0;
|
|
|
|
assert index <= working.size();
|
|
|
|
return working.get(index - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void
|
|
|
|
open(JToggleButton button)
|
|
|
|
{
|
|
|
|
assert selections.isAncestorOf(button);
|
|
|
|
|
|
|
|
if (selected != null)
|
|
|
|
{
|
|
|
|
Attachment a = getAttachmentFor(selected);
|
|
|
|
a.description = description.getText();
|
|
|
|
selected.setSelected(false);
|
2022-05-22 03:41:46 +02:00
|
|
|
}
|
2022-06-02 20:41:59 +02:00
|
|
|
|
|
|
|
Attachment a = getAttachmentFor(button);
|
|
|
|
description.setText(a.description);
|
|
|
|
descriptionUndos.discardAllEdits();
|
|
|
|
(selected = button).setSelected(true);
|
2022-05-22 03:41:46 +02:00
|
|
|
}
|
|
|
|
|
2022-06-02 20:41:59 +02:00
|
|
|
public void
|
|
|
|
componentHidden(ComponentEvent eC)
|
|
|
|
{
|
|
|
|
if (selected != null) open(selected);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void
|
|
|
|
preview(Attachment a)
|
|
|
|
{
|
|
|
|
ImageWindow w = new ImageWindow();
|
|
|
|
w.showAttachments(new Attachment[] { a } );
|
|
|
|
w.setTitle("Attachment preview");
|
|
|
|
w.setVisible(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
|
|
|
componentShown(ComponentEvent eC) { }
|
|
|
|
|
|
|
|
public void
|
|
|
|
componentMoved(ComponentEvent eC) { }
|
|
|
|
|
|
|
|
public void
|
|
|
|
componentResized(ComponentEvent eC) { }
|
2022-06-02 15:16:34 +02:00
|
|
|
|
2022-05-22 03:41:46 +02:00
|
|
|
// ---%-@-%---
|
|
|
|
|
|
|
|
AttachmentsComponent(ComposeWindow primaire)
|
|
|
|
{
|
|
|
|
this.primaire = primaire;
|
|
|
|
|
|
|
|
Border b1 = BorderFactory.createEmptyBorder(8, 8, 8, 8);
|
|
|
|
Border b2 = BorderFactory.createEmptyBorder(4, 4, 4, 4);
|
|
|
|
Border b3 = BorderFactory.createLineBorder(Color.GRAY);
|
2022-05-28 03:41:12 +02:00
|
|
|
Border b4 = BorderFactory.createEmptyBorder(4, 8, 8, 8);
|
|
|
|
Border b5 = BorderFactory.createEtchedBorder();
|
2022-05-27 14:28:18 +02:00
|
|
|
Border bc1 = BorderFactory.createCompoundBorder(b3, b2);
|
2022-05-28 03:41:12 +02:00
|
|
|
Border bc2 = BorderFactory.createCompoundBorder(b4, b2);
|
|
|
|
|
2022-06-02 20:41:59 +02:00
|
|
|
TextActionPopupMenu textActionPopup;
|
|
|
|
textActionPopup = new TextActionPopupMenu();
|
2022-05-30 09:56:09 +02:00
|
|
|
|
2022-06-02 20:41:59 +02:00
|
|
|
chooser = new JFileChooser();
|
2022-05-30 09:56:09 +02:00
|
|
|
|
2022-05-28 03:41:12 +02:00
|
|
|
add = new JButton("+");
|
|
|
|
add.setPreferredSize(new Dimension(32, 32));
|
|
|
|
add.setMargin(new Insets(0, 0, 0, 0));
|
2022-06-02 20:41:59 +02:00
|
|
|
add.addActionListener(this);
|
2022-05-28 03:41:12 +02:00
|
|
|
attachment1 = new JToggleButton("1");
|
|
|
|
attachment2 = new JToggleButton("2");
|
|
|
|
attachment3 = new JToggleButton("3");
|
|
|
|
attachment4 = new JToggleButton("4");
|
|
|
|
attachment1.setMargin(add.getMargin());
|
|
|
|
attachment2.setMargin(add.getMargin());
|
|
|
|
attachment3.setMargin(add.getMargin());
|
|
|
|
attachment4.setMargin(add.getMargin());
|
2022-06-02 20:41:59 +02:00
|
|
|
attachment1.addActionListener(this);
|
|
|
|
attachment2.addActionListener(this);
|
|
|
|
attachment3.addActionListener(this);
|
|
|
|
attachment4.addActionListener(this);
|
2022-05-28 03:41:12 +02:00
|
|
|
|
|
|
|
selections = new JPanel();
|
|
|
|
selections.setOpaque(false);
|
|
|
|
selections.setLayout(new GridLayout(1, 0, 4, 0));
|
|
|
|
working = new ArrayList<Attachment>();
|
2022-06-02 15:16:34 +02:00
|
|
|
|
2022-06-02 20:41:59 +02:00
|
|
|
Box top = Box.createHorizontalBox();
|
2022-06-02 15:16:34 +02:00
|
|
|
top.add(selections);
|
|
|
|
top.add(Box.createGlue());
|
2022-05-28 03:41:12 +02:00
|
|
|
|
2022-05-22 03:41:46 +02:00
|
|
|
delete = new JButton("Delete");
|
|
|
|
revert = new JButton("Revert");
|
2022-06-02 20:41:59 +02:00
|
|
|
JButton ml = new JButton("←");
|
2022-06-02 15:16:34 +02:00
|
|
|
JButton mr = new JButton("→");
|
2022-06-02 20:41:59 +02:00
|
|
|
delete.addActionListener(this);
|
2022-05-22 03:41:46 +02:00
|
|
|
revert.addActionListener(this);
|
2022-05-27 14:28:18 +02:00
|
|
|
|
2022-06-02 20:41:59 +02:00
|
|
|
Box bottom = Box.createHorizontalBox();
|
2022-05-27 14:28:18 +02:00
|
|
|
bottom.add(ml);
|
2022-05-28 03:41:12 +02:00
|
|
|
bottom.add(mr);
|
|
|
|
bottom.add(Box.createHorizontalStrut(8));
|
|
|
|
bottom.add(delete);
|
|
|
|
bottom.add(Box.createGlue());
|
|
|
|
bottom.add(revert);
|
2022-05-22 03:41:46 +02:00
|
|
|
|
|
|
|
description = new JTextArea();
|
|
|
|
description.setLineWrap(true);
|
|
|
|
description.setWrapStyleWord(true);
|
|
|
|
java.awt.Font f = description.getFont();
|
|
|
|
description.setFont(f.deriveFont(16f));
|
2022-05-27 14:28:18 +02:00
|
|
|
description.setBorder(bc1);
|
2022-06-02 20:41:59 +02:00
|
|
|
description.addMouseListener(textActionPopup);
|
2022-05-22 03:41:46 +02:00
|
|
|
descriptionLabel = new JLabel("Description");
|
|
|
|
descriptionLabel.setLabelFor(description);
|
2022-06-02 20:41:59 +02:00
|
|
|
descriptionUndos = new UndoManager();
|
|
|
|
description.getDocument().
|
|
|
|
addUndoableEditListener(descriptionUndos);
|
2022-06-02 15:16:34 +02:00
|
|
|
|
2022-06-02 20:41:59 +02:00
|
|
|
updateButtons();
|
2022-05-22 03:41:46 +02:00
|
|
|
|
|
|
|
JPanel row1 = new JPanel();
|
|
|
|
row1.setOpaque(false);
|
2022-05-28 03:41:12 +02:00
|
|
|
row1.setLayout(new BorderLayout());
|
2022-05-22 03:41:46 +02:00
|
|
|
row1.add(descriptionLabel, BorderLayout.NORTH);
|
|
|
|
row1.add(description, BorderLayout.CENTER);
|
2022-05-27 14:28:18 +02:00
|
|
|
|
2022-06-02 20:41:59 +02:00
|
|
|
Box centre = Box.createVerticalBox();
|
2022-05-28 03:41:12 +02:00
|
|
|
centre.setBorder(b4);
|
|
|
|
centre.add(row1);
|
2022-05-22 03:41:46 +02:00
|
|
|
|
2022-05-22 05:14:21 +02:00
|
|
|
setLayout(new BorderLayout(8, 8));
|
2022-05-27 14:28:18 +02:00
|
|
|
add(centre, BorderLayout.CENTER);
|
|
|
|
add(top, BorderLayout.NORTH);
|
2022-05-22 03:41:46 +02:00
|
|
|
add(bottom, BorderLayout.SOUTH);
|
|
|
|
|
|
|
|
setBorder(b1);
|
2022-06-02 20:41:59 +02:00
|
|
|
this.addComponentListener(this);
|
2022-05-22 03:41:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2022-05-30 09:56:09 +02:00
|
|
|
|
|
|
|
class
|
|
|
|
TextActionPopupMenu extends JPopupMenu
|
|
|
|
implements MouseListener, ActionListener {
|
|
|
|
|
2022-06-02 20:41:59 +02:00
|
|
|
private JMenuItem
|
|
|
|
copy,
|
|
|
|
cut,
|
|
|
|
paste;
|
|
|
|
|
|
|
|
private long
|
|
|
|
pressed;
|
|
|
|
|
|
|
|
// ---%-@-%---
|
|
|
|
|
|
|
|
public void
|
|
|
|
mousePressed(MouseEvent eM)
|
|
|
|
{
|
|
|
|
assert eM.getSource() instanceof JTextComponent;
|
|
|
|
if (!eM.isPopupTrigger()) return;
|
|
|
|
show((Component)eM.getSource(), eM.getX(), eM.getY());
|
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
|
|
|
mouseReleased(MouseEvent eM)
|
|
|
|
{
|
|
|
|
if (eM.getClickCount() == 0) setVisible(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void
|
|
|
|
mouseClicked(MouseEvent eM) { }
|
|
|
|
|
|
|
|
public void
|
|
|
|
mouseEntered(MouseEvent eM) { }
|
|
|
|
|
|
|
|
public void
|
|
|
|
mouseExited(MouseEvent eM) { }
|
|
|
|
|
|
|
|
public void
|
|
|
|
actionPerformed(ActionEvent eA)
|
|
|
|
{
|
|
|
|
assert getInvoker() instanceof JTextComponent;
|
|
|
|
JTextComponent inv = (JTextComponent)getInvoker();
|
|
|
|
|
|
|
|
if (eA.getSource() == copy) inv.copy();
|
|
|
|
if (eA.getSource() == cut) inv.cut();
|
|
|
|
if (eA.getSource() == paste) inv.paste();
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---%-@-%---
|
|
|
|
|
|
|
|
public
|
|
|
|
TextActionPopupMenu()
|
|
|
|
{
|
|
|
|
copy = new JMenuItem("Copy");
|
|
|
|
cut = new JMenuItem("Cut");
|
|
|
|
paste = new JMenuItem("Paste");
|
|
|
|
copy.addActionListener(this);
|
|
|
|
cut.addActionListener(this);
|
|
|
|
paste.addActionListener(this);
|
|
|
|
|
|
|
|
add(cut);
|
|
|
|
add(copy);
|
|
|
|
add(paste);
|
|
|
|
}
|
2022-05-30 09:56:09 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|