diff --git a/PostWindow.java b/PostWindow.java index f82051f..ebcea31 100755 --- a/PostWindow.java +++ b/PostWindow.java @@ -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(); } diff --git a/RichTextPane.java b/RichTextPane.java index 3f71a2a..42124e1 100644 --- a/RichTextPane.java +++ b/RichTextPane.java @@ -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 text; + private int + selectionStart, selectionEnd; + // ---%-@-%--- public void - setText(List text) { this.text = text; } + setText(List text) + { + this.text = text; + selectionStart = selectionEnd = -1; + } + + public List + getSelection() + { + List 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. + * 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 @@ -272,6 +429,9 @@ RichTextPane extends JComponent { RichTextPane() { text = new LinkedList<>(); + addMouseListener(this); + addMouseMotionListener(this); + addKeyListener(this); } } \ No newline at end of file diff --git a/TimelineWindow.java b/TimelineWindow.java index 4e61d43..6e70483 100755 --- a/TimelineWindow.java +++ b/TimelineWindow.java @@ -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 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 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 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 nodes = RudimentaryHTMLParser.depthlessRead(html); + if (nodes.size() == 0) return "-"; Tree first = nodes.get(0); for (Tree node: nodes) { diff --git a/TwoToggleButton.java b/TwoToggleButton.java index a767e72..406f7a8 100755 --- a/TwoToggleButton.java +++ b/TwoToggleButton.java @@ -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); } diff --git a/graphics/miscToggled.png b/graphics/miscToggled.png new file mode 100755 index 0000000..7151ec9 Binary files /dev/null and b/graphics/miscToggled.png differ diff --git a/graphics/miscUntoggled.png b/graphics/miscUntoggled.png new file mode 100755 index 0000000..7151ec9 Binary files /dev/null and b/graphics/miscUntoggled.png differ diff --git a/graphics/replyToggled.png b/graphics/replyToggled.png new file mode 100644 index 0000000..2857766 Binary files /dev/null and b/graphics/replyToggled.png differ diff --git a/graphics/replyUntoggled.png b/graphics/replyUntoggled.png new file mode 100644 index 0000000..2857766 Binary files /dev/null and b/graphics/replyUntoggled.png differ diff --git a/graphics/test1.png b/graphics/test1.png deleted file mode 100755 index 17d196e..0000000 Binary files a/graphics/test1.png and /dev/null differ diff --git a/graphics/test2.png b/graphics/test2.png deleted file mode 100755 index bc41328..0000000 Binary files a/graphics/test2.png and /dev/null differ diff --git a/graphics/test3.png b/graphics/test3.png deleted file mode 100755 index 86e400e..0000000 Binary files a/graphics/test3.png and /dev/null differ diff --git a/graphics/test4.png b/graphics/test4.png deleted file mode 100755 index 7e0e599..0000000 Binary files a/graphics/test4.png and /dev/null differ