tkri 0.9.3 → 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/tkri.rb +178 -67
  2. metadata +2 -2
@@ -44,7 +44,7 @@ module Tkri
44
44
  'mswin' => 'qri.bat -f ansi "%s" 2>&1',
45
45
  }
46
46
 
47
- TAGS = {
47
+ VISUALS = {
48
48
  # The '__base__' attibutes are applied for every textfield and textarea.
49
49
  # Set the background color and font to whatever you like.
50
50
  #
@@ -53,14 +53,16 @@ module Tkri
53
53
  # name (i.e., one of: 'courier', 'times', 'helvetica') at the end to serve
54
54
  # as a fallback.
55
55
  '__base__' => { :background => '#ffeeff', :font => { :family => ['Bitstream Vera Sans Mono', 'Menlo', 'Monaco', 'Courier'], :size => 10 } },
56
- 'bold' => { :foreground => 'blue' },
57
- 'italic' => { :foreground => '#6b8e23' }, # greenish
58
- 'code' => { :foreground => '#1874cd' }, # blueish
59
- 'header2' => { :background => '#ffe4b5', :font => { :family => ['Geneva', 'Arial', 'Helvetica'], :size => 12 } },
60
- 'header3' => { :background => '#ffe4b5', :font => { :family => ['Geneva', 'Arial', 'Helvetica'], :size => 14 } },
61
- 'keyword' => { :foreground => 'red' },
62
- 'search' => { :background => 'yellow' },
63
- 'hidden' => { :elide => true },
56
+ 'bold' => { :foreground => 'blue', :is_tag => true },
57
+ 'italic' => { :foreground => '#6b8e23', :is_tag => true }, # greenish
58
+ 'code' => { :foreground => '#1874cd', :is_tag => true }, # blueish
59
+ 'header2' => { :background => '#ffe4b5', :font => { :family => ['Geneva', 'Arial', 'Helvetica'], :size => 12 }, :is_tag => true },
60
+ 'header3' => { :background => '#ffe4b5', :font => { :family => ['Geneva', 'Arial', 'Helvetica'], :size => 14 }, :is_tag => true },
61
+ 'keyword' => { :foreground => 'red', :is_tag => true },
62
+ 'search' => { :background => 'yellow', :is_tag => true },
63
+ 'hidden' => { :elide => true, :is_tag => true},
64
+ 'tab_button' => { :padx => 10, :font => { :family => ['Bitstream Vera Sans Mono', 'Menlo', 'Monaco', 'Courier'], :size => 10 } },
65
+ 'go_button' => { :padx => 20 },
64
66
  }
65
67
 
66
68
  BINDINGS = {
@@ -113,6 +115,8 @@ module Tkri
113
115
  # History.
114
116
  'b1013' => { :key => 'ButtonRelease-3', :source => 'info', :command => 'interactive_history_back' },
115
117
  'b1014' => { :key => 'Key-BackSpace', :source => 'info', :command => 'interactive_history_back', :cancel_default => true },
118
+ 'b1014b' => { :key => 'Alt-Key-Left', :source => 'root', :command => 'interactive_history_back', :cancel_default => true },
119
+ 'b1014c' => { :key => 'Alt-Key-Right', :source => 'root', :command => 'interactive_history_forward', :cancel_default => true },
116
120
 
117
121
  # Tk doesn't support read-only rext widgets. So for every "ascii" global binding we also
118
122
  # need to duplicate it on the 'info' widget, :cancel_default'ing it.
@@ -142,7 +146,7 @@ module Tkri
142
146
  f.puts "# You may erase any setting in this file for which you want to use"
143
147
  f.puts "# the default value."
144
148
  f.puts "#"
145
- f.puts({ 'command' => COMMAND, 'tags' => TAGS, 'bindings' => BINDINGS }.to_yaml)
149
+ f.puts({ 'command' => COMMAND, 'visuals' => VISUALS, 'bindings' => BINDINGS }.to_yaml)
146
150
  end
147
151
  end
148
152
 
@@ -154,7 +158,7 @@ module Tkri
154
158
 
155
159
  module Settings
156
160
  COMMAND = DefaultSettings::COMMAND.dup
157
- TAGS = DefaultSettings::TAGS.dup
161
+ VISUALS = DefaultSettings::VISUALS.dup
158
162
  BINDINGS = DefaultSettings::BINDINGS
159
163
 
160
164
  # Load the settings from the 'rc' file. We merge them into the existing settings.
@@ -164,33 +168,84 @@ module Tkri
164
168
  settings = YAML.load_file(Tkri.get_rc_file_path)
165
169
  if settings.instance_of? Hash
166
170
  COMMAND.merge!(settings['command']) if settings['command']
167
- TAGS.merge!(settings['tags']) if settings['tags']
171
+ VISUALS.merge!(settings['visuals']) if settings['visuals']
168
172
  BINDINGS.merge!(settings['bindings']) if settings['bindings']
169
173
  end
170
174
  end
171
175
  end
176
+
177
+ # get_configuration() converts any of the VISUAL hashes, above, to a hash suitable
178
+ # for use in Tk. Corrently, it only converts the :font attribute to a TkFont instance.
179
+ def self.get_configuration(name)
180
+ h = VISUALS[name].dup
181
+ h.delete(:is_tag)
182
+ if h[:font].instance_of? Hash
183
+ h[:font] = h[:font].dup
184
+ if h[:font][:family]
185
+ availables = TkFont.families.map { |s| s.downcase }
186
+ desired = Array(h[:font][:family]).map { |s| s.downcase }
187
+ # Select the first family available on this system.
188
+ h[:font][:family] = (desired & availables).first || 'courier'
189
+ end
190
+ h[:font] = TkFont.new(h[:font])
191
+ end
192
+ return h
193
+ end
172
194
  end
173
195
 
174
196
  HistoryEntry = Struct.new(:topic, :cursor, :yview)
175
197
 
176
- # hash_to_configuration() converts any of the TAG hashes, above, to a hash suitable
177
- # for use in Tk. Corrently, it only converts the :font attribute to a TkFont instance.
178
- def self.hash_to_configuration(hash)
179
- ret = hash.dup
180
- if ret[:font].instance_of? Hash
181
- ret[:font] = ret[:font].dup
182
- if ret[:font][:family]
183
- availables = TkFont.families.map { |s| s.downcase }
184
- desired = Array(ret[:font][:family]).map { |s| s.downcase }
185
- # Select the first family available on this system.
186
- ret[:font][:family] = (desired & availables).first || 'courier'
187
- end
188
- ret[:font] = TkFont.new(ret[:font])
189
- end
190
- return ret
198
+ class History
199
+ def initialize
200
+ @arr = []
201
+ @current = -1
202
+ end
203
+
204
+ def size
205
+ @arr.size
206
+ end
207
+
208
+ def current
209
+ if @current >= 0
210
+ @arr[@current]
211
+ else
212
+ nil
213
+ end
214
+ end
215
+
216
+ def back
217
+ if @current > 0
218
+ @current -= 1
219
+ end
220
+ current
221
+ end
222
+
223
+ def foreward
224
+ if @current < @arr.size - 1
225
+ @current += 1
226
+ end
227
+ end
228
+
229
+ def add(entry)
230
+ @current += 1
231
+ @arr[@current] = entry
232
+ # Adding an entry removes all entries in the "future".
233
+ @arr.slice!(@current + 1, @arr.size)
234
+ end
235
+
236
+ def at_beginning
237
+ @current <= 0
238
+ end
239
+
240
+ def at_end
241
+ @current == @arr.size - 1;
242
+ end
191
243
  end
192
244
 
193
245
  # Attachs Settings::BINDINGS to a certain widget.
246
+ #
247
+ # Ideally this should be a method of TkWidget, but for some reason widgets don't
248
+ # seem to inherit methods I define on TkWidget.
194
249
  def self.attach_bindings(widget, widget_id_string)
195
250
  Tkri::Settings::BINDINGS.each_pair { |ignored_key, b|
196
251
  if (b[:source] == widget_id_string)
@@ -229,13 +284,14 @@ class Tab < TkFrame
229
284
  addressbar = TkFrame.new(self) { |ab|
230
285
  pack :side => 'top', :fill => 'x'
231
286
  TkButton.new(ab) {
287
+ configure Settings::get_configuration('go_button')
232
288
  text 'Go'
233
289
  command { app.go }
234
290
  pack :side => 'right'
235
291
  }
236
292
  }
237
293
  @address = TkEntry.new(addressbar) {
238
- configure Tkri::hash_to_configuration(Settings::TAGS['__base__'])
294
+ configure Settings::get_configuration('__base__')
239
295
  configure :width => 30
240
296
  pack :side => 'left', :expand => true, :fill => 'both'
241
297
  }
@@ -245,7 +301,7 @@ class Tab < TkFrame
245
301
  #
246
302
  _frame = self
247
303
  @info = TkText.new(self) { |t|
248
- configure Tkri::hash_to_configuration(Settings::TAGS['__base__'])
304
+ configure Settings::get_configuration('__base__')
249
305
  pack :side => 'left', :fill => 'both', :expand => true
250
306
  TkScrollbar.new(_frame) { |s|
251
307
  pack :side => 'right', :fill => 'y'
@@ -254,18 +310,16 @@ class Tab < TkFrame
254
310
  }
255
311
  }
256
312
 
257
- Settings::TAGS.each do |name, hash|
258
- @info.tag_configure(name, Tkri::hash_to_configuration(hash))
313
+ Settings::VISUALS.each do |name, hash|
314
+ if hash[:is_tag]
315
+ @info.tag_configure(name, Settings::get_configuration(name))
316
+ end
259
317
  end
260
318
 
261
319
  Tkri.attach_bindings @address, 'addressbox'
262
320
  Tkri.attach_bindings @info, 'info'
263
321
 
264
- @history = []
265
- end
266
-
267
- def interactive_history_back e
268
- back
322
+ @history = History.new
269
323
  end
270
324
 
271
325
  def interactive_goto_topic_in_addressbox e
@@ -288,7 +342,7 @@ class Tab < TkFrame
288
342
  end
289
343
  end
290
344
 
291
- # It seeks RubyTk doesn't support the the getSelected method for Text widgets.
345
+ # It seems RubyTk doesn't support the the getSelected method for Text widgets.
292
346
  # So here's a method of our own to get the selection.
293
347
  def get_selection
294
348
  begin
@@ -510,22 +564,49 @@ class Tab < TkFrame
510
564
  end
511
565
 
512
566
  # Navigates to some topic.
513
- def go(topic=nil, skip_history=false)
567
+ def go(topic=nil)
514
568
  topic = (topic || @address.get).strip
515
569
  return if topic.empty?
516
- if @topic and not skip_history
517
- # Push current topic into history.
518
- @history << HistoryEntry.new(@topic, @info.index('insert'), @info.yview[0])
519
- end
520
570
  @topic = fixup_topic(topic)
521
- @app.status = 'Loading "%s"...' % @topic
571
+ # First, save the cursor position in the current history entry.
572
+ store_in_history
573
+ # Next, add a new entry to the history.
574
+ @history.add HistoryEntry.new(@topic, nil, nil)
575
+ # Finally, load the topic.
576
+ _load_topic @topic
577
+ end
578
+
579
+ # Call this method before switching to another topic. This
580
+ # method saves the cursor position in the history.
581
+ def store_in_history
582
+ if current = @history.current
583
+ current.cursor = @info.index('insert')
584
+ current.yview = @info.yview[0]
585
+ end
586
+ end
587
+
588
+ # Call this method after moving fack and forth in the history.
589
+ # This method restores the topic and cursor position recorded
590
+ # in the current history entry.
591
+ def restore_from_history
592
+ if current = @history.current
593
+ @topic = current.topic
594
+ _load_topic(topic) do
595
+ @info.yview_moveto current.yview
596
+ set_cursor current.cursor
597
+ end
598
+ end
599
+ end
600
+
601
+ def _load_topic(topic)
602
+ @app.status = 'Loading "%s"...' % topic
522
603
  @address.delete('0', 'end')
523
- @address.insert('end', @topic)
604
+ @address.insert('end', topic)
524
605
  focus_address
525
606
  # We need to give our GUI a chance to redraw itself, so we run the
526
607
  # time-consuming 'ri' command "in the next go".
527
608
  TkAfter.new 100, 1 do
528
- ri = @app.fetch_ri(@topic)
609
+ ri = @app.fetch_ri(topic)
529
610
  set_ansi_text(ri)
530
611
  @app.refresh_tabsbar
531
612
  @app.status = ''
@@ -534,14 +615,22 @@ class Tab < TkFrame
534
615
  yield if block_given?
535
616
  end.start
536
617
  end
618
+
619
+ # Navigate to the previous topic in history.
620
+ def interactive_history_back e
621
+ if not @history.at_beginning
622
+ store_in_history
623
+ @history.back
624
+ restore_from_history
625
+ end
626
+ end
537
627
 
538
- # Navigate to the previous topic viewed.
539
- def back
540
- if (entry = @history.pop)
541
- go(entry.topic, true) do
542
- @info.yview_moveto entry.yview
543
- set_cursor entry.cursor
544
- end
628
+ # Navigate to the next topic in history.
629
+ def interactive_history_forward e
630
+ if not @history.at_end
631
+ store_in_history
632
+ @history.foreward
633
+ restore_from_history
545
634
  end
546
635
  end
547
636
 
@@ -586,11 +675,13 @@ class Tabsbar < TkFrame
586
675
  @buttons = []
587
676
  @tabs.each_with_index do |tab, i|
588
677
  b = TkButton.new(self, :text => (tab.topic || '<new>')).pack :side => 'left'
678
+ b.configure Settings::get_configuration('tab_button')
589
679
  b.command { set_current_tab_by_index i }
590
680
  Tkri.attach_bindings b, 'tabbutton'
591
681
  @buttons << b
592
682
  end
593
- plus = TkButton.new(self, :text => '+').pack :side => 'left'
683
+ plus = TkButton.new(self, :text => '+').pack :side => 'left', :padx => 10
684
+ plus.configure Settings::get_configuration('tab_button')
594
685
  plus.command { @tabs.new_tab }
595
686
  @buttons << plus
596
687
  set_current_tab_by_index @tabs.current_tab_as_index
@@ -621,7 +712,6 @@ class Tabs < TkFrame
621
712
  tab.focus_address
622
713
  @tabs << tab
623
714
  set_current_tab_by_index(@tabs.size - 1, true)
624
- # @app.refresh_tabsbar
625
715
  end
626
716
 
627
717
  def interactive_new_tab e
@@ -844,10 +934,22 @@ class App
844
934
  if text == "nil\n"
845
935
  text = 'Topic "%s" not found.' % topic
846
936
  end
937
+ text = _post_process_text(text)
847
938
  @ri_cache[topic] = text
848
939
  @cached_topics << topic
849
940
  end
850
941
 
942
+ # Remove the oldest topic from the cache
943
+ if @cached_topics.length > 20
944
+ @ri_cache.delete @cached_topics.shift
945
+ end
946
+
947
+ return text
948
+ end
949
+
950
+ # Enhance the ri text a bit.
951
+ def _post_process_text(text)
952
+
851
953
  # Make the "Multiple choices" output easier to read.
852
954
  if text.match /Multiple choices/
853
955
  text.gsub! /,\s+/, "\n"
@@ -855,18 +957,27 @@ class App
855
957
  text.gsub! /\n/, "\n "
856
958
  end
857
959
 
858
- # Remove the oldest topic from the cache
859
- if @cached_topics.length > 20
860
- @ri_cache.delete @cached_topics.shift
861
- end
862
-
960
+ # Highlight the names of included modules.
961
+ bold_on = "\x1b[1m"
962
+ bold_off = "\x1b[0m"
963
+ parts = text.split /([\r\n]\S*Includes:.*?[\r\n][\r\n])/m
964
+ parts.map! { |s|
965
+ if s =~ /[\r\n]\S*Includes:/
966
+ # Modules have parentheses following them.
967
+ s.gsub! /(\S+)\(/, bold_on + '\1' + bold_off + '('
968
+ else
969
+ s
970
+ end
971
+ }
972
+ text = parts.join
973
+
863
974
  return text
864
975
  end
865
976
 
866
977
  def helpbox(title, text)
867
978
  w = TkToplevel.new(:title => title)
868
979
  t = TkText.new(w, :height => text.count("\n"), :width => 80).pack.insert('1.0', text)
869
- t.configure Tkri::hash_to_configuration(Settings::TAGS['__base__'])
980
+ t.configure Settings::get_configuration('__base__')
870
981
  TkButton.new(w, :text => 'Close', :command => proc { w.destroy }).pack
871
982
  end
872
983
 
@@ -900,16 +1011,18 @@ Ctrl + Left mouse button
900
1011
  Navigate to the topic selected (marked) with the mouse.
901
1012
  Middle mouse button
902
1013
  Navigate to the topic under the cursor. Opens in a new tab.
903
- Right mouse button
904
- Move back in the history.
1014
+ Right mouse button, Backspace, Alt+Left
1015
+ Move back in the history.
1016
+ Alt+Right
1017
+ Move foreward in the history.
905
1018
  Ctrl+W. Or middle mouse button, on a tab button
906
1019
  Close the tab (unless this is the only tab).
907
1020
  Ctrl+L
908
1021
  Move the keyboard focus to the "address" box, where you can type a topic.
909
1022
  Ctrl+T
910
1023
  New tab.
911
- Alt-1 .. Alt-9, Ctrl-PgUp, Ctrl-PgDn
912
- Swith to a certain, or to the next/previous one.
1024
+ Alt+1 .. Alt+9, Ctrl+PgUp, Ctrl+PgDn
1025
+ Swith to a certain tab, or to the next/previous one.
913
1026
  u
914
1027
  Goes "up" one level. That is, if you're browsing Module::Class#method,
915
1028
  you'll be directed to Module::Class. Press 'u' again for Module.
@@ -983,8 +1096,6 @@ Here's a list of known issues / bugs:
983
1096
 
984
1097
  ON THE WINDOWS PLATFORM:
985
1098
 
986
- * Tkri looks ugly.
987
-
988
1099
  * The mouse wheel works only if the keyboard focus is in the
989
1100
  textarea. That's unfortunate. It's a Tk issue, not Tkri's.
990
1101
 
@@ -994,7 +1105,7 @@ ON THE WINDOWS PLATFORM:
994
1105
 
995
1106
  ALL PLATFORMS:
996
1107
 
997
- * There's a 'backward' command, but no 'forward' command.
1108
+ * No known issues.
998
1109
  EOS
999
1110
  end
1000
1111
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tkri
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.3
4
+ version: 0.9.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mooffie
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-29 00:00:00 +02:00
12
+ date: 2009-10-30 00:00:00 +02:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency