File: main.rb

package info (click to toggle)
ruby-gnome2 0.15.0-1.1etch1
  • links: PTS
  • area: main
  • in suites: etch
  • size: 7,704 kB
  • ctags: 8,558
  • sloc: ansic: 69,912; ruby: 19,511; makefile: 97; xml: 35; sql: 13
file content (376 lines) | stat: -rwxr-xr-x 8,969 bytes parent folder | download | duplicates (2)
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
374
375
376
#! /usr/bin/env ruby
=begin
  main.rb - Main class of gtk-demo.

  Copyright (c) 2003-2006 Ruby-GNOME2 Project Team
  This program is licenced under the same licence as Ruby-GNOME2.

  $Id: main.rb,v 1.19 2006/06/17 13:18:12 mutoh Exp $
=end

require 'gtk2'

module Demo

  class Main < Gtk::Window
    TITLE_COLUMN, FILENAME_COLUMN, CLASS_COLUMN, ITALIC_COLUMN = 0, 1, 2, 3

    def initialize
      super()

      @current_file = nil

      @info_buffer = Gtk::TextBuffer.new
      @source_buffer = Gtk::TextBuffer.new

      set_title('Ruby/GTK+ Code Demos')
      signal_connect('destroy') do
	Gtk.main_quit
      end

      signal_connect("key_press_event") do |widget, event|
        if event.state.control_mask? and event.keyval == Gdk::Keyval::GDK_q
          destroy
          true
        else
          false
        end
      end

      hbox = Gtk::HBox.new
      add(hbox)

      tree = create_tree
      scrolled_window = Gtk::ScrolledWindow.new
      scrolled_window.add(tree)
      hbox.pack_start(scrolled_window, false, false, 0)

      notebook = Gtk::Notebook.new
      hbox.pack_start(notebook, true, true, 0)

      notebook.append_page(create_text(@info_buffer, false),
			   Gtk::Label.new('_Info', true))

      notebook.append_page(create_text(@source_buffer, true),
			   Gtk::Label.new('_Source', true))

      @info_buffer.create_tag('title',
                             {'font' => 'Sans 18'})

      @source_buffer.create_tag('comment',
                               {'foreground' => 'red'})
      @source_buffer.create_tag('const',
                               {'foreground' => 'ForestGreen'})
      @source_buffer.create_tag('string',
                               {
				  'foreground' => 'RosyBrown',
                                  'weight' => Pango::FontDescription::WEIGHT_BOLD
				})
      @source_buffer.create_tag('reserved',
                               {'foreground' => 'purple'})
    end

    def script_info(path)
      title = nil
      klass = nil
      depend = nil

      file = File.open(path)
      file.each do |ln|
        if not title and ln =~ /^=\s+(.*)$/
          title = $1
          if title =~ /^(.*)\((.+?)\)$/
            title = $1
            depend = $2
          end
        elsif not klass and ln =~ /\s*class\s+([A-Z][A-Za-z0-9_]*)/
          klass = $1
        end

        if title and klass
          break
        end
      end

      return title, klass.intern, depend
    end

    def generate_index
      # Target scripts
      scripts = Dir.glob('*.rb') - %w(common.rb main.rb)

      # Generate index tree
      children = {}
      index = []

      scripts.each do |fn|
        title, klass, depend = script_info(fn)

        if depend and not Gtk.const_defined?(depend)
          next
        end

        if title =~ %r{^(.+?)/(.+)$}
          parent = $1
          child = $2

          unless children[parent]
            children[parent] = []
            index += [[parent, nil, nil, []]]
          end

          children[parent] += [[child, fn, klass]]
        else
          index += [[title, fn, klass]]
        end
      end

      # Sort children
      children.each_key do |parent|
        children[parent].sort! do |a, b|
          a[0] <=> b[0]
        end
      end

      # Expand children
      index.collect! do |row|
        if row[3]
          row[3] = children[row[0]]
        end

        row
      end

      index.sort! do |a, b|
        a[0] <=> b[0]
      end

      index
    end

    def create_tree
      model = Gtk::TreeStore.new(String, String, String, TrueClass)

      tree_view = Gtk::TreeView.new
      tree_view.set_model(model)
      selection = tree_view.selection

      selection.set_mode(Gtk::SELECTION_BROWSE)
      tree_view.set_size_request(200, -1)

      append_children(model, generate_index)

      cell = Gtk::CellRendererText.new
      cell.style = Pango::FontDescription::STYLE_ITALIC
      column = Gtk::TreeViewColumn.new("Widget (double click for demo)", cell,
                                      {
                                         'text' => TITLE_COLUMN,
                                         'style_set' => ITALIC_COLUMN,
                                       })

      tree_view.append_column(column)

      selection.signal_connect('changed') do |selection|
	iter = selection.selected
	load_file(iter.get_value(FILENAME_COLUMN)) if iter
      end
      tree_view.signal_connect('row_activated') do |tree_view, path, column|
        row_activated_cb(tree_view.model, path)
      end

      tree_view.expand_all
      return tree_view
    end

    def append_children(model, source, parent = nil)
      source.each do |title, filename, klass, children|
	iter = model.append(parent)

	[title, filename, klass].each_with_index do |value, i|
	  if value
	    iter.set_value(i, value)
	  end
	end
        iter.set_value(ITALIC_COLUMN, false)

        if children
          append_children(model, children, iter)
        end
      end
    end

    def row_activated_cb(model, path)
      iter = model.get_iter(path)
      iter.set_value(ITALIC_COLUMN, true)

      lib = iter.get_value(FILENAME_COLUMN)
      if lib
        require(lib)
 
        klass = Demo.const_get(iter.get_value(CLASS_COLUMN))
        window = klass.new
        window.signal_connect('destroy') do
          iter.set_value(ITALIC_COLUMN, false)
        end
        window.show_all
	@window = window
      end
    end

    def create_text(buffer, is_source)
      scrolled_window = Gtk::ScrolledWindow.new
      scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC,
				 Gtk::POLICY_AUTOMATIC)
      scrolled_window.set_shadow_type(Gtk::SHADOW_IN)

      text_view = Gtk::TextView.new

      text_view.set_buffer(buffer)
      text_view.set_editable(false)
      text_view.set_cursor_visible(false)

      scrolled_window.add(text_view)

      if is_source
	font_desc = Pango::FontDescription.new('Monospace 12')
	text_view.modify_font(font_desc)

	text_view.set_wrap_mode(Gtk::TextTag::WRAP_NONE)
      else
	text_view.set_wrap_mode(Gtk::TextTag::WRAP_WORD)
	text_view.set_pixels_above_lines(2)
	text_view.set_pixels_below_lines(2)
      end

      return scrolled_window
    end

    def fontify(start_iter = @source_buffer.start_iter,
		end_iter = @source_buffer.end_iter)
      str = @source_buffer.get_text(start_iter, end_iter, true)

      tokenizer = RubyTokonizer.new
      tokenizer.tokenize(str, start_iter.offset) do |tag, start, last|
	@source_buffer.apply_tag(tag.to_s,
				 @source_buffer.get_iter_at_offset(start),
				 @source_buffer.get_iter_at_offset(last))
      end
    end


    def load_file(filename)
      if filename == @current_file
	return
      end

      @info_buffer.delete(*@info_buffer.bounds)

      @source_buffer.delete(*@source_buffer.bounds)

      file = begin
	       File.open(filename)
	     rescue
	       $stderr.puts "Cannot open: #{$!}" if $DEBUG
	       return
	     end
      start = @info_buffer.get_iter_at_offset(0)
      state = :before_header

      file.each do |line|
	case state
	when :before_header
	  if line =~ /^=begin$/
	    state = :in_header
	  end
	when :in_header
	  if line =~ /^=end$/
	    state = :body
	    start = @source_buffer.get_iter_at_offset(0)
	  elsif line =~ /^=\s+(.*)$/
	    title = $1
            title.gsub!(/\s*\(.*\)$/, '') # Delete depend field

	    last = start

	    @info_buffer.insert(last, title)
	    start = last.clone

	    start.backward_chars(title.length)
	    @info_buffer.apply_tag('title', start, last)

	    start = last
	  else
	    @info_buffer.insert(start, line)
	  end
	when :body  # Reading program body
	  @source_buffer.insert(start, line)
	end
      end

      fontify

      @current_file = filename
    end
  end

  class RubyTokonizer
    RESERVED_WORDS = %w(begin end module class def if then else while unless do case when require yield)
    RESERVED_WORDS_PATTERN = Regexp.compile(/(^|\s+)(#{RESERVED_WORDS.collect do |pat| Regexp.quote(pat) end.join('|')})(\s+|$)/)

    def tokenize(str, index = 0)
      until str.empty?
	tag = nil

	case str
	when /".+?"/, /'.+?'/
	  tag = :string
	when /#.*$/
	  tag = :comment
	when RESERVED_WORDS_PATTERN
	  tag = :reserved
	when /[A-Z][A-Za-z0-9_]+/
	  tag = :const
	end

	if tag
	  tokenize($~.pre_match, index) do |*args|
            yield(*args)
	  end
	  yield(tag, index + $~.begin(0), index + $~.end(0))
	  index += (str.length - $~.post_match.length)
	  str = $~.post_match
	else
	  index += str.length
	  str = ''
	end
      end
    end
  end
end


target = ARGV.shift
if target
  Demo::INDEX.each do |title, filename, klass_symbol, children|
    if target == filename or target == klass_symbol.id2name
      require filename

      window = Demo.const_get(klass_symbol).new
      window.show_all

      class << window
	def quit
	  Gtk.main_quit
	end
      end

      break
    end
  end
else
  main = Demo::Main.new
  main.set_default_size(600, 400)
  main.show_all
end

Gtk.main