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
 from html.parser import HTMLParser
2
 from html.parser import HTMLParser
3
 from mastodon import Mastodon
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
 def newLine(ncwin, lines=1):
11
 def newLine(ncwin, lines=1):
6
     cy, cx = ncwin.getyx()
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
 def printPost(win, post, parser):
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
     if (post["reblog"] is not None):
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
     newLine(win)
67
     newLine(win)
17
     if (post["spoiler_text"] != ""):
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
         newLine(win)
71
         newLine(win)
20
     parser.feed(post["content"])
72
     parser.feed(post["content"])
21
     if parser.openp:
73
     if parser.openp:
27
                                              "%Y-%m-%d %H:%M:%S"))
79
                                              "%Y-%m-%d %H:%M:%S"))
28
     cy, cx = win.getyx()
80
     cy, cx = win.getyx()
29
     my, mx = win.getmaxyx()
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
     newLine(win, 1)
86
     newLine(win, 1)
33
 
87
 
55
         self.defncatt = defncatt
109
         self.defncatt = defncatt
56
         self.curatt = defncatt
110
         self.curatt = defncatt
57
         self.openp = False
111
         self.openp = False
112
+        self.colour = 0
113
+        self.colstack = []
58
 
114
 
59
     def handle_starttag(self, tag, attrs):
115
     def handle_starttag(self, tag, attrs):
60
         if tag == 'p':
116
         if tag == 'p':
61
             self.curatt = self.defncatt
117
             self.curatt = self.defncatt
118
+            self.colstack = []
119
+            self.colour = 0
62
             # newLine(self.win)
120
             # newLine(self.win)
63
         elif tag == 'strong' or tag == 'b':
121
         elif tag == 'strong' or tag == 'b':
64
             self.curatt = self.curatt ^ curses.A_REVERSE
122
             self.curatt = self.curatt ^ curses.A_REVERSE
66
             self.curatt = self.curatt ^ curses.A_BOLD
124
             self.curatt = self.curatt ^ curses.A_BOLD
67
         elif tag == 'br':
125
         elif tag == 'br':
68
             newLine(self.win)
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
     def handle_endtag(self, tag):
134
     def handle_endtag(self, tag):
71
         if tag == 'p':
135
         if tag == 'p':
72
-            newLine(self.win)
136
+            newLine(self.win, 2)
73
             self.openp = False
137
             self.openp = False
74
         elif tag == 'strong' or tag == 'b':
138
         elif tag == 'strong' or tag == 'b':
75
             self.curatt = self.curatt ^ curses.A_REVERSE
139
             self.curatt = self.curatt ^ curses.A_REVERSE
76
         elif tag == 'em' or tag == 'i':
140
         elif tag == 'em' or tag == 'i':
77
             self.curatt = self.curatt ^ curses.A_BOLD
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
     def handle_data(self, data):
147
     def handle_data(self, data):
80
         try:
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
         except curses.error:
151
         except curses.error:
83
             pass
152
             pass
84
         self.openp = True
153
         self.openp = True
88
 
157
 
89
 
158
 
90
 def main(stdscr):
159
 def main(stdscr):
160
+    initColours()
91
     mastodon = Mastodon(
161
     mastodon = Mastodon(
92
             access_token = 'd100.club_usercred.secret',
162
             access_token = 'd100.club_usercred.secret',
93
             api_base_url = 'd100.club'
163
             api_base_url = 'd100.club'