10 CURSES_COLORS = [Curses::COLOR_BLACK, Curses::COLOR_RED, Curses::COLOR_GREEN,
11 Curses::COLOR_YELLOW, Curses::COLOR_BLUE,
12 Curses::COLOR_MAGENTA, Curses::COLOR_CYAN,
13 Curses::COLOR_WHITE, Curses::COLOR_DEFAULT]
14 NUM_COLORS = (CURSES_COLORS.size - 1) * (CURSES_COLORS.size - 1)
17 :status => { :fg => "white", :bg => "blue", :attrs => ["bold"] },
18 :index_old => { :fg => "white", :bg => "default" },
19 :index_new => { :fg => "white", :bg => "default", :attrs => ["bold"] },
20 :index_starred => { :fg => "yellow", :bg => "default", :attrs => ["bold"] },
21 :index_draft => { :fg => "red", :bg => "default", :attrs => ["bold"] },
22 :labellist_old => { :fg => "white", :bg => "default" },
23 :labellist_new => { :fg => "white", :bg => "default", :attrs => ["bold"] },
24 :twiddle => { :fg => "blue", :bg => "default" },
25 :label => { :fg => "yellow", :bg => "default" },
26 :message_patina => { :fg => "black", :bg => "green" },
27 :alternate_patina => { :fg => "black", :bg => "blue" },
28 :missing_message => { :fg => "black", :bg => "red" },
29 :attachment => { :fg => "cyan", :bg => "default" },
30 :cryptosig_valid => { :fg => "yellow", :bg => "default", :attrs => ["bold"] },
31 :cryptosig_unknown => { :fg => "cyan", :bg => "default" },
32 :cryptosig_invalid => { :fg => "yellow", :bg => "red", :attrs => ["bold"] },
33 :generic_notice_patina => { :fg => "cyan", :bg => "default" },
34 :quote_patina => { :fg => "yellow", :bg => "default" },
35 :sig_patina => { :fg => "yellow", :bg => "default" },
36 :quote => { :fg => "yellow", :bg => "default" },
37 :sig => { :fg => "yellow", :bg => "default" },
38 :to_me => { :fg => "green", :bg => "default" },
39 :starred => { :fg => "yellow", :bg => "default", :attrs => ["bold"] },
40 :starred_patina => { :fg => "yellow", :bg => "green", :attrs => ["bold"] },
41 :alternate_starred_patina => { :fg => "yellow", :bg => "blue", :attrs => ["bold"] },
42 :snippet => { :fg => "cyan", :bg => "default" },
43 :option => { :fg => "white", :bg => "default" },
44 :tagged => { :fg => "yellow", :bg => "default", :attrs => ["bold"] },
45 :draft_notification => { :fg => "red", :bg => "default", :attrs => ["bold"] },
46 :completion_character => { :fg => "white", :bg => "default", :attrs => ["bold"] },
47 :horizontal_selector_selected => { :fg => "yellow", :bg => "default", :attrs => ["bold"] },
48 :horizontal_selector_unselected => { :fg => "cyan", :bg => "default" },
49 :search_highlight => { :fg => "black", :bg => "yellow", :attrs => ["bold"] },
50 :system_buf => { :fg => "blue", :bg => "default" },
51 :regular_buf => { :fg => "white", :bg => "default" },
52 :modified_buffer => { :fg => "yellow", :bg => "default", :attrs => ["bold"] },
56 raise "only one instance can be created" if @@instance
59 @color_pairs = {[Curses::COLOR_WHITE, Curses::COLOR_BLACK] => 0}
62 yield self if block_given?
63 @entries[highlight_sym(:none)] = highlight_for(Curses::COLOR_WHITE,
68 def add sym, fg, bg, attr=nil, opts={}
69 raise ArgumentError, "color for #{sym} already defined" if @entries.member? sym
70 raise ArgumentError, "color '#{fg}' unknown" unless CURSES_COLORS.include? fg
71 raise ArgumentError, "color '#{bg}' unknown" unless CURSES_COLORS.include? bg
72 attrs = [attr].flatten.compact
74 @entries[sym] = [fg, bg, attrs, nil]
75 @entries[highlight_sym(sym)] = opts[:highlight] ? @entries[opts[:highlight]] : highlight_for(fg, bg, attrs) + [nil]
79 "#{sym}_highlight".intern
82 def highlight_for fg, bg, attrs
83 [fg, bg, attrs + [Curses::A_UNDERLINE]]
86 # when Curses::COLOR_YELLOW
92 # when Curses::COLOR_BLUE
94 # when Curses::COLOR_YELLOW, Curses::COLOR_GREEN
100 # hbg = Curses::COLOR_YELLOW
102 # when Curses::COLOR_CYAN
103 # Curses::COLOR_YELLOW
104 # when Curses::COLOR_YELLOW
111 # if attrs.include?(Curses::A_BOLD)
115 # when Curses::COLOR_BLACK
124 def color_for sym, highlight=false
125 sym = highlight_sym(sym) if highlight
126 return Curses::COLOR_BLACK if sym == :none
127 raise ArgumentError, "undefined color #{sym}" unless @entries.member? sym
129 ## if this color is cached, return it
130 fg, bg, attrs, color = @entries[sym]
131 return color if color
133 if(cp = @color_pairs[[fg, bg]])
135 else ## need to get a new colorpair
136 @next_id = (@next_id + 1) % NUM_COLORS
137 @next_id += 1 if @next_id == 0 # 0 is always white on black
139 debug "colormap: for color #{sym}, using id #{id} -> #{fg}, #{bg}"
140 Curses.init_pair id, fg, bg or raise ArgumentError,
141 "couldn't initialize curses color pair #{fg}, #{bg} (key #{id})"
143 cp = @color_pairs[[fg, bg]] = Curses.color_pair(id)
144 ## delete the old mapping, if it exists
146 @users[cp].each do |usym|
147 warn "dropping color #{usym} (#{id})"
148 @entries[usym][3] = nil
154 ## by now we have a color pair
155 color = attrs.inject(cp) { |color, attr| color | attr }
156 @entries[sym][3] = color # fill the cache
157 (@users[cp] ||= []) << sym # record entry as a user of that color pair
161 ## Try to use the user defined colors, in case of an error fall back
162 ## to the default ones.
163 def populate_colormap
164 user_colors = if File.exists? Redwood::COLOR_FN
165 debug "loading user colors from #{Redwood::COLOR_FN}"
166 Redwood::load_yaml_obj Redwood::COLOR_FN
170 Colormap::DEFAULT_COLORS.each_pair do |k, v|
171 fg = Curses.const_get "COLOR_#{v[:fg].upcase}"
172 bg = Curses.const_get "COLOR_#{v[:bg].upcase}"
173 attrs = v[:attrs] ? v[:attrs].map { |a| Curses.const_get "A_#{a.upcase}" } : []
175 if user_colors && (ucolor = user_colors[k])
176 if(ufg = ucolor[:fg])
178 fg = Curses.const_get "COLOR_#{ufg.upcase}"
180 error ||= "Warning: there is no color named \"#{ufg}\", using fallback."
181 warn "there is no color named \"#{ufg}\""
185 if(ubg = ucolor[:bg])
187 bg = Curses.const_get "COLOR_#{ubg.upcase}"
189 error ||= "Warning: there is no color named \"#{ubg}\", using fallback."
190 warn "there is no color named \"#{ubg}\""
194 if(uattrs = ucolor[:attrs])
195 attrs = [*uattrs].flatten.map do |a|
197 Curses.const_get "A_#{a.upcase}"
199 error ||= "Warning: there is no attribute named \"#{a}\", using fallback."
200 warn "there is no attribute named \"#{a}\", using fallback."
206 symbol = (k.to_s + "_color").to_sym
207 add symbol, fg, bg, attrs
210 BufferManager.flash error if error
213 def self.instance; @@instance; end
214 def self.method_missing meth, *a
215 Colormap.new unless @@instance
216 @@instance.send meth, *a