Implemented selection for RichTextPane.
Added reply and misc graphics.
@ -4,6 +4,7 @@ import javax.swing.JPanel;
|
||||
import javax.swing.JMenuBar;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JSeparator;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JScrollPane;
|
||||
@ -39,7 +40,6 @@ import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
|
||||
|
||||
class
|
||||
PostWindow extends JFrame
|
||||
implements ActionListener {
|
||||
@ -125,6 +125,9 @@ implements ActionListener {
|
||||
}
|
||||
else postDisplay.setMediaPreview(null);
|
||||
|
||||
String html = post.get("content").value;
|
||||
setTitle(TimelineComponent.textApproximation(html));
|
||||
|
||||
postDisplay.resetFocus();
|
||||
repaint();
|
||||
}
|
||||
|
@ -6,20 +6,55 @@ import java.awt.FontMetrics;
|
||||
import java.awt.Image;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Toolkit;
|
||||
import java.util.List;
|
||||
import java.util.LinkedList;
|
||||
import java.util.ListIterator;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.awt.event.MouseMotionListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.ClipboardOwner;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
|
||||
class
|
||||
RichTextPane extends JComponent {
|
||||
RichTextPane extends JComponent
|
||||
implements
|
||||
MouseListener, MouseMotionListener, KeyListener,
|
||||
Transferable, ClipboardOwner {
|
||||
|
||||
private List<Segment>
|
||||
text;
|
||||
|
||||
private int
|
||||
selectionStart, selectionEnd;
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
public void
|
||||
setText(List<Segment> text) { this.text = text; }
|
||||
setText(List<Segment> text)
|
||||
{
|
||||
this.text = text;
|
||||
selectionStart = selectionEnd = -1;
|
||||
}
|
||||
|
||||
public List<Segment>
|
||||
getSelection()
|
||||
{
|
||||
List<Segment> returnee = new LinkedList<>();
|
||||
if (selectionEnd == -1) return returnee;
|
||||
|
||||
if (selectionEnd < selectionStart) {
|
||||
int t = selectionEnd;
|
||||
selectionEnd = selectionStart;
|
||||
selectionStart = t;
|
||||
}
|
||||
returnee.addAll(text.subList(selectionStart + 1, selectionEnd));
|
||||
return returnee;
|
||||
}
|
||||
|
||||
// - -%- -
|
||||
|
||||
@ -30,6 +65,7 @@ RichTextPane extends JComponent {
|
||||
FontMetrics fm = g.getFontMetrics(getFont());
|
||||
g.clearRect(0, 0, getWidth(), getHeight());
|
||||
|
||||
int o = 0;
|
||||
for (Segment segment: text)
|
||||
{
|
||||
if (segment.image != null) {
|
||||
@ -44,13 +80,134 @@ RichTextPane extends JComponent {
|
||||
g.drawImage(img, x, y - h, w, h, this);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (o > selectionStart && o < selectionEnd)
|
||||
{
|
||||
int dx = fm.stringWidth(segment.text);
|
||||
int dy1 = fm.getAscent();
|
||||
int dy2 = dy1 + fm.getDescent();
|
||||
g.setColor(new Color(0, 0, 0, 15));
|
||||
g.fillRect(segment.x, segment.y - dy1, dx, dy2);
|
||||
g.setColor(getForeground());
|
||||
}
|
||||
|
||||
if (segment.link != null) g.setColor(Color.BLUE);
|
||||
g.drawString(segment.text, segment.x, segment.y);
|
||||
g.setColor(getForeground());
|
||||
|
||||
++o;
|
||||
}
|
||||
}
|
||||
|
||||
public void
|
||||
mousePressed(MouseEvent eM)
|
||||
{
|
||||
requestFocusInWindow();
|
||||
selectionStart = identify(eM.getX(), eM.getY()) - 2;
|
||||
selectionEnd = -1;
|
||||
repaint();
|
||||
}
|
||||
|
||||
public void
|
||||
mouseDragged(MouseEvent eM)
|
||||
{
|
||||
selectionEnd = identify(eM.getX(), eM.getY());
|
||||
repaint();
|
||||
}
|
||||
|
||||
private int
|
||||
identify(int x, int y)
|
||||
{
|
||||
FontMetrics fm = getFontMetrics(getFont());
|
||||
int iy = fm.getAscent(), lh = fm.getHeight();
|
||||
y -= fm.getDescent();
|
||||
if (y <= iy) y = iy;
|
||||
else y += lh - ((y - iy) % lh);
|
||||
/*
|
||||
* Snaps y to the next baseline. Kind of obtuse,
|
||||
* but it wasn't randomly derived, anyways
|
||||
* you can test it for 13, 30, 47, etc.
|
||||
*/
|
||||
|
||||
int o = 0;
|
||||
for (Segment segment: text)
|
||||
{
|
||||
if (segment.y == y && segment.x > x) break;
|
||||
if (segment.y > y) break;
|
||||
++o;
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
public String
|
||||
getTransferData(DataFlavor flavour)
|
||||
{
|
||||
assert flavour == DataFlavor.stringFlavor;
|
||||
StringBuilder b = new StringBuilder();
|
||||
for (Segment segment: getSelection())
|
||||
{
|
||||
if (segment.link != null) b.append(segment.link);
|
||||
else if (segment.text != null) b.append(segment.text);
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
public DataFlavor[]
|
||||
getTransferDataFlavors()
|
||||
{
|
||||
return new DataFlavor[] { DataFlavor.stringFlavor };
|
||||
/*
|
||||
* We should probably also support javaJVMLocalObjectMimeType,
|
||||
* so that the compose window can ask for the List<Segment>.
|
||||
* Although also like, if we don't store emoji shortcodes in
|
||||
* the image segments, there is no point. Anyways, what is
|
||||
* important is the string format first, allowing us to
|
||||
* copy links or large lengths of text.
|
||||
*/
|
||||
}
|
||||
|
||||
public boolean
|
||||
isDataFlavorSupported(DataFlavor flavour)
|
||||
{
|
||||
return flavour == DataFlavor.stringFlavor;
|
||||
}
|
||||
|
||||
public void
|
||||
keyPressed(KeyEvent eK)
|
||||
{
|
||||
if (selectionEnd == -1) return;
|
||||
if (eK.getKeyCode() != KeyEvent.VK_C) return;
|
||||
if (!eK.isControlDown()) return;
|
||||
Toolkit tk = Toolkit.getDefaultToolkit();
|
||||
Clipboard cb = tk.getSystemClipboard();
|
||||
cb.setContents(this, this);
|
||||
}
|
||||
|
||||
|
||||
public void
|
||||
lostOwnership(Clipboard clipboard, Transferable contents) { }
|
||||
|
||||
public void
|
||||
keyReleased(KeyEvent eK) { }
|
||||
|
||||
public void
|
||||
keyTyped(KeyEvent eK) { }
|
||||
|
||||
public void
|
||||
mouseClicked(MouseEvent eM) { }
|
||||
|
||||
public void
|
||||
mouseReleased(MouseEvent eM) { }
|
||||
|
||||
public void
|
||||
mouseEntered(MouseEvent eM) { }
|
||||
|
||||
public void
|
||||
mouseExited(MouseEvent eM) { }
|
||||
|
||||
public void
|
||||
mouseMoved(MouseEvent eM) { }
|
||||
|
||||
// - -%- -
|
||||
|
||||
public static List<Segment>
|
||||
@ -272,6 +429,9 @@ RichTextPane extends JComponent {
|
||||
RichTextPane()
|
||||
{
|
||||
text = new LinkedList<>();
|
||||
addMouseListener(this);
|
||||
addMouseMotionListener(this);
|
||||
addKeyListener(this);
|
||||
}
|
||||
|
||||
}
|
@ -127,17 +127,22 @@ implements ActionListener {
|
||||
public void
|
||||
connectionFailed(IOException eIo)
|
||||
{
|
||||
eIo.printStackTrace();
|
||||
String s = eIo.getClass().getName();
|
||||
setTitle(s + " - JKomasto");
|
||||
JOptionPane.showMessageDialog(
|
||||
TimelineWindow.this,
|
||||
"Failed to fetch page.."
|
||||
+ "\n" + eIo.getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
public void
|
||||
requestFailed(int httpCode, Tree<String> json)
|
||||
{
|
||||
System.err.println(json.get("error").value);
|
||||
setTitle(httpCode + " - JKomasto");
|
||||
// lol...
|
||||
JOptionPane.showMessageDialog(
|
||||
TimelineWindow.this,
|
||||
"Failed to fetch page.."
|
||||
+ "\n" + json.get("error").value
|
||||
+ "\n(HTTP code: " + httpCode + ")"
|
||||
);
|
||||
}
|
||||
|
||||
public void
|
||||
@ -174,14 +179,22 @@ implements ActionListener {
|
||||
public void
|
||||
connectionFailed(IOException eIo)
|
||||
{
|
||||
String s = eIo.getClass().getName();
|
||||
setTitle(s + " - JKomasto");
|
||||
JOptionPane.showMessageDialog(
|
||||
TimelineWindow.this,
|
||||
"Failed to fetch page.."
|
||||
+ "\n" + eIo.getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
public void
|
||||
requestFailed(int httpCode, Tree<String> json)
|
||||
{
|
||||
setTitle(httpCode + " - JKomasto");
|
||||
JOptionPane.showMessageDialog(
|
||||
TimelineWindow.this,
|
||||
"Failed to fetch page.."
|
||||
+ "\n" + json.get("error").value
|
||||
+ "\n(HTTP code: " + httpCode + ")"
|
||||
);
|
||||
}
|
||||
|
||||
public void
|
||||
@ -224,14 +237,22 @@ implements ActionListener {
|
||||
public void
|
||||
connectionFailed(IOException eIo)
|
||||
{
|
||||
String s = eIo.getClass().getName();
|
||||
setTitle(s + " - JKomasto");
|
||||
JOptionPane.showMessageDialog(
|
||||
TimelineWindow.this,
|
||||
"Failed to fetch page.."
|
||||
+ "\n" + eIo.getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
public void
|
||||
requestFailed(int httpCode, Tree<String> json)
|
||||
{
|
||||
setTitle(httpCode + " - JKomasto");
|
||||
JOptionPane.showMessageDialog(
|
||||
TimelineWindow.this,
|
||||
"Failed to fetch page.."
|
||||
+ "\n" + json.get("error").value
|
||||
+ "\n(HTTP code: " + httpCode + ")"
|
||||
);
|
||||
}
|
||||
|
||||
public void
|
||||
@ -623,7 +644,7 @@ implements
|
||||
if (!cw.isEmpty())
|
||||
c.setBottom("(" + cw + ")");
|
||||
else
|
||||
c.setBottom(textApproximation(html) + " ");
|
||||
c.setBottom(textApproximation(html));
|
||||
}
|
||||
for (int o = posts.size(); o < postPreviews.size(); ++o)
|
||||
{
|
||||
@ -786,6 +807,7 @@ implements
|
||||
{
|
||||
StringBuilder returnee = new StringBuilder();
|
||||
Tree<String> nodes = RudimentaryHTMLParser.depthlessRead(html);
|
||||
if (nodes.size() == 0) return "-";
|
||||
Tree<String> first = nodes.get(0);
|
||||
for (Tree<String> node: nodes)
|
||||
{
|
||||
|
@ -115,14 +115,14 @@ implements KeyListener, MouseListener, FocusListener {
|
||||
if (isFocusOwner())
|
||||
g.drawImage(selectedOverlay, 0, 0, this);
|
||||
|
||||
if (primaryToggled)
|
||||
g.drawImage(primaryToggledIcon, 0, 0, this);
|
||||
else
|
||||
g.drawImage(primaryUntoggledIcon, 0, 0, this);
|
||||
if (secondaryToggled)
|
||||
g.drawImage(secondaryToggledIcon, 0, 0, this);
|
||||
else
|
||||
g.drawImage(secondaryUntoggledIcon, 0, 0, this);
|
||||
if (primaryToggled)
|
||||
g.drawImage(primaryToggledIcon, 0, 0, this);
|
||||
else
|
||||
g.drawImage(primaryUntoggledIcon, 0, 0, this);
|
||||
}
|
||||
|
||||
|
||||
|
BIN
graphics/miscToggled.png
Executable file
After Width: | Height: | Size: 2.5 KiB |
BIN
graphics/miscUntoggled.png
Executable file
After Width: | Height: | Size: 2.5 KiB |
BIN
graphics/replyToggled.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
graphics/replyUntoggled.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.3 KiB |