4 Commits

Author SHA1 Message Date
  Petra Lamborn 8b5a21844a Parser improvements 4 years ago
  Petra Lamborn 62bff8be88 Working typesetting 4 years ago
  Petra Lamborn a6da44a6c5 Broken typesetting 4 years ago
  Petra Lamborn 211cfda12c Colours! 4 years ago
1 changed files with 80 additions and 10 deletions
  1. 80
    10
      fedicurses.py

+ 80
- 10
fedicurses.py View File

@@ -2,20 +2,72 @@ import curses
2 2
 from html.parser import HTMLParser
3 3
 from mastodon import Mastodon
4 4
 
5
+def initColours():
6
+                       # 0: White on black
7
+    curses.init_pair(1, 4, 0) # 1: Blue on black
8
+    curses.init_pair(2, 1, 0) # 2: Red on black
9
+    curses.init_pair(3, 6, 0) # 2: Cyan on black
10
+
5 11
 def newLine(ncwin, lines=1):
6 12
     cy, cx = ncwin.getyx()
7
-    ncwin.move(cy + lines, 0)
13
+    # my, mx = ncwin.getmaxyx()
14
+    try:
15
+        ncwin.move(cy + lines, 0)
16
+    except curses.error:
17
+        pass
18
+
19
+def bump(win):
20
+    my, mx = win.getmaxyx()
21
+    cy, cx = win.getyx()
22
+    if cx < mx and cx != 0:
23
+        try:
24
+            win.move(cy, cx + 1)
25
+        except curses.error:
26
+            pass
27
+
28
+def typeset(text, win, attr, colour):
29
+    my, mx = win.getmaxyx()
30
+    line = 0
31
+    position = 0
32
+    strlen = len(text)
33
+    while position < (strlen):
34
+        cy, cx = win.getyx()
35
+        if cx == mx:
36
+            if cy == my:
37
+                raise curses.error("Out of space")
38
+        rem = (mx - cx) + 0
39
+        nsp = text.find(' ', position)
40
+        if (strlen - position <= rem):
41
+            win.addnstr(text[position:], rem,
42
+                        attr | curses.color_pair(colour))
43
+            position = strlen
44
+        elif (nsp == -1 or nsp - position > rem): # and not strlen - position <= rem:
45
+            if cx == 0:
46
+                win.addnstr(text[position:], rem, attr |
47
+                            curses.color_pair(colour))
48
+                position = position + rem
49
+            else:
50
+                newLine(win)
51
+        else:
52
+            win.addnstr(text[position:], nsp - position,
53
+                        attr | curses.color_pair(colour))
54
+            position = max(nsp, 0) + 1
55
+            bump(win)
8 56
 
9 57
 def printPost(win, post, parser):
58
+    # win.addstr(post["account"]["acct"], curses.A_BOLD)
59
+    typeset(post["account"]["acct"], win, curses.A_BOLD, 0)
10 60
     if (post["reblog"] is not None):
11
-        win.addstr("{} boosted {}:".format(post["account"]["acct"],
12
-                                           post["reblog"]["account"]["acct"]),
13
-                   curses.A_BOLD)
14
-    else:
15
-        win.addstr("{}:".format(post["account"]["acct"]), curses.A_BOLD)
61
+        # win.addstr(" boosted ")
62
+        typeset(" boosted ", win, 0, 0)
63
+        # win.addstr(post["reblog"]["account"]["acct"], curses.A_BOLD)
64
+        typeset(post["reblog"]["account"]["acct"], win, curses.A_BOLD, 0)
65
+    # win.addstr(":")
66
+    typeset(":", win, 0, 0)
16 67
     newLine(win)
17 68
     if (post["spoiler_text"] != ""):
18
-        win.addstr(post["spoiler_text"], curses.A_UNDERLINE)
69
+        # win.addstr(post["spoiler_text"], curses.A_UNDERLINE)
70
+        typeset(post["spoiler_text"], win, curses.A_UNDERLINE, 0)
19 71
         newLine(win)
20 72
     parser.feed(post["content"])
21 73
     if parser.openp:
@@ -27,7 +79,9 @@ def printPost(win, post, parser):
27 79
                                              "%Y-%m-%d %H:%M:%S"))
28 80
     cy, cx = win.getyx()
29 81
     my, mx = win.getmaxyx()
30
-    win.addstr(cy, mx - len(botstring), botstring, curses.A_UNDERLINE)
82
+    # win.addstr(cy, mx - len(botstring), botstring, curses.A_UNDERLINE)
83
+    win.move(cy, mx - len(botstring))
84
+    typeset(botstring, win, curses.A_UNDERLINE, 0)
31 85
 
32 86
     newLine(win, 1)
33 87
 
@@ -55,10 +109,14 @@ class PostParser(HTMLParser):
55 109
         self.defncatt = defncatt
56 110
         self.curatt = defncatt
57 111
         self.openp = False
112
+        self.colour = 0
113
+        self.colstack = []
58 114
 
59 115
     def handle_starttag(self, tag, attrs):
60 116
         if tag == 'p':
61 117
             self.curatt = self.defncatt
118
+            self.colstack = []
119
+            self.colour = 0
62 120
             # newLine(self.win)
63 121
         elif tag == 'strong' or tag == 'b':
64 122
             self.curatt = self.curatt ^ curses.A_REVERSE
@@ -66,19 +124,30 @@ class PostParser(HTMLParser):
66 124
             self.curatt = self.curatt ^ curses.A_BOLD
67 125
         elif tag == 'br':
68 126
             newLine(self.win)
127
+        elif tag == 'a':
128
+            self.colstack.append(self.colour)
129
+            self.colour = 1
130
+        elif tag == 'code':
131
+            self.colstack.append(self.colour)
132
+            self.colour = 3
69 133
 
70 134
     def handle_endtag(self, tag):
71 135
         if tag == 'p':
72
-            newLine(self.win)
136
+            newLine(self.win, 2)
73 137
             self.openp = False
74 138
         elif tag == 'strong' or tag == 'b':
75 139
             self.curatt = self.curatt ^ curses.A_REVERSE
76 140
         elif tag == 'em' or tag == 'i':
77 141
             self.curatt = self.curatt ^ curses.A_BOLD
142
+        elif tag == 'a':
143
+            self.colour = self.colstack.pop()
144
+        elif tag == 'code':
145
+            self.colour = self.colstack.pop()
78 146
 
79 147
     def handle_data(self, data):
80 148
         try:
81
-            self.win.addstr(data, self.curatt)
149
+            # self.win.addstr(data, self.curatt | curses.color_pair(self.colour))
150
+            typeset(data, self.win, self.curatt, self.colour)
82 151
         except curses.error:
83 152
             pass
84 153
         self.openp = True
@@ -88,6 +157,7 @@ class PostParser(HTMLParser):
88 157
 
89 158
 
90 159
 def main(stdscr):
160
+    initColours()
91 161
     mastodon = Mastodon(
92 162
             access_token = 'd100.club_usercred.secret',
93 163
             api_base_url = 'd100.club'