3 ## A description of the attributes:
5 ## @rowid (Integer) from 1 to the limit of the SQL database
6 ## contains an Integer with the Row ID of the neuron in the sql database.
8 ## @key (String or nil)
9 ## contains the key, if it exists. Texts usually don't have a key.
11 ## @type (String) one of { cmd map opt tag txt }
12 ## depending on the type, the neuron might have a different behaviour
15 ## the creation time, as an integer time stamp. 0 if not found.
18 ## the modification time, as an integer time stamp. 0 if not found.
20 ## @prio (Integer or Float)
21 ## The priority, for sorting.
23 ## @tags (Array<String>)
24 ## A list of strings, the tags that are assigned to this neuron.
26 ## @value (may be any type)
27 ## The value is normally a string or nil. Options might use @info2 to specify
28 ## other types. The main content is here, eg text, command code, setting values
30 ## @descr (String or nil)
31 ## If this is set, it might be shown instead of the usual "Type: key => value"
33 ## @onchg (String or nil)
34 ## The code which is executed every time the value is changed via value=
36 ## @sortstring (String)
37 ## The string by which it will be sorted when using the value keyword.
38 ## This is the same as @value, except for options, where it's the raw string.
40 ## @time (false or Time)
41 ## is false, unless @type is "txt" and @info1 is not nil
43 ## @bold_on_the_left, @bold_on_the_right (Integer)
44 ## how many characters from the left or from the right should be made bold?
45 ## @bold_on_the_left is usually 18,
46 ## @bold_on_the_right is the width of the string that indicates priority.
49 ## each neuron has generic info-attributes, which contain type-specific data.
50 ## Here is a Guidemap. As you can see,
51 ## there is still plenty of room for additions in the future.
52 ## Type | Long Name | @info1 | @info2 | @info3
53 ## -----+--------------+----------------------+--------------+------------
54 ## cmd | Command | Tabcompletion Code | |
56 ## opt | Option | (used to be @onchg) | Datatype(s) |
58 ## txt | Text | Deadline (timestamp) | Reoccur Code |
65 @rowid, @key, @type, @ctime, @mtime, @prio, @tags,
66 @value, @descr, @onchg, @info1, @info2, @info3 = table
69 @prio = @prio ? @prio.to_i : 0
70 @value = @value ? @value : ""
72 @key = @key ? @key : ""
76 @tags = Convert.query_to_tags(@tags)
81 @value = Convert.query_to_option(value, @info2)
86 if @tags.include? 'always_now'
87 @info1 = $now.to_i - 1
88 @time = Time.at(@info1)
89 elsif @info1 and not @info1.empty?
91 @time = Time.at(@info1)
92 if @info2 and not @info2.empty? and @info1 < $now.to_i and @tags.include?('r')
98 @bold_on_the_left = 18
99 @bold_on_the_right = 0
103 def self.from_query(condition, *args)
104 test = Query.get_where(condition, *args)
105 test ? new(test) : EmptyNeuron
108 def self.from_rowid(n)
109 from_query("rowid=?", n) || EmptyNeuron
112 def self.find(type, key)
113 from_query("type=? AND key=?", type, key) || EmptyNeuron
117 ## attributes and related stuff {{{
120 PAD_WITHOUT_TIME = 10
132 %w(key type ctime mtime prio descr onchg info1 info2 info3).each do |key|
134 def #{key}() @#{key} end
138 Query.set(self, '#{key}', x)
143 attr_reader(*%w(rowid tags value bold_on_the_left bold_on_the_right sortstring time raw_value tagstring))
146 def empty?() false end
153 @tags = Convert.string_to_tags(x)
158 Query.set(self, 'tags', Convert.tags_to_query(@tags))
160 update_reoccur_option if @tags.include?('r')
164 def update_reoccur_option
165 if Opt.next_reoccur.nil?
166 Query.create_opt("next_reoccur", @info1, "int")
167 elsif Opt.next_reoccur.to_i > @info1 or Opt.next_reoccur.to_i == 0
168 Opt.next_reoccur = @info1
179 # self.tags = (self.tags + args).uniq
182 def tags_remove(*args)
186 def tags_toggle(*args)
188 if @tags.include? arg
214 Query.set(self, 'info1', @info1)
219 update_reoccur_option
221 Query.set(self, 'info2', (@info2 =x))
224 ## sets the value to arg. onchange-actions and query-to-option
225 ## conversions are handled here.
229 (arg.is_a? String and
231 String === @info2 and @info2 !~ /^str/))
232 arg = Convert.query_to_option(arg, @info2)
234 if @onchg and not @onchg.empty?
236 if code =~ /^alias (\w+)$/
237 ## TODO: @onchg aliases
250 @value = result unless result.nil?
259 ## updates the value in the database.
260 def update_value_in_database()
262 query_value = Convert.option_to_query(@value, @info2)
267 Query.set(self, 'value', query_value)
270 ## use set_to(arg) and if the value has changed, update it in the database.
274 ## don't use destructive operations on @value, pretty please.
275 ## if its rly necessary, use oldval = @value.dup above
279 update_value_in_database()
283 ## TODO: loop detection :(
285 log "I (#{@value[0..20]}..)ADVANCE BY #{int}"
287 @time = Time.at(@info1)
288 self.tags += ['r'] unless @tags.include?('r')
292 log "I (#{@value[0..20]}..)DESCEND BY #{int}"
294 @time = Time.at(@info1)
295 # self.tags += ['r'] unless @tags.include?('r')
298 def run_code_in_environment(code)
299 env = Environment.new
303 env.oldvalue = oldval
305 result = env.run(code)
306 val = result unless result.nil?
309 def is_text?() @type == "txt" end
311 def key?() not (@key.nil? or @key.empty?) end
312 def value?() not (@value.nil? or @value.empty?) end
313 def option?() @type == "opt" end
314 def keymap?() @type == "map" end
315 def command?() @type == "cmd" end
316 def text?() @type == "txt" end
317 def tag?() @type == "tag" end
318 def time?() @time != false end
319 def descr?() @descr and !@descr.empty? end
324 update_reoccur_option if @tags.include? 'r'
329 return 1 unless text? or command?
330 self.line(0) unless @line
334 (command?) ? (@info1 and not @info1.empty?) : (false)
337 (command? and @info1) ? (@info1) : ('')
341 initialize(Query.get_where("rowid=#{@rowid}"))
359 @width = -1 ## to force a resize
370 Info.request_draw = true
381 return "..." unless text? or command?
383 return @line[n] || "<nil>" if @line
389 rest = @value.dup.strip
391 max = cols - PAD_WITHOUT_TIME
396 @line << "" unless rest.empty?
400 current = rest[0...max]
401 if ix = current.index("\n")
402 current = current[0...ix]
403 rest.slice!(0, ix + 1)
407 @line << current.ljust(max)
412 def generate_new_string
421 if Array === (hide = Opt.hide_tags_dynamic) and !hide.empty?
424 if Array === (hide = Opt.hide_tags_static) and !hide.empty?
427 @tagstring = tmp.join(" ")
432 if Numeric === @prio and @prio != 0
434 str = " -#{@prio.abs}"
436 str = " #{'-' * @prio.to_i.abs}"
440 str = " #{'+' * @prio.to_i}"
443 @bold_on_the_right = str.size
445 @bold_on_the_right = 0
451 @left << (@value.include?("\n") ? "**" : " ")
453 @bold_on_the_left = PAD_WITHOUT_TIME
455 @left << "#{Convert.time_to_string(@info1).rjust(18)}: "
456 @bold_on_the_left = PAD_WITH_TIME
459 @bold_on_the_left = PAD_WITH_TYPE
460 @left << "#{TYPES_MAP[@type].rjust(10) rescue ''}: "
463 if @descr and !@descr.empty? and @type != "txt"
464 unless @key.nil? or (@key.is_a? String and @key.empty?)
467 @bold_on_the_left = @left.size
470 @left << "#{@descr.nolinebreaks}"
473 unless @key.nil? or (String === @key and @key.empty?)
478 val = (@value.is_a? String) ? @value.to_s : @value.inspect
479 if pos = val.index("\n")
483 val = val.nolinebreaks
492 ## don't regenerate the whole string if the window is resized.
493 ## There are two seperate parts, a left and a right part of the string,
494 ## and this method puts them together.
495 def resize(new_width)
498 if @width < @right.size + 30
499 ## favour left side, so you can see shit when the terminal is small
500 left = @left [0..@width]
501 right = @right[0..@width-left.size]
502 @string = left.ljust(@width - right.size) + right
505 right = @right[0..@width]
506 left = @left [0...@width-right.size]
507 @string = left + right.rjust(@width - left.size)