Add :style command
authorJack Miller <jack@codezen.org>
Tue, 9 Jun 2015 17:28:22 +0000 (12:28 -0500)
committerJack Miller <jack@codezen.org>
Tue, 9 Jun 2015 17:28:22 +0000 (12:28 -0500)
Allow easy manipulation of curses styles (normal, bold, reverse,
standout, underline).

canto_curses/color.py
canto_curses/config.py
canto_curses/html.py
canto_curses/reader.py
canto_curses/screen.py
canto_curses/story.py
canto_curses/tag.py

index 6209fbe..09208ed 100644 (file)
@@ -16,15 +16,51 @@ from .config import config
 class CantoColorManager:
     def __init__(self):
         self.color_conf = config.get_opt("color")
+        self.style_conf = config.get_opt("style")
         on_hook("curses_opt_change", self.on_opt_change, self)
 
     def on_opt_change(self, config):
         if "color" in config:
             self.color_conf = config["color"]
+        if "style" in config:
+            self.style_conf = config["style"]
+
+    def _invert(self, codes):
+        inverted = ""
+
+        in_code = False
+        long_code = False
+
+        for c in codes:
+            if in_code:
+                if long_code:
+                    if c == "]":
+                        inverted += "%0"
+                        long_code = False
+                else:
+                    if c in "12345678":
+                        inverted += "%0"
+                    elif c in "BRDSU":
+                        inverted += "%" + c.lower()
+                    elif c == "[":
+                        long_code = True
+                    in_code = False
+            elif c == "%":
+                in_code = True
+        return inverted
 
     def __call__(self, name):
+        color = ""
+        style = ""
+
         if self.color_conf[name] > 8:
-            return "%[" + str(self.color_conf[name]) + "]"
-        return "%" + str(self.color_conf[name])
+            color = "%[" + str(self.color_conf[name]) + "]"
+        else:
+            color = "%" + str(self.color_conf[name])
+
+        return color + self.style_conf[name]
+
+    def end(self, name):
+        return self._invert(self(name))
 
 cc = CantoColorManager()
index e0248f6..ba17c60 100644 (file)
@@ -170,6 +170,8 @@ class CantoCursesConfig(SubThread):
 
             "color" : self.validate_color_block,
 
+            "style" : self.validate_style_block,
+
             "kill_daemon_on_exit" : self.validate_bool
         }
 
@@ -363,6 +365,22 @@ class CantoCursesConfig(SubThread):
                 }
             },
 
+            "style" :
+            {
+                "unread" : "%B",
+                "read" : "",
+                "pending" : "%B",
+                "error" : "",
+                "marked" : "%B",
+                "reader_quote" : "",
+                "reader_link" : "",
+                "reader_image_link" : "",
+                "reader_italics" : "",
+                "enum_hints" : "",
+                "selected" : "%R",
+
+            },
+
             "color" :
             {
                 "defbg" : -1,
@@ -370,7 +388,6 @@ class CantoCursesConfig(SubThread):
                 "unread" : 5,
                 "read" : 4,
                 "pending" : 1,
-                "text" : 8,
                 "error" : 2,
                 "marked" : 8,
                 "reader_quote" : 6,
@@ -378,6 +395,7 @@ class CantoCursesConfig(SubThread):
                 "reader_image_link" : 5,
                 "reader_italics" : 8,
                 "enum_hints" : 8,
+                "selected" : 5,
             },
 
             "kill_daemon_on_exit" : False
@@ -659,6 +677,23 @@ class CantoCursesConfig(SubThread):
 
         return (True, r)
 
+    # We don't care if the styles actually make sense, only that they won't
+    # cause trouble when used as a string.
+
+    def validate_style_block(self, val, d):
+        if type(val) != dict:
+            return (False, False)
+
+        r = eval(repr(d))
+
+        for key in val:
+            if type(key) != str or type(val[key]) != str:
+                log.error("Ignoring style %s - %s", key, val[key])
+            else:
+                r[key] = val[key]
+
+        return (True, r)
+
     def validate_string_list(self, val, d):
         if type(val) != list:
             return (False, False)
index 047140c..88f233b 100644 (file)
@@ -99,7 +99,7 @@ class CantoHTML(HTMLParser):
                 self.link_text = ""
                 self.link_href = ""
                 self.link_open = False
-                self.result += "[" + str(len(self.links)) + "]%0"
+                self.result += "[" + str(len(self.links)) + "]" + cc.end("reader_link")
 
         elif tag in ["img"]:
             if open:
@@ -109,7 +109,7 @@ class CantoHTML(HTMLParser):
                     attrs["alt"] = ""
                 self.links.append(("image", attrs["src"], attrs["alt"]))
                 self.handle_data_clean(cc("reader_image_link") + attrs["alt"] +\
-                        "[" + str(len(self.links)) + "]%0")
+                        "[" + str(len(self.links)) + "]" + cc.end("reader_image_link"))
 
         elif tag in ["h" + str(x) for x in range(1,7)]:
             if open:
@@ -162,9 +162,9 @@ class CantoHTML(HTMLParser):
 
         elif tag in ["i", "small", "em"]:
             if open:
-                self.result += "%B" + cc("reader_italics")
+                self.result += cc("reader_italics")
             else:
-                self.result += "%0%b"
+                self.result += cc.end("reader_italics")
         elif tag in ["b", "strong"]:
             if open:
                 self.result += "%B"
index cecdacb..c54af82 100644 (file)
@@ -185,7 +185,7 @@ class Reader(TextBox):
                 self.links += links
 
                 if reader_conf['show_description']:
-                    s += self.quote_rgx.sub(cc("reader_quote") + "\"\\1\"%0", content)
+                    s += self.quote_rgx.sub(cc("reader_quote") + "\"\\1\"" + cc.end("reader_quote"), content)
 
                 if reader_conf['enumerate_links']:
                     s += "\n\n"
@@ -198,9 +198,9 @@ class Reader(TextBox):
                                 text + "]: " + url + "\n\n"
 
                         if t == "link":
-                            link_text = cc("reader_link") + link_text + "%0"
+                            link_text = cc("reader_link") + link_text + cc.end("reader_link")
                         elif t == "image":
-                            link_text = cc("reader_image_link") + link_text + "%0"
+                            link_text = cc("reader_image_link") + link_text + cc.end("reader_image_link")
 
                         s += link_text
 
index 2507285..adc5f7b 100644 (file)
@@ -84,12 +84,12 @@ class Screen(CommandHandler):
             "color_name" : ("[color name] Either a pair number (0-255, >8 ignored on 8 color terminals), a default fore/background (deffg, defbg), or an arbitrary name to be used in themes (unread, pending, etc.)", self.type_color_name),
             "fg-color" : ("[fg-color] Foreground color", self.type_color),
             "bg-color" : ("[bg-color] Background color (optional)\n\nNamed colors: white black red yellow green blue magenta pink\nNumeric colors: 1-256", self.type_color),
+            "style" : ("[style] Curses style (normal, bold, dim, reverse, standout, underline)", self.type_style),
         }
 
         cmds = {
             "color": (self.cmd_color, ["color_name", "fg-color", "bg-color"],
-"""
-Change the color palette.
+"""Change the color palette.
 
 Most like you want to use this to change a color used in the theme. For example,
 
@@ -99,6 +99,7 @@ Will change the color of unread items to green, with the default background. The
 
     unread
     read
+    selected
     marked
     pending
     error
@@ -122,6 +123,28 @@ Lastly you can change the color pairs themselves. This isn't recommended, they'r
 Arguments:"""
 
 ),
+        "style": (self.cmd_style, ["color_name", "style"],
+"""Change the curses style of a named color. For example,
+
+    :style selected underline
+
+The names used in the default theme are:
+
+    unread
+    read
+    selected
+    marked
+    pending
+    error
+    reader_quote
+    reader_link
+    reader_image_link
+    reader_italics
+    enum_hints
+
+Changing other colors (numeric pairs or deffg/defbg) will have no effect as these are separate from the built in curses color system.
+
+Arguments:"""),
         }
 
         register_arg_types(self, args)
@@ -768,6 +791,30 @@ Arguments:"""
         # Cause curses to be re-init'd
         self.resize()
 
+    def type_style(self):
+        styles = [ "normal", "bold", "dim", "reverse", "standout", "underline" ]
+        return (styles, lambda x: (x in styles, x))
+
+    def cmd_style(self, name, style):
+        conf = self.callbacks["get_opt"]("style")
+
+        styles = {
+            "bold" : "%B",
+            "normal" : "",
+            "dim" : "%D",
+            "standout" : "%S",
+            "reverse" : "%R",
+            "underline" : "%U",
+        }
+
+        conf[name] = styles[style]
+
+        log.debug("style %s set: %s", name, style)
+
+        self.callbacks["set_opt"]("style", conf)
+
+        self.resize()
+
     def get_focus_list(self):
 
         # self.focused might be None, if this is getting called during a
index 0ada4b4..25987c3 100644 (file)
@@ -124,7 +124,7 @@ class Story(PluginHandler):
         if "taglist" in config and "border" in config["taglist"]:
             self.need_redraw()
 
-        if "color" in config:
+        if "color" in config or "style" in config:
             self.need_redraw()
 
         if "story" not in config:
@@ -259,28 +259,28 @@ class Story(PluginHandler):
         s = ""
 
         if self.selected:
-            s += "%R"
+            s += cc("selected")
 
         if self.marked:
-            s += "%B[*]" + cc("marked")
+            s += cc("marked") + "[*]"
 
         if "read" in self.content["canto-state"]:
             s += cc("read")
         else:
-            s += cc("unread") + "%B"
+            s += cc("unread")
 
         s += prep_for_display(self.content["title"])
 
         if "read" in self.content["canto-state"]:
-            s += "%0"
+            s += cc.end("read")
         else:
-            s += "%b%0"
+            s += cc.end("unread")
 
         if self.marked:
-            s += "%0%b"
+            s += cc.end("marked")
 
         if self.selected:
-            s += "%r"
+            s += cc.end("selected")
 
         return s
 
index fb2593f..0248cc1 100644 (file)
@@ -136,7 +136,7 @@ class Tag(PluginHandler, list):
         if "tagobj" in opts:
             self.need_redraw()
 
-        if "color" in opts:
+        if "color" in opts or "style" in opts:
             self.need_redraw()
 
     def on_tag_opt_change(self, opts):
@@ -246,7 +246,7 @@ class Tag(PluginHandler, list):
 
         s = ""
         if self.selected:
-            s += "%R"
+            s += cc("selected")
 
         if self.collapsed:
             s += "[+]"
@@ -255,12 +255,12 @@ class Tag(PluginHandler, list):
 
         s += " " + tag + " "
 
-        s += "[%B" + cc("unread") + str(unread) + "%0%b]"
+        s += "[" + cc("unread") + str(unread) + cc.end("unread") + "]"
         if self.updates_pending:
-            s += " [%B" + cc("pending") + str(self.updates_pending) + "%0%b]"
+            s += " [" + cc("pending") + str(self.updates_pending) + cc.end("pending") + "]"
 
         if self.selected:
-            s += "%r"
+            s += cc.end("selected")
 
         return s