aboutsummaryrefslogtreecommitdiffstats
path: root/core/mui/cairo_inner_tl.rb
blob: a464587a075a40b117e9ef7bf4c94fb7d4174130 (plain)
1
2
3
4
5
6
7
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
51
52
53
54
55
56
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# -*- coding: utf-8 -*-

miquire :mui, 'timeline', 'tree_view_pretty_scroll', 'dark_matter_prification'
miquire :lib, 'uithreadonly'
require 'gtk2'

class Gtk::TimeLine::InnerTL < Gtk::CRUD

  include UiThreadOnly
  include Gtk::TreeViewPrettyScroll
  include Gtk::InnerTLDarkMatterPurification

  attr_writer :force_retrieve_in_reply_to
  attr_accessor :postbox, :imaginary
  type_register('GtkInnerTL')

  # TLの値を返すときに使う
  Record = Struct.new(:uri, :message, :order, :miracle_painter)

  URI = 0
  MESSAGE = 1
  ORDER = 2
  MIRACLE_PAINTER = 3

  def self.current_tl
    ctl = @@current_tl and @@current_tl.toplevel.focus.get_ancestor(Gtk::TimeLine::InnerTL) rescue nil
    ctl if(ctl.is_a?(Gtk::TimeLine::InnerTL) and not ctl.destroyed?)
  end

  # ==== Args
  # [from] 設定値を引き継ぐ元のInnerTL
  def initialize(from = nil)
    super()
    @force_retrieve_in_reply_to = :auto
    @@current_tl ||= self
    @iter_dict = {} # String: Gdk::TreeIter
    @order = ->(m) { m.modified.to_i }
    self.name = 'timeline'
    set_headers_visible(false)
    set_enable_search(false)
    last_geo = nil
    selection.mode = Gtk::SELECTION_MULTIPLE
    get_column(0).set_sizing(Gtk::TreeViewColumn::AUTOSIZE)
    extend(from) if from
    set_events end

  def cell_renderer_message
    @cell_renderer_message ||= Gtk::CellRendererMessage.new
  end

  def column_schemer
    [ {:renderer => lambda{ |x,y|
          cell_renderer_message.tree = self
          cell_renderer_message
        },
        :kind => :uri, :widget => :text, :type => String, :label => ''},
      {:kind => :text, :widget => :text, :type => Message},
      {:kind => :text, :type => Integer},
      {:kind => :text, :type => Object}
    ].freeze
  end

  def get_order(m)
    @order.call(m) end

  # レコードの並び順を決めるブロックを登録する。ブロックは一つの Message を受け取り、数値を返す
  # ==== Args
  # [&block] 並び順を決めるブロック
  # ==== Return
  # self
  def set_order(&block)
    @order = block end

  def menu_pop(widget, event)
  end

  def handle_row_activated
  end

  def reply(options = {})
    ctl = Gtk::TimeLine::InnerTL.current_tl
    pb = nil
    if(ctl)
      options = options.dup
      options[:before_post_hook] = lambda{ |this|
        get_ancestor(Gtk::Window).set_focus(self) unless self.destroyed? }
      pb = Gtk::PostBox.new(options).show_all
      postbox.closeup(pb)
      pb.on_delete(&Proc.new) if block_given?
      get_ancestor(Gtk::Window).set_focus(pb.post)
      ctl.selection.unselect_all end
    pb end

  def add_postbox(i_postbox)
    reply(i_postbox.options)
  end

  def set_cursor_to_display_top
    iter = model.iter_first
    set_cursor(iter.path, get_column(0), false) if iter end

  def get_active_messages
    get_active_iterators.map{ |iter| iter[1] } end

  def get_active_iterators
    selected = []
    if not destroyed?
      selection.selected_each{ |model, path, iter|
        selected << iter } end
    selected end

  def get_active_pathes
    selected = []
    if not destroyed?
      selection.selected_each{ |model, path, iter|
        selected << path } end
    selected end

  # _message_ のレコードの _column_ 番目のカラムの値を _value_ にセットする。
  # 成功したら _value_ を返す
  def update!(message, column, value)
    iter = get_iter_by_message(message)
    if iter
      iter[column] = value
      # 更新の結果、高さが変わる可能性がある
      iter[MIRACLE_PAINTER].reset_height
      value end end

  # タイムラインの内容を全て削除する
  # ==== Return
  # self
  def clear
    deleted = @iter_dict
    @iter_dict = {}
    deleted.values.each{ |iter| iter[MIRACLE_PAINTER].destroy }
    model.clear end

  # _path_ からレコードを取得する。なければnilを返す。
  def get_record(path)
    iter = model.get_iter(path)
    if iter
      Record.new(iter[0], iter[1], iter[2], iter[3]) end end

  # _message_ に対応する Gtk::TreePath を返す。なければnilを返す。
  def get_path_by_message(message)
    get_path_and_iter_by_message(message)[1] end

  # _message_ に対応する値の構造体を返す。なければnilを返す。
  def get_record_by_message(message)
    get_record_by_uri(message.uri.to_s) end

  # _message_ に対応する値の構造体を返す。なければnilを返す。
  def get_record_by_uri(uri)
    path = get_path_and_iter_by_uri(uri)[1]
    if path
      get_record(path) end end

  def force_retrieve_in_reply_to
    if(:auto == @force_retrieve_in_reply_to)
      UserConfig[:retrieve_force_mumbleparent]
    else
      @force_retrieve_in_reply_to end end

  # _message_ が含まれているかを返す
  # ==== Args
  # [message] Message タイムラインに含まれているかどうかを確認するMessage
  # ==== Return
  # 含まれていれば真
  def include?(message)
    @iter_dict.has_key?(message.uri.to_s) end

  # Gtk::TreeIterの対を再利用できるように登録しておく
  # ==== Args
  # [iter] Gtk::TreeIter
  # ==== Return
  # self
  def set_iter_dict(iter)
    uri = iter[URI]
    if not @iter_dict.has_key?(uri)
      @iter_dict[uri] = iter
      iters = @iter_dict
      iter[MIRACLE_PAINTER].signal_connect(:destroy) {
        iters.delete(uri)
        false } end
    self end


  # 別の InnerTL が自分をextend()した時に呼ばれる
  def extended
    if @destroy_child_miraclepainters and signal_handler_is_connected?(@destroy_child_miraclepainters)
      signal_handler_disconnect(@destroy_child_miraclepainters) end end

  private

  # self に _from_ の内容をコピーする
  # ==== Args
  # [from] InnerTL
  # ==== Return
  # self
  def extend(from)
    @force_retrieve_in_reply_to = from.instance_eval{ @force_retrieve_in_reply_to }
    @imaginary = from.imaginary
    from.extended
    from.model.each{ |from_model, from_path, from_iter|
      iter = model.append
      iter[URI] = from_iter[URI]
      iter[MESSAGE] = from_iter[MESSAGE]
      iter[ORDER] = from_iter[ORDER]
      iter[MIRACLE_PAINTER] = from_iter[MIRACLE_PAINTER].set_tree(self)
      set_iter_dict(iter) }
    self
  end

  def set_events
    @destroy_child_miraclepainters = signal_connect(:destroy) {
      model.each{ |m, p, iter|
        iter[MIRACLE_PAINTER].destroy }
    }
    signal_connect(:focus_in_event){
      @@current_tl.selection.unselect_all if not(@@current_tl.destroyed?) and @@current_tl and @@current_tl != self
      @@current_tl = self
      false } end

  # _message_ に対応する Gtk::TreeIter を返す。なければnilを返す。
  def get_iter_by_message(message)
    get_path_and_iter_by_message(message)[2] end

  # _message_ に対応する Gtk::TreeIter を返す。なければnilを返す。
  def get_iter_by_uri(uri)
    get_path_and_iter_by_uri(uri)[2] end

  # _message_ から [model, path, iter] の配列を返す。見つからなかった場合は空の配列を返す。
  def get_path_and_iter_by_message(message)
    get_path_and_iter_by_uri(message.uri.to_s) end

  def get_path_and_iter_by_uri(uri)
    uri = uri.to_s
    iter = @iter_dict[uri]
    if iter
      if iter[MIRACLE_PAINTER].destroyed?
        warn "destroyed miracle painter in cache (##{uri})"
        @iter_dict.delete(uri)
        []
      else
        [model, iter.path, iter] end
    else
      [] end end

end