aboutsummaryrefslogtreecommitdiffstats
path: root/core/mui/cairo_cell_renderer_message.rb
blob: e93bf2a7493f978d8537759c26514ffeaceb999a (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
# -*- coding: utf-8 -*-

miquire :mui, 'miracle_painter'

require 'gtk2'

module Gtk
  class CellRendererMessage < CellRendererPixbuf
    type_register
    install_property(GLib::Param::String.new("uri", "uri", "Resource URI", "hoge", GLib::Param::READABLE|GLib::Param::WRITABLE))

    attr_reader :message

    def initialize()
      super() end

    # Register events for this Renderer:
    signal_new("button_press_event", GLib::Signal::RUN_FIRST, nil, nil,
               Gdk::EventButton, Gtk::TreePath, Gtk::TreeViewColumn,
               Integer, Integer)

    signal_new("button_release_event", GLib::Signal::RUN_FIRST, nil, nil,
               Gdk::EventButton, Gtk::TreePath, Gtk::TreeViewColumn,
               Integer, Integer)

    signal_new("motion_notify_event", GLib::Signal::RUN_FIRST, nil, nil,
               Gtk::BINDING_VERSION >= [2,0,3] ? Gdk::EventMotion : Gdk::EventButton,
               Gtk::TreePath, Gtk::TreeViewColumn,
               Integer, Integer)

    signal_new("leave_notify_event", GLib::Signal::RUN_FIRST, nil, nil,
               Gtk::BINDING_VERSION >= [2,0,3] ? Gdk::EventCrossing : Gdk::EventButton,
               Gtk::TreePath, Gtk::TreeViewColumn,
               Integer, Integer)

    signal_new("click", GLib::Signal::RUN_FIRST, nil, nil,
               Gdk::EventButton, Gtk::TreePath, Gtk::TreeViewColumn,
               Integer, Integer)

    def signal_do_button_press_event(e, path, column, cell_x, cell_y)
    end

    def signal_do_button_release_event(e, path, column, cell_x, cell_y)
    end

    def signal_do_motion_notify_event(e, path, column, cell_x, cell_y)
    end

    def signal_do_leave_notify_event(e, path, column, cell_x, cell_y)
    end

    def signal_do_click(e, path, column, cell_x, cell_y)
    end

    def tree=(tree)
      @tree = tree
      tree.add_events(Gdk::Event::BUTTON_PRESS_MASK|Gdk::Event::BUTTON_RELEASE_MASK)
      armed_column = nil
      last_motioned = nil
      click_start = []

      tree.ssc("leave_notify_event") { |w, e|
        if last_motioned
          signal_emit("leave_notify_event", e, *last_motioned)
          last_motioned = nil end
        false }

      tree.ssc("motion_notify_event") { |w, e|
        path, column, cell_x, cell_y = tree.get_path_at_pos(e.x, e.y)
        if column
          armed_column = column
          motioned = [path, column, cell_x, cell_y]
          signal_emit("motion_notify_event", e, *motioned)
          if last_motioned
            motioned_message = @tree.get_record(motioned[0]).message rescue nil
            last_motioned_message = @tree.get_record(last_motioned[0]).message rescue nil
            if(last_motioned_message and motioned_message != last_motioned_message)
              emit_leave_notify_from_event_motion(e, *last_motioned) end end
          last_motioned = motioned end }

      tree.ssc("button_press_event") { |w, e|
        path, column, cell_x, cell_y = tree.get_path_at_pos(e.x, e.y)
        click_start = [cell_x, cell_y]
        if column
          armed_column = column
          signal_emit("button_press_event", e, path, column, cell_x, cell_y) end
        e.button == 3 && tree.get_active_pathes.include?(path) # 選択してるものを右クリックした時は、他のセルの選択を解除しない
      }

      tree.ssc("button_release_event") { |w, e|
        path, column, cell_x, cell_y = tree.get_path_at_pos(e.x, e.y)
        if column
          cell_x ||= -1
          cell_y ||= -1
          signal_emit("button_release_event", e, path, column, cell_x, cell_y)
          if click_start.size == 2 and click_start.all?{|x| x.respond_to? :-} and (column == armed_column) and (click_start[0] - cell_x).abs <= 4 and (click_start[1] - cell_y).abs <= 4
            signal_emit("click", e, path, column, cell_x, cell_y) end
          armed_column = nil end }

      last_selected = Set.new(tree.selection.to_enum(:selected_each).map{ |m, p, i| i[3] }).freeze
      tree.selection.ssc("changed") { |this|
        now_selecting = Set.new(this.to_enum(:selected_each).map{ |m, p, i| i[3] }).freeze
        new_selected = now_selecting - last_selected
        unselected = last_selected - now_selecting
        new_selected.each(&:on_selected)
        unselected.each(&:on_unselected)
        last_selected = now_selecting
        false }

      event_hooks end

    # Messageに関連付けられた Gdk::MiraclePainter を取得する
    def miracle_painter(message)
      record = @tree.get_record_by_message(message)
      if record and record.miracle_painter
        record.miracle_painter
      else
        @tree.update!(message, Gtk::TimeLine::InnerTL::MIRACLE_PAINTER, create_miracle_painter(message)) end end

    # MiraclePainterを生成して返す
    def create_miracle_painter(message)
      Gdk::MiraclePainter.new(message, avail_width).set_tree(@tree)
    end

    def uri=(uri)
      record = @tree.get_record_by_uri(uri)
      if record and record.message
        return render_message(record.message)
      else
        self.pixbuf = GdkPixbuf::Pixbuf.new(file: Skin.get('notfound.png')) end
    rescue Exception => err
      error "#{err.class} by uri: #{uri} model: #{record ? record.message.inspect : nil}"
      raise if Mopt.debug
      error err
      self.pixbuf = GdkPixbuf::Pixbuf.new(file: Skin.get('notfound.png')) end

    private

    def user
      message[:user]
    end

    def render_message(message)
      if(@tree.realized?)
        h = miracle_painter(message).height
        miracle_painter(message).width = @tree.get_cell_area(nil, @tree.get_column(0)).width
        if(h != miracle_painter(message).height)
          @tree.get_column(0).queue_resize end end
      self.pixbuf = miracle_painter(message).pixbuf end

    # 描画するセルの横幅を取得する
    def avail_width
      [@tree.get_column(0).width, 100].max
    end

    def event_hooks
      last_pressed = nil
      ssc(:click, @tree){ |r, e, path, column, cell_x, cell_y|
        record = @tree.get_record(path)
        record.miracle_painter.clicked(cell_x, cell_y, e) if record
        false }
      ssc(:button_press_event, @tree){ |r, e, path, column, cell_x, cell_y|
        record = @tree.get_record(path)
        if record
          last_pressed = record.miracle_painter
          if e.button == 1
            last_pressed.pressed(cell_x, cell_y) end
          Delayer.new(:ui_response) {
            Plugin::GUI.keypress(::Gtk::buttonname([e.event_type, e.button, e.state]), @tree.imaginary) }
        end
        false }
      ssc(:button_release_event, @tree){ |r, e, path, column, cell_x, cell_y|
        if e.button == 1 and last_pressed
          record = @tree.get_record(path)
          if record
            if(last_pressed == record.miracle_painter)
              last_pressed.released(cell_x, cell_y)
            else
              last_pressed.released end end
          last_pressed = nil end
        false }
      ssc(:motion_notify_event, @tree){ |r, e, path, column, cell_x, cell_y|
        record = @tree.get_record(path)
        record.miracle_painter.point_moved(cell_x, cell_y) if record
        false }
      ssc(:leave_notify_event, @tree){ |r, e, path, column, cell_x, cell_y|
        record = @tree.get_record(path)
        record.miracle_painter.point_leaved(cell_x, cell_y) if record
        false }
    end

    # RubyGtk2 2.0.3以降は、motion_notify_eventやleave_notify_eventに
    # 発行されるイベントが変更されている
    if Gtk::BINDING_VERSION >= [2,0,3]
      def emit_leave_notify_from_event_motion(e, *args)
        signal_emit("leave_notify_event",
                    Gdk::EventCrossing.new(Gdk::Event::LEAVE_NOTIFY).tap{ |le|
                      le.time = e.time
                      le.x, le.y = e.x, e.y
                      le.x_root, le.y_root = e.x_root, e.y_root
                      le.focus = true
                    }, *args) end
    else
      def emit_leave_notify_from_event_motion(e, *args)
        signal_emit("leave_notify_event", e, *args) end end

  end
end