"""
summary: dynamically colorize [parts of] lines

description:
  shows how one can dynamically alter the lines background
  rendering (as opposed to, say, using ida_nalt.set_item_color()),
  and also shows how that rendering can be limited to just a few
  glyphs, not the whole line.

level: intermediate
"""

import ida_kernwin
import ida_bytes

class lines_rendering_hooks_t(ida_kernwin.UI_Hooks):
    def __init__(self):
        ida_kernwin.UI_Hooks.__init__(self)

        # We'll color all lines starting with the current
        # one, with all available highlights...
        self.instantiated_at = ida_kernwin.get_screen_ea()
        self.color_info = []

        data = [
            ida_kernwin.CK_EXTRA1,
            ida_kernwin.CK_EXTRA2,
            ida_kernwin.CK_EXTRA3,
            ida_kernwin.CK_EXTRA4,
            ida_kernwin.CK_EXTRA5,
            ida_kernwin.CK_EXTRA6,
            ida_kernwin.CK_EXTRA7,
            ida_kernwin.CK_EXTRA8,
            ida_kernwin.CK_EXTRA9,
            ida_kernwin.CK_EXTRA10,
            ida_kernwin.CK_EXTRA11,
            ida_kernwin.CK_EXTRA12,
            ida_kernwin.CK_EXTRA13,
            ida_kernwin.CK_EXTRA14,
            ida_kernwin.CK_EXTRA15,
            ida_kernwin.CK_EXTRA16,
            # let's also try these colors keys, because why not
            ida_kernwin.CK_TRACE,
            ida_kernwin.CK_TRACE_OVL,
            [
                ida_kernwin.CK_TRACE,
                ida_kernwin.CK_TRACE_OVL,
            ],
        ]
        ea = self.instantiated_at
        for one in data:
            self.color_info.append((ea, one))
            ea = ida_bytes.next_head(ea, ida_idaapi.BADADDR)

        # ...and then we'll a few more things, such as
        # overriding parts of a previously-specified overlay,
        # and restricting the override to a few glyphs
        self.color_info.append(
            (
                self.color_info[6][0],
                [
                    (ida_kernwin.CK_EXTRA2, 7, 3),
                    (ida_kernwin.CK_EXTRA4, 2, 1),
                    (ida_kernwin.CK_EXTRA10, 2, 0),
                    (ida_kernwin.CK_EXTRA10, 20, 10),
                ]
            ))
        self.color_info.append(
            (
                self.color_info[7][0],
                [
                    (ida_kernwin.CK_EXTRA1, 1, 1),
                    (ida_kernwin.CK_EXTRA2, 3, 1),
                    (ida_kernwin.CK_EXTRA3, 5, 1),
                    (ida_kernwin.CK_EXTRA4, 7, 1),
                    (ida_kernwin.CK_EXTRA5, 9, 1),
                    (ida_kernwin.CK_EXTRA6, 11, 1),
                    (ida_kernwin.CK_EXTRA7, 13, 1),
                    (ida_kernwin.CK_EXTRA8, 15, 1),
                    (ida_kernwin.CK_EXTRA9, 17, 1),
                    (ida_kernwin.CK_EXTRA10, 19, 1),
                    (ida_kernwin.CK_EXTRA11, 21, 1),
                    (ida_kernwin.CK_EXTRA12, 23, 1),
                    (ida_kernwin.CK_EXTRA13, 25, 1),
                    (ida_kernwin.CK_EXTRA14, 27, 1),
                    (ida_kernwin.CK_EXTRA15, 29, 1),
                    (ida_kernwin.CK_EXTRA16, 31, 1),
                ]
            ))
        self.color_info.append(
            (
                self.color_info[8][0],
                [
                    (ida_kernwin.CK_EXTRA1, 16, 45),
                    (ida_kernwin.CK_EXTRA2, 19, 45),
                    (ida_kernwin.CK_EXTRA3, 22, 45),
                    (ida_kernwin.CK_EXTRA4, 25, 45),
                ]
            ))


    def get_lines_rendering_info(self, out, widget, rin):
        for section_lines in rin.sections_lines:
            for line in section_lines:
                line_ea = line.at.toea()
                for ea, directives in self.color_info:
                    if ea == line_ea:
                        if not isinstance(directives, list):
                            directives = [directives]
                        for directive in directives:
                            e = ida_kernwin.line_rendering_output_entry_t(line)
                            if isinstance(directive, tuple):
                                color, cpx, nchars = directive
                                e.bg_color = color
                                e.cpx = cpx
                                e.nchars = nchars
                                e.flags |= ida_kernwin.LROEF_CPS_RANGE
                            else:
                                e.bg_color = directive
                            out.entries.push_back(e)


lrh = lines_rendering_hooks_t()
lrh.hook()

# Force a refresh of IDA View-A
ida_kernwin.refresh_idaview_anyway()
