File: gtk_mtk.rb

package info (click to toggle)
mikutter 3.8.6%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 10,544 kB
  • sloc: ruby: 20,548; sh: 99; makefile: 19
file content (373 lines) | stat: -rw-r--r-- 12,625 bytes parent folder | download
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
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
# -*- coding:utf-8 -*-

require_relative '../utils'
miquire :mui, 'extension'
miquire :mui, 'message_picker'
miquire :mui, 'crud'
miquire :mui, 'keyconfig'
miquire :mui, 'selectbox'

require 'gtk2'

module Mtk
  def self.adjustment(name, config, min, max)
    container = Gtk::HBox.new(false, 0)
    container.pack_start(Gtk::Label.new(name), false, true, 0)
    adj = Gtk::Adjustment.new((UserConfig[config] or min), min*1.0, max*1.0, 1.0, 5.0, 0.0)
    spinner = Gtk::SpinButton.new(adj, 0, 0)
    spinner.wrap = true
    adj.signal_connect('value-changed'){ |widget, e|
      UserConfig[config] = widget.value.to_i
      false
    }
    container.pack_start(Gtk::Alignment.new(1.0, 0.5, 0, 0).add(spinner), true, true, 0)
  end

  def self.chooseone(key, label, values)
    values.freeze
    if key.respond_to?(:call)
      proc = key
    else
      proc = lambda{ |new|
        if new === nil
          UserConfig[key]
        else
          UserConfig[key] = new end } end
    container = Gtk::HBox.new(false, 0)
    input = Gtk::ComboBox.new(true)
    sorted = values.keys.sort_by(&:to_s).freeze
    sorted.each{ |x|
      input.append_text(values[x].respond_to?(:call) ? values[x].call(nil) : values[x])
    }
    input.active = (sorted.index{ |i| i.to_s == proc.call(*[nil, input][0, proc.arity]).to_s } or 0)
    input.signal_connect('changed'){ |widget|
      proc.call(*[sorted[widget.active], widget][0, proc.arity])
      nil
    }
    container.pack_start(Gtk::Label.new(label), false, true, 0) if label
    container.pack_start(Gtk::Alignment.new(1.0, 0.5, 0, 0).add(input), true, true, 0)
  end

  def self.choosemany(key, label, values)
    values.freeze
    if key.respond_to?(:call)
      proc = key
    else
      proc = lambda{ |new|
        if new === nil
          UserConfig[key] or []
        else
          UserConfig[key] = new end } end
    input = Gtk::SelectBox.new(values, proc.call(*[nil, input][0, proc.arity])){ |selected|
      proc.call(*[selected, input][0, proc.arity])
    }
    if label
      Gtk::HBox.new(false, 0).
        pack_start(Gtk::Label.new(label), false, true, 0).
        pack_start(Gtk::Alignment.new(1.0, 0.5, 0, 0).add(input), true, true, 0)
    else
      input end end

  def self.boolean(key, label)
    if key.respond_to?(:call)
      proc = key
    else
      proc = lambda{ |new|
        if new === nil
          UserConfig[key]
        else
          UserConfig[key] = new end } end
    input = Gtk::CheckButton.new(label)
    input.active = proc.call(*[nil, input][0, proc.arity])
    input.signal_connect('toggled'){ |widget|
      proc.call(*[widget.active?, widget][0, proc.arity]) }
    return input
  end

  def self.message_picker(key)
    if key.respond_to?(:call)
      proc = key
    else
      proc = lambda{ |new|
        if new.nil?
          UserConfig[key]
        else
          UserConfig[key] = new.freeze end } end
    input = Gtk::MessagePicker.new(proc.call(*[nil, input][0, proc.arity])){
        proc.call(*[ input.to_a, input][0, proc.arity]) }
    input.signal_connect(:destroy){
      proc.call(*[ input.to_a, input][0, proc.arity]) }
    return input
  end

  def self.default_or_custom(key, title, default_label, custom_label)
    group = default = Gtk::RadioButton.new(default_label)
    custom = Gtk::RadioButton.new(group, custom_label)
    input = Gtk::Entry.new
    input.text = UserConfig[:url_open_command] if UserConfig[:url_open_command].is_a?(String)
    default.active = !(input.sensitive = custom.active = UserConfig[key])
    default.signal_connect('toggled'){ |widget|
      UserConfig[key] = nil
      input.sensitive = !widget.active?
    }
    custom.signal_connect('toggled'){ |widget|
      UserConfig[key] = input.text
      input.sensitive = widget.active?
    }
    input.signal_connect('changed'){ |widget|
      UserConfig[key] = widget.text
    }
    self.group(title, default, Gtk::HBox.new(false, 0).add(custom).add(input))
  end

  def self.input(key, label, visibility=true, &callback)
    if key.respond_to?(:call)
      proc = key
    else
      proc = lambda{ |new|
        if new
          UserConfig[key] = new
        else
          UserConfig[key].to_s end } end
    container = Gtk::HBox.new(false, 0)
    input = Gtk::Entry.new
    input.text = proc.call(nil)
    input.visibility = visibility
    container.pack_start(Gtk::Label.new(label), false, true, 0) if label
    container.pack_start(Gtk::Alignment.new(1.0, 0.5, 0, 0).add(input), true, true, 0)
    input.signal_connect('changed'){ |widget|
      proc.call(widget.text) }
    callback.call(container, input) if block_given?
    return container
  end

  def self.keyconfig(key, title)
    if key.respond_to?(:call)
      proc = key
    else
      proc = lambda{ |new|
        if new
          UserConfig[key] = new
        else
          UserConfig[key].to_s end } end
    keyconfig = Gtk::KeyConfig.new(title, proc.call(nil))
    container = Gtk::HBox.new(false, 0)
    container.pack_start(Gtk::Label.new(title), false, true, 0)
    container.closeup(keyconfig.right)
    keyconfig.change_hook = proc
    return container
  end

  def self.group(title, *children)
    group = Gtk::Frame.new.set_border_width(8)
    if(title.is_a?(Gtk::Widget))
      group.set_label_widget(title)
    else
      group.set_label(title) end
    box = Gtk::VBox.new(false, 0).set_border_width(4)
    group.add(box)
    children.each{ |w|
      box.pack_start(w, false)
    }
    group
  end

  def self.expander(title, expanded, *children)
    group = Gtk::Expander.new(title).set_border_width(8)
    group.expanded = expanded
    box = Gtk::VBox.new(false, 0).set_border_width(4)
    group.add(box)
    children.each{ |w|
      box.pack_start(w, false)
    }
    group
  end

  def self.fileselect(key, label, current=Dir.pwd)
    container = input = nil
    self.input(key, label){ |c, i|
      container = c
      input = i }
    button = Gtk::Button.new('参照')
    container.pack_start(button, false)
    button.signal_connect('clicked'){ |widget|
      dialog = Gtk::FileChooserDialog.new("Open File",
                                          widget.get_ancestor(Gtk::Window),
                                          Gtk::FileChooser::ACTION_OPEN,
                                          nil,
                                          [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
                                          [Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT])
      dialog.current_folder = File.expand_path(current)
      if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
        UserConfig[key] = dialog.filename
        input.text = dialog.filename
      end
      dialog.destroy
    }
    container
  end

  def self._colorselect(key, label)
    color = UserConfig[key]
    button = Gtk::ColorButton.new((color and Gdk::Color.new(*color)))
    button.title = label
    button.signal_connect('color-set'){ |w|
      UserConfig[key] = w.color.to_a }
    button end

  def self._fontselect(key, label)
    button = Gtk::FontButton.new(UserConfig[key])
    button.title = label
    button.signal_connect('font-set'){ |w|
      UserConfig[key] = w.font_name }
    button end

  def self.fontselect(key, label)
    Gtk::HBox.new(false, 0).add(Gtk::Label.new(label).left).closeup(_fontselect(key, label))
  end

  def self.colorselect(key, label)
    Gtk::HBox.new(false, 0).add(Gtk::Label.new(label).left).closeup(_colorselect(key, label))
  end

  def self.fontcolorselect(font, color, label)
    self.fontselect(font, label).closeup(_colorselect(color, label))
  end

  def self.accountdialog_button(label, kuser, lvuser,  kpasswd, lvpasswd, &validator)
    btn = Gtk::Button.new(label)
    btn.signal_connect('clicked'){
      self.account_dialog(label, kuser, lvuser,  kpasswd, lvpasswd, &validator) }
    btn
  end

  def self.account_dialog_inner(kuser, lvuser,  kpasswd, lvpasswd, cancel=true)
    def entrybox(label, visibility=true, default="")
      container = Gtk::HBox.new(false, 0)
      input = Gtk::Entry.new
      input.text = default
      input.visibility = visibility
      container.pack_start(Gtk::Label.new(label), false, true, 0)
      container.pack_start(Gtk::Alignment.new(1.0, 0.5, 0, 0).add(input), true, true, 0)
      return container, input
    end
    box = Gtk::VBox.new(false, 8)
    user, user_input = entrybox(lvuser, true, (UserConfig[kuser] or ""))
    pass, pass_input = entrybox(lvpasswd, false)
    return box.closeup(user).closeup(pass), user_input, pass_input
  end

  def self.adi(symbol, label)
    input(lambda{ |new| UserConfig[symbol] }, label){ |c, i| yield(i) } end

  def self.account_dialog(label, kuser, lvuser,  kpasswd, lvpasswd, cancel=true, &validator)
    alert_thread = if(Thread.main != Thread.current) then Thread.current end
    dialog = Gtk::Dialog.new(label)
    dialog.window_position = Gtk::Window::POS_CENTER
    iuser = ipass = nil
    container = Gtk::VBox.new(false, 8).
      closeup(adi(kuser, lvuser){ |i| iuser = i }).
      closeup(adi(kpasswd, lvpasswd){ |i| ipass = i })
    dialog.vbox.pack_start(container, true, true, 30)
    dialog.add_button(Gtk::Stock::OK, Gtk::Dialog::RESPONSE_OK)
    dialog.add_button(Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL) if cancel
    dialog.default_response = Gtk::Dialog::RESPONSE_OK
    quit = lambda{
      dialog.hide_all.destroy
      Gtk.main_iteration_do(false)
      Gtk::Window.toplevels.first.show
      if alert_thread
        alert_thread.run
      else
        Gtk.main_quit
      end }
    dialog.signal_connect("response"){ |widget, response|
      if response == Gtk::Dialog::RESPONSE_OK
        if validator.call(iuser.text, ipass.text)
          UserConfig[kuser] = iuser.text
          UserConfig[kpasswd] = ipass.text
          quit.call
        else
          alert("#{lvuser}か#{lvpasswd}が違います")
        end
      elsif (cancel and response == Gtk::Dialog::RESPONSE_CANCEL) or
          response == Gtk::Dialog::RESPONSE_DELETE_EVENT
        quit.call
      end }
    dialog.signal_connect("destroy") {
      false
    }
    container.show
    dialog.show_all
    Gtk::Window.toplevels.first.hide
    if(alert_thread)
      Thread.stop
    else
      Gtk::main
    end
  end

  def self.alert(message)
    dialog = Gtk::MessageDialog.new(nil,
                                    Gtk::Dialog::DESTROY_WITH_PARENT,
                                    Gtk::MessageDialog::QUESTION,
                                    Gtk::MessageDialog::BUTTONS_CLOSE,
                                    message)
    dialog.run
    dialog.destroy
  end

  def self.dialog_button(label, callback = Proc.new)
    btn = Gtk::Button.new(label)
    btn.signal_connect('clicked'){
      params = callback.call
      self.dialog(label, params[:container], &params[:success]) }
    btn
  end

  def self.scrolled_dialog(title, container, parent=nil, expand=true, &block)
    dialog(title,
           Gtk::ScrolledWindow.new.
           set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC).
           add_with_viewport(container),
           parent, expand, &block) end

  def self.dialog(title, container, parent=nil, expand=true, &block)
    parent_window = parent and parent.toplevel.toplevel? and parent.toplevel
    result = nil
    dialog = Gtk::Dialog.new("#{title} - " + Environment::NAME)
    dialog.set_size_request(640, 480)
    dialog.window_position = Gtk::Window::POS_CENTER
    dialog.vbox.pack_start(container, expand, true, 30)
    dialog.add_button(Gtk::Stock::OK, Gtk::Dialog::RESPONSE_OK)
    dialog.add_button(Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL) if block_given?
    dialog.signal_connect('response'){ |widget, response|
      if block and response == Gtk::Dialog::RESPONSE_OK
        begin
          result = block.call(*[response, dialog][0,block.arity])
        rescue Mtk::ValidateError => e
          dialog.sensitive = false
          alert = Gtk::Dialog.new("エラー - " + Environment::NAME)
          alert.set_size_request(420, 90)
          alert.window_position = Gtk::Window::POS_CENTER
          alert.vbox.add(Gtk::Label.new(e.to_s))
          alert.add_button(Gtk::Stock::OK, Gtk::Dialog::RESPONSE_OK)
          alert.show_all
          alert.signal_connect('response'){
            dialog.sensitive = true
            alert.hide_all.destroy }
          next
        end
      end
      parent_window.sensitive = true if parent_window
      dialog.hide_all.destroy
      Gtk::main_quit
    }
    parent_window.sensitive = false if parent_window
    dialog.show_all
    Gtk::main
    result end

  class Mtk::ValidateError < StandardError;  end

end