Class: MenuWindow

Inherits:
Object
  • Object
show all
Defined in:
lib/menu_window.rb

Overview

Wraps a collection of objects into a menu window

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(title, scr, items, current_index, matches = nil, options = {}) ⇒ MenuWindow

Matches are passed in when a search succeeds and certain items need to be indicated as matches TODO This should be changed into a hash parameter with keys



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/menu_window.rb', line 8

def initialize(title, scr, items, current_index, matches=nil, options={})

  @options = options
  @matches = matches
  @height = scr.maxy - 6
  @width = scr.maxx - 2
  scr.clear
  scr.setpos(2, 5)
  scr.refresh

  @title = title
  @title_box = Curses::Window.new(2, @width, 2, 5)

  # @window is the main content window
  
  @window = Curses::Window.new(@height, @width, 4, 2)

  @items = items 
  if @items.empty?
    @title_box.addstr(@title)
    @title_box.refresh
    @window.clear
    @window.addstr("   No items")
    @window.refresh
    # need to disable some commands in the controller
    return
  end
  @pager = MenuPager.new(@height, @width)
  @current_index = current_index || 0

  @window.clear if @window

  # page_content is an array of hashes with keys :item and :text
  @page_index, @page_content = create_page
  
  draw_menu(@page_content)

  draw_title

  draw_arrow
  # draw pointer at current selection
  @window.refresh
end

Instance Attribute Details

#current_indexObject

Returns the value of attribute current_index.



3
4
5
# File 'lib/menu_window.rb', line 3

def current_index
  @current_index
end

#itemsObject

Returns the value of attribute items.



3
4
5
# File 'lib/menu_window.rb', line 3

def items
  @items
end

#matchesObject

Returns the value of attribute matches.



3
4
5
# File 'lib/menu_window.rb', line 3

def matches
  @matches
end

#search_stringObject

Returns the value of attribute search_string.



3
4
5
# File 'lib/menu_window.rb', line 3

def search_string
  @search_string
end

Instance Method Details

#beginningObject



387
388
389
390
391
# File 'lib/menu_window.rb', line 387

def beginning
  erase_arrow
  @current_index = @items.size - 1
  move_arrow_or_page
end

#bottomObject



371
372
373
374
375
376
377
378
379
# File 'lib/menu_window.rb', line 371

def bottom
  new_index = (@page_index + 1) * @height - 1 
  if new_index >= @items.size
    new_index = @items.size - 1
  end
  erase_arrow
  @current_index = new_index
  move_arrow_or_page
end

#cancel_searchObject

Cancels local search only



100
101
102
103
104
# File 'lib/menu_window.rb', line 100

def cancel_search # Cancels local search only
  @matches = nil
  @search_string = nil
  draw_menu
end

#closeObject



413
414
415
416
417
# File 'lib/menu_window.rb', line 413

def close
  @window.clear
  @window.refresh
  @window.close
end

#create_pageObject



52
53
54
# File 'lib/menu_window.rb', line 52

def create_page
  @pager.create_page(@items, @current_index, @matches, @options)
end

#draw_arrowObject



223
224
225
226
227
228
229
230
231
232
233
# File 'lib/menu_window.rb', line 223

def draw_arrow
  # calculate where arrow should be drawn
  LOGGER.debug("Drawing arrow. Current index: #{@current_index}, Page index: #{@page_index}, Height: #{@height}")
  y = (@current_index - @page_index * @height) 
  x = 0
  LOGGER.debug "Drawing arrow at y #{y}"
  @window.setpos(y, x)
  @window.addstr("->")
  # move the cursor 
  #@window.setpos(0,0)
end

#draw_line(text, item) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/menu_window.rb', line 119

def draw_line(text, item)
  if item.is_a?(Entry)

    color = case 
            when item.respond_to?(:flagged) && item.flagged && !@options[:turn_off_flagged_color] 
              :red
#                when item.respond_to?(:is_new?) && item.is_new?
#                  :green

            else
              nil
            end
    
    # local matches take precedence
    matches = @matches || @global_matches
    search_string = @search_string || @global_search_string

    if matches && !matches.empty? && matches.include?( @items.index(item) )
      LOGGER.debug("matching line detected : search string #{search_string} : text: #{text}")
      text = text.gsub(/(#{search_string})/i, '%%%\1%%%') 
      text.split(/%%%/).each_with_index do |chunk, index|
        if index % 2 == 0
          @window.addstr(chunk, color)
        else 
          @window.addstr(chunk, :yellow)
        end
      end
    else
      @window.addstr(text, color)
    end

    if @options[:show_feed_titles]  # put the feed title in the title
      @window.addstr(" #{item.feed.title}", :cyan)
    end

    item_date = case 
      when @options[:feed_title] == "All Entries"
        item.created_at
      when @options[:feed_title] == "Flagged Entries"
        item.flagged
      else
        item.date_published
      end
    if item_date 
      item_date = " #{time_ago_in_words(item_date)} ago"
    else
      item_date = ''
    end
    @window.addstr(item_date, :magenta)
    
    @window.addstr("\n")

  # a feed or virtual feed
  else 

    color = case 
            when item.is_a?(VirtualFeed) && text =~ /Flagged Entries/
              :red
            else
              nil
            end
    @window.addstr(text, color)

    # May not be the case with some virtual feeds
    # Add a column to feed that cached this attribute after an feed
    # update_self
    if item.is_a?(VirtualFeed) && item.title == "All Entries"
      # Use the created_at of the last entry
      entry = Entry.find(:first, :order => "id desc")
      last_updated = entry ? "#{time_ago_in_words(entry.created_at)} ago\n" : "\n"

      @window.addstr(" " + last_updated, :magenta)

    elsif item.is_a?(VirtualFeed) && item.title == "Flagged Entries"
      # Use the flagged timetamp of the last entry
      entry = Entry.find(:first, :conditions => "flagged IS NOT NULL", :order => "flagged desc")
      last_updated = entry ? "#{time_ago_in_words(entry.flagged)} ago\n" : "\n"
      @window.addstr(" " + last_updated , :magenta)

    elsif item.last_updated 
      most_recent_entry = item.entries.first
      last_updated = (most_recent_entry && most_recent_entry.date_published) ? 
        most_recent_entry.date_published : item.last_updated
      if last_updated
        last_updated = time_ago_in_words(last_updated) + " ago\n"
      else
        last_updated = ''
      end
      @window.addstr(" " + last_updated , :magenta)
    else 
      @window.addstr("\n")
    end
  end
end

#draw_menu(items = @page_content) ⇒ Object

Draws the actual menu on the screen, with appropriate color highlighting The color of the highlighting is determined by the model. The item should have a :color attribute that returns the appropriate color as a symbol, which will be looked up on the Curses::Color table.



110
111
112
113
114
115
116
117
# File 'lib/menu_window.rb', line 110

def draw_menu(items=@page_content)
  @window.setpos(0, 0)
  items.each do |x|
    draw_line(x[:text], x[:item])
  end
  draw_arrow
  @window.refresh
end

#draw_titleObject

TODO put the date range of the items in the title



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/menu_window.rb', line 57

def draw_title
  @title_box.clear
  
  # This handles a list of feeds, which don't really have a date range
  unless @items.first && @items.first.respond_to?(:date_published)
    @title_box.addstr(@title)
    @title_box.refresh
    return
  end

  # Get the date range from the items in the page content
  dates = @page_content.map {|x| x[:item]}.map {|i| 
    if i.respond_to?(:date_published)
     i.date_published 
    elsif i.respond_to?(:last_updated)
     i.last_updated 
    end
  }
  most_recent_date = dates.max.strftime('%B %d %Y')
  oldest_date = dates.min.strftime('%B %d %Y')

  if most_recent_date != oldest_date 
    @title_box.addstr(@title + " | " + oldest_date + " - " + most_recent_date)
  else
    @title_box.addstr(@title + " | " + oldest_date)
  end
  @title_box.refresh
end

#endObject



381
382
383
384
385
# File 'lib/menu_window.rb', line 381

def end
  erase_arrow
  @current_index = 0
  move_arrow_or_page
end

#erase_arrowObject



261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/menu_window.rb', line 261

def erase_arrow
  # calculate where arrow should be drawn
  y = (@current_index - @page_index * @height) 
  x = 0
  LOGGER.debug "Erasing arrow at y #{y}"
  @window.setpos(y, x)
  @window << "  "
  return
  @window.delch
  @window.delch
  @window.delch
  @window.addstr
end

#flag_allObject



254
255
256
257
258
259
# File 'lib/menu_window.rb', line 254

def flag_all
  @items.each do |item|
    item.update_attribute(:flagged, Time.now)

  end
end

#highlight_global_matches(matches, search_string) ⇒ Object



93
94
95
96
97
98
# File 'lib/menu_window.rb', line 93

def highlight_global_matches(matches, search_string)
  LOGGER.debug("setting matches to #{matches.inspect}")
  @global_search_string = search_string
  @global_matches = matches
  draw_menu
end

#highlight_matches(matches, search_string) ⇒ Object



86
87
88
89
90
91
# File 'lib/menu_window.rb', line 86

def highlight_matches(matches, search_string)
  LOGGER.debug("setting matches to #{matches.inspect}")
  @search_string = search_string
  @matches = matches
  draw_menu
end

#middleObject



360
361
362
363
364
365
366
367
368
369
# File 'lib/menu_window.rb', line 360

def middle
  new_index = (@page_index + 1) * (@height / 2)
  # Don't want the cursor positioned in a void when the list is short
  if new_index >= @items.size
    new_index = @items.size - 1
  end
  erase_arrow
  @current_index = new_index
  move_arrow_or_page
end

#move_arrow_or_pageObject



393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
# File 'lib/menu_window.rb', line 393

def move_arrow_or_page
  new_page_index = @pager.calculate_page_for_index(@current_index)
  
  LOGGER.debug("Current page: #{@page_index}; New Page number: #{new_page_index}")
  if new_page_index == @page_index
    LOGGER.debug("Moving arrow")
    # move arrow
    draw_arrow
    @window.refresh
  else
    LOGGER.debug("Changing page")
    # change page

    # TODO  change this implementation
    @page_index, @page_content = create_page

    redraw_menu
  end
end

#move_to_item(item_index) ⇒ Object

Used to moved in constrainted way among a subset (search matches) of the larger set



277
278
279
280
281
282
# File 'lib/menu_window.rb', line 277

def move_to_item(item_index)
  # move arrow, or change page
  erase_arrow
  @current_index = item_index
  move_arrow_or_page
end

#move_to_next_item_in_set(set) ⇒ Object

set is a set of indexes to the original larger set



285
286
287
288
289
290
291
292
293
294
# File 'lib/menu_window.rb', line 285

def move_to_next_item_in_set(set)
  LOGGER.debug("set : #{set}")
  LOGGER.debug("@current_index : #{@current_index}")
  current_index = set.index( @current_index )
  # move arrow, or change page
  return if current_index == set.length - 1
  LOGGER.debug("current_index : #{current_index}")
  new_index = set[current_index + 1]
  move_to_item(new_index)
end

#move_to_prev_item_in_set(set) ⇒ Object



296
297
298
299
300
301
302
# File 'lib/menu_window.rb', line 296

def move_to_prev_item_in_set(set)
  current_index = set.index( @current_index )
  # move arrow, or change page
  return if current_index == 0
  new_index = set[current_index - 1]
  move_to_item(new_index)
end

#next_item(multiplier) ⇒ Object



305
306
307
308
309
310
311
312
313
314
315
# File 'lib/menu_window.rb', line 305

def next_item(multiplier)
  # move arrow, or change page
  new_index = @current_index + multiplier 
  LOGGER.debug("New index is #{new_index}")
  if new_index >= @items.size
    new_index = @items.size - 1
  end
  erase_arrow
  @current_index = new_index
  move_arrow_or_page
end

#next_pageObject



328
329
330
331
332
333
334
335
336
# File 'lib/menu_window.rb', line 328

def next_page
  new_index = (@page_index + 1) * @height
  if new_index >= @items.size
    new_index = @items.size - 1
  end
  erase_arrow
  @current_index = new_index
  move_arrow_or_page
end

#prev_item(multiplier) ⇒ Object



317
318
319
320
321
322
323
324
325
326
# File 'lib/menu_window.rb', line 317

def prev_item(multiplier)
  # move arrow, or change page
  new_index = @current_index - multiplier
  if new_index < 0
    new_index = 0
  end
  erase_arrow
  @current_index = new_index
  move_arrow_or_page
end

#prev_pageObject



338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/menu_window.rb', line 338

def prev_page
  if @page_index == 0
    top
    return
  end
  new_index = (@page_index - 1) * @height
  if new_index < 0
    new_index = 0
  end
  erase_arrow
  @current_index = new_index
  move_arrow_or_page
  # need to put cursor at bottom unless this is already 1st page
  bottom
end

#redraw_menuObject



214
215
216
217
218
219
220
221
# File 'lib/menu_window.rb', line 214

def redraw_menu
  LOGGER.debug("Redrawing menu")
  @window.clear
  draw_menu(@page_content)
  draw_arrow
  draw_title
  @window.refresh
end

#toggle_flagObject

TODO change this to coloring the line



236
237
238
239
240
241
242
243
244
245
# File 'lib/menu_window.rb', line 236

def toggle_flag
  item = @items[@current_index]
  if item.flagged 
    item.update_attribute(:flagged, nil)
  else
    item.update_attribute(:flagged, Time.now)
  end

  redraw_menu
end

#topObject



354
355
356
357
358
# File 'lib/menu_window.rb', line 354

def top
  erase_arrow
  @current_index = (@page_index * @height) 
  move_arrow_or_page
end

#unflag_allObject



247
248
249
250
251
252
# File 'lib/menu_window.rb', line 247

def unflag_all
  @items.each do |item|
    item.update_attribute(:flagged, nil)

  end
end