transmission-rss 0.0.9 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -43,9 +43,9 @@ and coult be omitted). The default +log_target+ is STDERR.
43
43
  - http://example.com/feed1
44
44
  - http://example.com/feed2
45
45
 
46
- rss_check_interval: 600
46
+ update_interval: 600
47
47
 
48
- paused: false
48
+ start_paused: false
49
49
 
50
50
  server:
51
51
  host: localhost
@@ -10,6 +10,9 @@ include TransmissionRSS
10
10
  # Default config file path.
11
11
  configFile = '/etc/transmission-rss.conf'
12
12
 
13
+ # Do not edit config file in Gtk GUI by default.
14
+ editConfig = false
15
+
13
16
  # Do not fork by default.
14
17
  dofork = false
15
18
 
@@ -22,6 +25,7 @@ def usageMessage( configFile )
22
25
  Adds torrents from rss feeds to transmission web frontend.
23
26
 
24
27
  -c <file> Custom config file path. Default: #{configFile}
28
+ -e Edit config file with Gtk GUI.
25
29
  -f Fork into background after startup.
26
30
  -h This help.
27
31
  -v Verbose mode.
@@ -33,6 +37,7 @@ end
33
37
  # Define command-line options.
34
38
  options = GetoptLong.new(
35
39
  [ '-c', GetoptLong::REQUIRED_ARGUMENT ],
40
+ [ '-e', GetoptLong::NO_ARGUMENT ],
36
41
  [ '-f', GetoptLong::NO_ARGUMENT ],
37
42
  [ '-h', GetoptLong::NO_ARGUMENT ],
38
43
  [ '-v', GetoptLong::NO_ARGUMENT ]
@@ -43,6 +48,8 @@ options.each do |option, argument|
43
48
  case( option )
44
49
  when '-c'
45
50
  configFile = argument
51
+ when '-e'
52
+ editConfig = true
46
53
  when '-f'
47
54
  dofork = true
48
55
  when '-h'
@@ -59,8 +66,8 @@ config = TransmissionRSS::Config.instance
59
66
  # Default configuration.
60
67
  config.load( {
61
68
  'feeds' => [],
62
- 'rss_check_interval' => 600,
63
- 'paused' => false,
69
+ 'update_interval' => 600,
70
+ 'start_paused' => false,
64
71
  'server' => {
65
72
  'host' => 'localhost',
66
73
  'port' => 9091
@@ -75,9 +82,25 @@ log.target = config.log_target
75
82
  tLog = Thread.start do log.run end
76
83
 
77
84
  # Load config file (default or given by argument).
78
- config.load( configFile )
85
+ begin
86
+ config.load( configFile )
87
+ rescue Errno::ENOENT
88
+ log.add( configFile + ' not found' )
89
+ end
79
90
  log.add( config )
80
91
 
92
+ # Start GUI if config edit option is given.
93
+ if( editConfig )
94
+ require( 'transmission-rss/config-editor' )
95
+
96
+ Gtk.init
97
+
98
+ ConfigEditor.new( configFile, config )
99
+ Gtk.main
100
+
101
+ exit( 0 )
102
+ end
103
+
81
104
  # Connect reload of config file to SIGHUP.
82
105
  trap( 'HUP' ) do
83
106
  config.load( configFile )
@@ -96,7 +119,7 @@ aggregator.feeds.concat( config.feeds )
96
119
  # Callback for a new item on one of the feeds.
97
120
  aggregator.on_new_item do |torrentFile|
98
121
  Thread.start do
99
- client.addTorrent( torrentFile, config.paused )
122
+ client.addTorrent( torrentFile, config.start_paused )
100
123
  end
101
124
  end
102
125
 
@@ -104,12 +127,12 @@ end
104
127
  begin
105
128
  if( dofork )
106
129
  pid = fork do
107
- aggregator.run( config.rss_check_interval )
130
+ aggregator.run( config.update_interval )
108
131
  end
109
132
 
110
133
  puts( 'forked ' + pid.to_s )
111
134
  else
112
- aggregator.run( config.rss_check_interval )
135
+ aggregator.run( config.update_interval )
113
136
  end
114
137
  rescue Interrupt
115
138
  end
@@ -1,9 +1,19 @@
1
1
  $:.unshift( File.dirname( __FILE__ ) )
2
2
 
3
3
  module TransmissionRSS
4
- VERSION = '0.0.9'
4
+ VERSION = '0.1.0'
5
5
  end
6
6
 
7
- Dir.glob( $:.first + '/**/*.rb' ).each do |file|
8
- require( file )
7
+ dir = 'transmission-rss'
8
+
9
+ blacklist = %w(
10
+ config-editor
11
+ )
12
+
13
+ blacklist.map! do |name|
14
+ $:.first + '/' + dir + '/' + name + '.rb'
15
+ end
16
+
17
+ ( Dir.glob( $:.first + '/' + dir + '/*.rb' ) - blacklist ).each do |lib|
18
+ require( lib )
9
19
  end
@@ -0,0 +1,17 @@
1
+ $:.unshift( File.dirname( __FILE__ ) )
2
+
3
+ require( 'libglade2' )
4
+
5
+ dir = 'config-editor'
6
+
7
+ blacklist = %w(
8
+ listbox-original
9
+ )
10
+
11
+ blacklist.map! do |name|
12
+ $:.first + '/' + dir + '/' + name + '.rb'
13
+ end
14
+
15
+ ( Dir.glob( $:.first + '/' + dir + '/*.rb' ) - blacklist ).each do |lib|
16
+ require( lib )
17
+ end
@@ -0,0 +1,233 @@
1
+ # ListBox - A GTK Listbox
2
+ #--
3
+ ###################################################################################
4
+ #
5
+ # ListBox - A GTK Listbox
6
+ # Copyright (C) 2007 Lake Information Works
7
+ #
8
+ # This program is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation, either version 3 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
20
+ #
21
+ # Contact: alan.lake AT lakeinfoworks DOT com
22
+ # Lake Information Works
23
+ # Kalervonkatu 13 A 2
24
+ # 20400 Jyväskylä
25
+ # Finland
26
+ #
27
+ ###################################################################################
28
+ #++
29
+ =begin rdoc
30
+ Sample usage
31
+ Gtk.init
32
+ @main_window = Gtk::Window.new
33
+ @main_window.set_default_size 500,300
34
+ lb_left = ListBox.new
35
+ lb_left.header = 'Ad Groups'
36
+ lb_left.append('ulkomaanpuhelut')
37
+ lb_left.append('kaukopuhelut')
38
+ lb_left.append('suuntanumerot')
39
+ lb_right = ListBox.new
40
+ lb_right.header = 'State Nicknames'
41
+ lb_right.fill(['Empire','Golden','Silver','Sooner','Hoosier','Razorback','Buckeye','Lone Star'])
42
+ box = Gtk::HBox.new
43
+ @main_window.add(box)
44
+ box.pack_start(lb_left,true)
45
+ box.pack_start(lb_right,true)
46
+ @main_window.show_all
47
+ @main_window.signal_connect("destroy") { Gtk.main_quit }
48
+ lb_left.signal_connect('button_release_event') do |widget,event|
49
+ puts lb_left.button_release(widget,event)
50
+ end
51
+ lb_left.signal_connect('key-release-event') do |widget,event|
52
+ selection = lb_left.key_release(widget,event)
53
+ puts "#{selection}" if selection.class == String
54
+ end
55
+ # Note that no signal_connect code has been written for lb_right, so a selection cannot be made.
56
+ Gtk.main
57
+ =end
58
+ =begin rdoc
59
+ Purpose:
60
+ 1. Document how to create a GTK widget in Ruby.
61
+ 2. Create a general purpose listbox.
62
+ Credit:
63
+ Mathieu Blondel responded to my post on the Ruby Forum to teach me how to
64
+ create a valid widget. http://www.ruby-forum.com/topic/126951
65
+ Assumptions:
66
+ This document doesn't attempt to teach Ruby or GTK programming (or the
67
+ combination. Therefore, we assume that the reader knows how to create a
68
+ Ruby Class.
69
+ What's a widget?
70
+ http://www.webopedia.com/TERM/W/widget.html defines a widget. The listbox shown
71
+ here provides a way for the user to interface with an application by using the
72
+ mouse or keyboard to select an item in the list, whereupon the listbox
73
+ communicates the selection to the application.
74
+ What is provided here:
75
+ The following is in two parts: The class that creates the listbox widget and a
76
+ piece of code that illustrates the use of it. I will include comments in the
77
+ code that shows what needs to be done to make a class into a widget, but will
78
+ not completely comment the code. I am commenting the parts involved with
79
+ inheritance because it is the inheritance that makes ListBox a widget.
80
+
81
+ This listbox is extremely basic. The minimalistic design meets my need for a
82
+ listbox and makes it easier to highlight what it is that makes it a widget.
83
+ Furthermore, the fact that it is basic will allow me or another programmer to
84
+ easily modify it to include more sophisticated features.
85
+ =end
86
+ =begin rdoc
87
+ ListBox inherits Gtk::ScrolledWindow. ScrolledWindow was chosen because
88
+ (1) ScrolledWindow is a widget. This fact can be shown with interactive ruby
89
+ (irb) by doing (a) or (b):
90
+ (a)
91
+ [alan@erie ~]$ irb
92
+ irb(main):001:0> require 'gtk2'
93
+ => true
94
+ irb(main):002:0> sw = Gtk::ScrolledWindow.new
95
+ => #<Gtk::ScrolledWindow:0x2aaab008d088 ptr=0xac4bd0>
96
+ irb(main):003:0> sw.is_a? Gtk::Widget
97
+ => true
98
+ (b)
99
+ [alan@erie ~]$ irb
100
+ irb(main):001:0> require 'gtk2'
101
+ => true
102
+ irb(main):002:0> Gtk::ScrolledWindow.ancestors
103
+ => [Gtk::ScrolledWindow, Gtk::Bin, Gtk::Container, Gtk::Widget, Atk::Implementor, GLib::Interface, GLib::MetaInterface, Gtk::Object, GLib::InitiallyUnowned, GLib::Object, GLib::Instantiatable, Object, Kernel]
104
+ Note that Gtk::Widget is among ScrolledWindow's ancestors.
105
+ (2) the scrolled window part of this widget is the part that the user adds
106
+ to the GUI application.
107
+ =end
108
+ class ListBox < Gtk::ScrolledWindow
109
+ =begin rdoc
110
+ "super()" is needed because the Listbox class is inherited.
111
+ "self" refers to the ListBox, but because ListBox inherits ScrolledWindow, references to this scrolled window are also made with "self".
112
+ =end
113
+ def initialize
114
+ super()
115
+
116
+ tbl = Gtk::Table.new(2,2,false) # 2 rows, 2 columns, not homogeneous
117
+ self.hscrollbar_policy = Gtk::PolicyType::AUTOMATIC
118
+ self.vscrollbar_policy = Gtk::PolicyType::AUTOMATIC
119
+ self.shadow_type = Gtk::ShadowType::NONE
120
+ self.window_placement= Gtk::CornerType::TOP_LEFT
121
+
122
+ @renderer=Gtk::CellRendererText.new
123
+ @renderer.set_property( 'background', 'lavender' )
124
+ @renderer.set_property( 'foreground', 'black' )
125
+
126
+ @list_store = Gtk::ListStore.new(String)
127
+ @tree_view = Gtk::TreeView.new(@list_store)
128
+
129
+ col_hdr = ''
130
+ @tree_view_col1 = Gtk::TreeViewColumn.new(col_hdr, @renderer, {:text => 0})
131
+ @tree_view_col1.sizing = Gtk::TreeViewColumn::AUTOSIZE
132
+ @text_col = 0
133
+ @tree_view.append_column(@tree_view_col1)
134
+ @tree_view.headers_visible = false
135
+ @tree_view.selection.mode = Gtk::SELECTION_SINGLE
136
+
137
+ tbl.attach_defaults(@tree_view,0,2,0,2) # widget, left, right, top, bottom
138
+ self.add_with_viewport(tbl)
139
+ end # def initialize
140
+
141
+ =begin rdoc
142
+ Appends a string to ListBox's list.
143
+ =end
144
+ def append(str)
145
+ iter = @list_store.append
146
+ iter.set_value(0, str)
147
+ end
148
+
149
+ =begin rdoc
150
+ This is a significant part of the widget, providing communication from it to the application.
151
+ Called by a signal connect mouse button_release_event:
152
+ lb_left.signal_connect('button_release_event') do |widget,event|
153
+ puts lb_left.button_release(widget,event)
154
+ end
155
+ Returns: The string that is selected.
156
+ =end
157
+ def button_release(widget,event,type='text')
158
+ path, column, cell_x, cell_y = @tree_view.get_path_at_pos(event.x, event.y)
159
+ return path if type == 'line_nbr'
160
+ @tree_view.selection.selected[@tree_view.columns.index(column)] if type == 'text'
161
+ end
162
+
163
+ =begin rdoc
164
+ Clears the ListBox's list
165
+ =end
166
+ def clear
167
+ @list_store.clear
168
+ end
169
+
170
+ =begin rdoc
171
+ Fills the ListBox's list from the array of "items".
172
+ =end
173
+ def fill(items)
174
+ items.each { |item| self.append(item) }
175
+ end
176
+
177
+ =begin rdoc
178
+ Provides a header for the list.
179
+ =end
180
+ def header=(hdr)
181
+ @tree_view.headers_visible = true unless @tree_view.headers_visible?
182
+ @tree_view_col1.title = hdr
183
+ end
184
+
185
+ =begin rdoc
186
+ This is a significant part of the widget, providing communication from it to the application.
187
+ Called by a signal connect key_release_event, which occurs when the user uses keys to navigate:
188
+ lb_left.signal_connect('key-release-event') do |widget,event|
189
+ selection = lb_left.key_release(widget,event)
190
+ puts "#{selection}" if selection.class == String
191
+ end
192
+ Returns: Selected string if the Enter or Return key is pressed; nil if any other key is pressed. The up and down keys navigate the list.
193
+ =end
194
+ def key_release(widget,event)
195
+ return nil unless event.keyval == 65293 # Enter
196
+ column = @tree_view.get_column(0)
197
+ @tree_view.selection.selected[@tree_view.columns.index(column)]
198
+ end
199
+
200
+ =begin rdoc
201
+ Select the item in the list that is equal to text
202
+ =end
203
+ def select(text)
204
+ iter = @list_store.iter_first
205
+ begin
206
+ if iter[@text_col] == text
207
+ @tree_view.selection.select_iter(iter)
208
+ return true
209
+ end
210
+ end while iter.next!
211
+ return false
212
+ end
213
+
214
+ =begin rdoc
215
+ Synonym for the header=(hdr) method.
216
+ =end
217
+ def set_header(hdr)
218
+ self.header = hdr
219
+ end
220
+
221
+ =begin rdoc
222
+ Returns the text of line <nbr>.
223
+ =end
224
+ def text_at_line(nbr)
225
+ iter = @list_store.iter_first
226
+ line_nbr = 0
227
+ begin
228
+ return iter.get_value(@text_col) if line_nbr == nbr
229
+ line_nbr += 1
230
+ end while iter.next!
231
+ return []
232
+ end
233
+ end
@@ -0,0 +1,93 @@
1
+ # http://www.lakeinfoworks.com/blog/wp-content/listbox.rb
2
+ class TransmissionRSS::ListBox < Gtk::ScrolledWindow
3
+ def initialize
4
+ super
5
+
6
+ tbl = Gtk::Table.new( 2, 2, false )
7
+
8
+ self.hscrollbar_policy = Gtk::PolicyType::AUTOMATIC
9
+ self.vscrollbar_policy = Gtk::PolicyType::AUTOMATIC
10
+ self.shadow_type = Gtk::ShadowType::NONE
11
+ self.window_placement= Gtk::CornerType::TOP_LEFT
12
+
13
+ @renderer = Gtk::CellRendererText.new
14
+
15
+ # @renderer.set_property( 'background', 'lavender' )
16
+ @renderer.set_property( 'background', 'white' )
17
+ @renderer.set_property( 'foreground', 'black' )
18
+
19
+ @list_store = Gtk::ListStore.new( String )
20
+ @tree_view = Gtk::TreeView.new( @list_store )
21
+
22
+ @tree_view_col1 = Gtk::TreeViewColumn.new( '', @renderer, { :text => 0 } )
23
+ @tree_view_col1.sizing = Gtk::TreeViewColumn::AUTOSIZE
24
+
25
+ @text_col = 0
26
+
27
+ @tree_view.append_column( @tree_view_col1 )
28
+ @tree_view.headers_visible = false
29
+ @tree_view.selection.mode = Gtk::SELECTION_SINGLE
30
+
31
+ tbl.attach_defaults( @tree_view, 0, 2, 0, 2 )
32
+
33
+ self.add_with_viewport(tbl)
34
+ end
35
+
36
+ def add( *args )
37
+ args.flatten.each do |arg|
38
+ iter = @list_store.append
39
+ iter.set_value( 0, arg.to_s )
40
+ end
41
+ end
42
+
43
+ def button_release( widget, event, type = 'text' )
44
+ path, column, cell_x, cell_y = @tree_view.get_path_at_pos( event.x, event.y )
45
+
46
+ return( '' ) if( column.nil? )
47
+
48
+ entry = case( type )
49
+ when 'line_nbr'
50
+ path
51
+ when 'text'
52
+ @tree_view.selection.selected[@tree_view.columns.index( column )]
53
+ end
54
+
55
+ return( entry )
56
+ end
57
+
58
+ def clear
59
+ @list_store.clear
60
+ end
61
+
62
+ def header=( string )
63
+ @tree_view.headers_visible = true
64
+ @tree_view_col1.title = string
65
+ end
66
+
67
+ # TODO produces Gtk-CRITICAL
68
+ def remove( *args )
69
+ args.each do |arg|
70
+ iter = @list_store.iter_first
71
+
72
+ begin
73
+ if( iter.get_value( 0 ) == arg )
74
+ @list_store.remove( iter )
75
+ end
76
+ end while( iter.next! )
77
+ end
78
+ end
79
+
80
+ def items
81
+ array = []
82
+
83
+ iter = @list_store.iter_first
84
+
85
+ if( not iter.nil? )
86
+ begin
87
+ array << iter.get_value( 0 )
88
+ end while( iter.next! )
89
+ end
90
+
91
+ array
92
+ end
93
+ end
@@ -0,0 +1,426 @@
1
+ <?xml version="1.0"?>
2
+ <glade-interface>
3
+ <!-- interface-requires gtk+ 2.16 -->
4
+ <!-- interface-naming-policy project-wide -->
5
+ <widget class="GtkWindow" id="window1">
6
+ <property name="width_request">550</property>
7
+ <property name="height_request">250</property>
8
+ <property name="visible">True</property>
9
+ <child>
10
+ <widget class="GtkVBox" id="vbox1">
11
+ <property name="visible">True</property>
12
+ <property name="orientation">vertical</property>
13
+ <child>
14
+ <widget class="GtkMenuBar" id="menubar1">
15
+ <property name="visible">True</property>
16
+ <child>
17
+ <widget class="GtkMenuItem" id="menuitem1">
18
+ <property name="visible">True</property>
19
+ <property name="label" translatable="yes">_Datei</property>
20
+ <property name="use_underline">True</property>
21
+ <child>
22
+ <widget class="GtkMenu" id="menu1">
23
+ <property name="visible">True</property>
24
+ <child>
25
+ <widget class="GtkImageMenuItem" id="imagemenuitem1">
26
+ <property name="label">gtk-new</property>
27
+ <property name="visible">True</property>
28
+ <property name="use_underline">True</property>
29
+ <property name="use_stock">True</property>
30
+ <signal name="activate" handler="on_menu_new"/>
31
+ </widget>
32
+ </child>
33
+ <child>
34
+ <widget class="GtkImageMenuItem" id="imagemenuitem2">
35
+ <property name="label">gtk-open</property>
36
+ <property name="visible">True</property>
37
+ <property name="use_underline">True</property>
38
+ <property name="use_stock">True</property>
39
+ <signal name="activate" handler="on_menu_open"/>
40
+ </widget>
41
+ </child>
42
+ <child>
43
+ <widget class="GtkImageMenuItem" id="imagemenuitem3">
44
+ <property name="label">gtk-save</property>
45
+ <property name="visible">True</property>
46
+ <property name="use_underline">True</property>
47
+ <property name="use_stock">True</property>
48
+ <signal name="activate" handler="on_menu_save"/>
49
+ </widget>
50
+ </child>
51
+ <child>
52
+ <widget class="GtkImageMenuItem" id="imagemenuitem4">
53
+ <property name="label">gtk-save-as</property>
54
+ <property name="visible">True</property>
55
+ <property name="use_underline">True</property>
56
+ <property name="use_stock">True</property>
57
+ <signal name="activate" handler="on_menu_save_as"/>
58
+ </widget>
59
+ </child>
60
+ <child>
61
+ <widget class="GtkSeparatorMenuItem" id="separatormenuitem1">
62
+ <property name="visible">True</property>
63
+ </widget>
64
+ </child>
65
+ <child>
66
+ <widget class="GtkImageMenuItem" id="imagemenuitem5">
67
+ <property name="label">gtk-quit</property>
68
+ <property name="visible">True</property>
69
+ <property name="use_underline">True</property>
70
+ <property name="use_stock">True</property>
71
+ <signal name="activate" handler="on_menu_quit"/>
72
+ </widget>
73
+ </child>
74
+ </widget>
75
+ </child>
76
+ </widget>
77
+ </child>
78
+ <child>
79
+ <widget class="GtkMenuItem" id="menuitem3">
80
+ <property name="visible">True</property>
81
+ <property name="label" translatable="yes">_Hilfe</property>
82
+ <property name="use_underline">True</property>
83
+ <child>
84
+ <widget class="GtkMenu" id="menu3">
85
+ <property name="visible">True</property>
86
+ <child>
87
+ <widget class="GtkImageMenuItem" id="imagemenuitem10">
88
+ <property name="label">gtk-about</property>
89
+ <property name="visible">True</property>
90
+ <property name="use_underline">True</property>
91
+ <property name="use_stock">True</property>
92
+ <signal name="activate" handler="on_menu_about"/>
93
+ </widget>
94
+ </child>
95
+ </widget>
96
+ </child>
97
+ </widget>
98
+ </child>
99
+ </widget>
100
+ <packing>
101
+ <property name="expand">False</property>
102
+ <property name="position">0</property>
103
+ </packing>
104
+ </child>
105
+ <child>
106
+ <widget class="GtkNotebook" id="notebook1">
107
+ <property name="visible">True</property>
108
+ <property name="can_focus">True</property>
109
+ <property name="tab_pos">left</property>
110
+ <child>
111
+ <widget class="GtkAlignment" id="alignment1">
112
+ <property name="visible">True</property>
113
+ <property name="top_padding">5</property>
114
+ <property name="bottom_padding">5</property>
115
+ <property name="left_padding">5</property>
116
+ <property name="right_padding">5</property>
117
+ <child>
118
+ <widget class="GtkTable" id="table1">
119
+ <property name="visible">True</property>
120
+ <property name="n_rows">2</property>
121
+ <property name="n_columns">2</property>
122
+ <property name="row_spacing">5</property>
123
+ <child>
124
+ <widget class="GtkLabel" id="label4">
125
+ <property name="visible">True</property>
126
+ <property name="xalign">0</property>
127
+ <property name="label" translatable="yes">Host</property>
128
+ </widget>
129
+ <packing>
130
+ <property name="y_options">GTK_FILL</property>
131
+ </packing>
132
+ </child>
133
+ <child>
134
+ <widget class="GtkLabel" id="label5">
135
+ <property name="visible">True</property>
136
+ <property name="xalign">0</property>
137
+ <property name="label" translatable="yes">Port</property>
138
+ </widget>
139
+ <packing>
140
+ <property name="top_attach">1</property>
141
+ <property name="bottom_attach">2</property>
142
+ <property name="y_options">GTK_FILL</property>
143
+ </packing>
144
+ </child>
145
+ <child>
146
+ <widget class="GtkEntry" id="entry_server_host">
147
+ <property name="visible">True</property>
148
+ <property name="can_focus">True</property>
149
+ <property name="invisible_char">&#x25CF;</property>
150
+ </widget>
151
+ <packing>
152
+ <property name="left_attach">1</property>
153
+ <property name="right_attach">2</property>
154
+ <property name="y_options">GTK_FILL</property>
155
+ </packing>
156
+ </child>
157
+ <child>
158
+ <widget class="GtkEntry" id="entry_server_port">
159
+ <property name="visible">True</property>
160
+ <property name="can_focus">True</property>
161
+ <property name="invisible_char">&#x25CF;</property>
162
+ </widget>
163
+ <packing>
164
+ <property name="left_attach">1</property>
165
+ <property name="right_attach">2</property>
166
+ <property name="top_attach">1</property>
167
+ <property name="bottom_attach">2</property>
168
+ <property name="y_options">GTK_FILL</property>
169
+ </packing>
170
+ </child>
171
+ </widget>
172
+ </child>
173
+ </widget>
174
+ </child>
175
+ <child>
176
+ <widget class="GtkLabel" id="label1">
177
+ <property name="visible">True</property>
178
+ <property name="label" translatable="yes">_Server</property>
179
+ <property name="use_underline">True</property>
180
+ </widget>
181
+ <packing>
182
+ <property name="tab_fill">False</property>
183
+ <property name="type">tab</property>
184
+ </packing>
185
+ </child>
186
+ <child>
187
+ <widget class="GtkAlignment" id="alignment2">
188
+ <property name="visible">True</property>
189
+ <property name="top_padding">5</property>
190
+ <property name="bottom_padding">5</property>
191
+ <property name="left_padding">5</property>
192
+ <property name="right_padding">5</property>
193
+ <child>
194
+ <widget class="GtkVBox" id="vbox2">
195
+ <property name="visible">True</property>
196
+ <property name="orientation">vertical</property>
197
+ <property name="spacing">5</property>
198
+ <child>
199
+ <placeholder/>
200
+ </child>
201
+ <child>
202
+ <widget class="GtkHBox" id="hbox2">
203
+ <property name="visible">True</property>
204
+ <child>
205
+ <widget class="GtkEntry" id="entry_feed_url">
206
+ <property name="visible">True</property>
207
+ <property name="can_focus">True</property>
208
+ <property name="invisible_char">&#x25CF;</property>
209
+ </widget>
210
+ <packing>
211
+ <property name="padding">5</property>
212
+ <property name="position">0</property>
213
+ </packing>
214
+ </child>
215
+ <child>
216
+ <widget class="GtkButton" id="button1">
217
+ <property name="label" translatable="yes">+</property>
218
+ <property name="width_request">25</property>
219
+ <property name="visible">True</property>
220
+ <property name="can_focus">True</property>
221
+ <property name="can_default">True</property>
222
+ <property name="has_default">True</property>
223
+ <property name="receives_default">True</property>
224
+ <property name="use_underline">True</property>
225
+ <signal name="clicked" handler="on_button_add_feed"/>
226
+ </widget>
227
+ <packing>
228
+ <property name="expand">False</property>
229
+ <property name="position">1</property>
230
+ </packing>
231
+ </child>
232
+ <child>
233
+ <widget class="GtkButton" id="button2">
234
+ <property name="label" translatable="yes">-</property>
235
+ <property name="width_request">25</property>
236
+ <property name="visible">True</property>
237
+ <property name="can_focus">True</property>
238
+ <property name="can_default">True</property>
239
+ <property name="receives_default">True</property>
240
+ <signal name="clicked" handler="on_button_remove_feed"/>
241
+ </widget>
242
+ <packing>
243
+ <property name="expand">False</property>
244
+ <property name="fill">False</property>
245
+ <property name="position">2</property>
246
+ </packing>
247
+ </child>
248
+ </widget>
249
+ <packing>
250
+ <property name="expand">False</property>
251
+ <property name="pack_type">end</property>
252
+ <property name="position">1</property>
253
+ </packing>
254
+ </child>
255
+ </widget>
256
+ </child>
257
+ </widget>
258
+ <packing>
259
+ <property name="position">1</property>
260
+ </packing>
261
+ </child>
262
+ <child>
263
+ <widget class="GtkLabel" id="label2">
264
+ <property name="visible">True</property>
265
+ <property name="label" translatable="yes">_Feeds</property>
266
+ <property name="use_underline">True</property>
267
+ </widget>
268
+ <packing>
269
+ <property name="position">1</property>
270
+ <property name="tab_fill">False</property>
271
+ <property name="type">tab</property>
272
+ </packing>
273
+ </child>
274
+ <child>
275
+ <widget class="GtkAlignment" id="alignment3">
276
+ <property name="visible">True</property>
277
+ <property name="top_padding">5</property>
278
+ <property name="bottom_padding">5</property>
279
+ <property name="left_padding">5</property>
280
+ <property name="right_padding">5</property>
281
+ <child>
282
+ <widget class="GtkVBox" id="vbox5">
283
+ <property name="visible">True</property>
284
+ <property name="orientation">vertical</property>
285
+ <property name="spacing">15</property>
286
+ <child>
287
+ <widget class="GtkTable" id="table2">
288
+ <property name="visible">True</property>
289
+ <property name="n_rows">3</property>
290
+ <property name="n_columns">2</property>
291
+ <property name="row_spacing">15</property>
292
+ <child>
293
+ <widget class="GtkLabel" id="label7">
294
+ <property name="visible">True</property>
295
+ <property name="xalign">0</property>
296
+ <property name="label" translatable="yes">Update interval (s)</property>
297
+ </widget>
298
+ </child>
299
+ <child>
300
+ <widget class="GtkEntry" id="entry_update_interval">
301
+ <property name="visible">True</property>
302
+ <property name="can_focus">True</property>
303
+ <property name="invisible_char">&#x25CF;</property>
304
+ <property name="width_chars">5</property>
305
+ </widget>
306
+ <packing>
307
+ <property name="left_attach">1</property>
308
+ <property name="right_attach">2</property>
309
+ </packing>
310
+ </child>
311
+ <child>
312
+ <widget class="GtkLabel" id="label6">
313
+ <property name="visible">True</property>
314
+ <property name="xalign">0</property>
315
+ <property name="label" translatable="yes">Logging mode</property>
316
+ </widget>
317
+ <packing>
318
+ <property name="top_attach">1</property>
319
+ <property name="bottom_attach">2</property>
320
+ </packing>
321
+ </child>
322
+ <child>
323
+ <widget class="GtkComboBox" id="combobox_logtype">
324
+ <property name="visible">True</property>
325
+ <property name="items" translatable="yes">STDERR
326
+ File</property>
327
+ <signal name="changed" handler="on_combobox_logtype_changed"/>
328
+ </widget>
329
+ <packing>
330
+ <property name="left_attach">1</property>
331
+ <property name="right_attach">2</property>
332
+ <property name="top_attach">1</property>
333
+ <property name="bottom_attach">2</property>
334
+ </packing>
335
+ </child>
336
+ <child>
337
+ <widget class="GtkLabel" id="label10">
338
+ <property name="visible">True</property>
339
+ <property name="xalign">0</property>
340
+ <property name="label" translatable="yes">Logging file path</property>
341
+ </widget>
342
+ <packing>
343
+ <property name="top_attach">2</property>
344
+ <property name="bottom_attach">3</property>
345
+ </packing>
346
+ </child>
347
+ <child>
348
+ <widget class="GtkEntry" id="entry_log_filepath">
349
+ <property name="visible">True</property>
350
+ <property name="can_focus">True</property>
351
+ <property name="invisible_char">&#x25CF;</property>
352
+ </widget>
353
+ <packing>
354
+ <property name="left_attach">1</property>
355
+ <property name="right_attach">2</property>
356
+ <property name="top_attach">2</property>
357
+ <property name="bottom_attach">3</property>
358
+ </packing>
359
+ </child>
360
+ </widget>
361
+ <packing>
362
+ <property name="expand">False</property>
363
+ <property name="position">0</property>
364
+ </packing>
365
+ </child>
366
+ <child>
367
+ <widget class="GtkCheckButton" id="checkbutton_add_paused">
368
+ <property name="label" translatable="yes">Do not start torrents when added</property>
369
+ <property name="visible">True</property>
370
+ <property name="can_focus">True</property>
371
+ <property name="receives_default">False</property>
372
+ <property name="draw_indicator">True</property>
373
+ </widget>
374
+ <packing>
375
+ <property name="expand">False</property>
376
+ <property name="position">1</property>
377
+ </packing>
378
+ </child>
379
+ </widget>
380
+ </child>
381
+ </widget>
382
+ <packing>
383
+ <property name="position">2</property>
384
+ </packing>
385
+ </child>
386
+ <child>
387
+ <widget class="GtkLabel" id="label3">
388
+ <property name="visible">True</property>
389
+ <property name="label" translatable="yes">_Options</property>
390
+ <property name="use_underline">True</property>
391
+ </widget>
392
+ <packing>
393
+ <property name="position">2</property>
394
+ <property name="tab_fill">False</property>
395
+ <property name="type">tab</property>
396
+ </packing>
397
+ </child>
398
+ </widget>
399
+ <packing>
400
+ <property name="position">1</property>
401
+ </packing>
402
+ </child>
403
+ <child>
404
+ <widget class="GtkStatusbar" id="statusbar1">
405
+ <property name="visible">True</property>
406
+ <property name="spacing">2</property>
407
+ </widget>
408
+ <packing>
409
+ <property name="expand">False</property>
410
+ <property name="position">2</property>
411
+ </packing>
412
+ </child>
413
+ </widget>
414
+ </child>
415
+ </widget>
416
+ <widget class="GtkWindow" id="window2">
417
+ <child>
418
+ <widget class="GtkLabel" id="label8">
419
+ <property name="width_request">320</property>
420
+ <property name="height_request">240</property>
421
+ <property name="visible">True</property>
422
+ <property name="label" translatable="yes">devoted to ann</property>
423
+ </widget>
424
+ </child>
425
+ </widget>
426
+ </glade-interface>
@@ -0,0 +1,254 @@
1
+ # Represents the config editor window.
2
+ class TransmissionRSS::ConfigEditor
3
+ TITLE = 'transmission-rss config editor'
4
+ NAME = 'config-editor'
5
+
6
+ DEFAULT_CONFIG = {
7
+ 'feeds' => [],
8
+ 'update_interval' => 600,
9
+ 'start_paused' => false,
10
+ 'server' => {
11
+ 'host' => 'localhost',
12
+ 'port' => 9091
13
+ },
14
+ 'log_target' => $stderr
15
+ }
16
+
17
+ # Loads glade file and initializes dynamic GUI elements.
18
+ def initialize( configFile, config )
19
+ @configFile = configFile
20
+ @config = config
21
+
22
+ path = File.join( File.dirname( __FILE__ ), 'main.glade' )
23
+
24
+ # Load glade file and connect to handler method. Dubious.
25
+ @glade = GladeXML.new( path ) do |handler|
26
+ method( handler )
27
+ end
28
+
29
+ # Connect every widget to an instance variable.
30
+ @glade.widget_names.each do |name|
31
+ name.gsub!( /-/, '_' )
32
+ instance_variable_set( "@#{name}".intern, @glade[name] )
33
+ end
34
+
35
+ # Quit program, when it is closed by the WM.
36
+ @window1.signal_connect( 'destroy' ) do
37
+ Gtk.main_quit
38
+ end
39
+
40
+ # Initialize the ListBox widget.
41
+ initialize_listbox
42
+
43
+ # Reflect the current config on the GUI.
44
+ initialize_config
45
+ end
46
+
47
+ # Reflects the current config on the GUI.
48
+ def initialize_config
49
+ @entry_server_host.text = @config.server.host
50
+ @entry_server_port.text = @config.server.port.to_s
51
+
52
+ @entry_update_interval.text = @config.update_interval.to_s
53
+
54
+ @checkbutton_add_paused.active = @config.start_paused
55
+
56
+ @listbox.clear
57
+ @listbox.add( @config.feeds )
58
+
59
+ # If log target is STDERR.
60
+ if( @config.log_target.class == IO )
61
+ # Deactivate log path entry.
62
+ @label10.sensitive = false
63
+ @entry_log_filepath.sensitive = false
64
+
65
+ @combobox_logtype.active = 0
66
+ else
67
+ # Activate log path entry.
68
+ @label10.sensitive = true
69
+ @entry_log_filepath.sensitive = true
70
+
71
+ # Set entry text accordingly.
72
+ @entry_log_filepath.text = @config.log_target
73
+
74
+ @combobox_logtype.active = 1
75
+ end
76
+ end
77
+
78
+ # Initializes the ListBox widget.
79
+ def initialize_listbox
80
+ @listbox = ListBox.new
81
+ @listbox.header = 'Feeds'
82
+
83
+ @vbox2.pack_start( @listbox )
84
+
85
+ @window1.show_all
86
+
87
+ @listbox.signal_connect( 'button_release_event' ) do |widget, event|
88
+ @entry_feed_url.text = @listbox.button_release( widget, event )
89
+ end
90
+ end
91
+
92
+ # Add a feed to the ListBox if the Add-feed-button is pressed.
93
+ def on_button_add_feed( widget )
94
+ if( not @entry_feed_url.text.empty? )
95
+ @listbox.add( @entry_feed_url.text )
96
+ @entry_feed_url.text = ''
97
+ end
98
+ end
99
+
100
+ # Remove a feed from the ListBox if the Remove-feed-button is pressed.
101
+ def on_button_remove_feed( widget )
102
+ @listbox.remove( @entry_feed_url.text )
103
+ end
104
+
105
+ # Is called when a value in the log type ComboBox is selected.
106
+ def on_combobox_logtype_changed( widget )
107
+ # If STDERR is selected.
108
+ if( @combobox_logtype.active == 0 )
109
+ # Deactivate the log file path entry.
110
+ @label10.sensitive = false
111
+ @entry_log_filepath.sensitive = false
112
+
113
+ @entry_log_filepath.text = ''
114
+ else
115
+ # Activate the log file path entry.
116
+ @label10.sensitive = true
117
+ @entry_log_filepath.sensitive = true
118
+ end
119
+ end
120
+
121
+ # Is called when the Help-About menu is selected.
122
+ def on_menu_about( widget )
123
+ Gnome::About.new(
124
+ TITLE,
125
+ VERSION,
126
+ 'Copyright (C) 2010 henning mueller',
127
+ 'Easy transmission-rss config editing. Devoted to Ann.',
128
+ ['henning mueller'],
129
+ ['henning mueller'],
130
+ nil
131
+ ).show
132
+ end
133
+
134
+ # Is called when the File-New menu is selected.
135
+ def on_menu_new( widget )
136
+ dialog = Gtk::MessageDialog.new(
137
+ @window1,
138
+ Gtk::Dialog::DESTROY_WITH_PARENT,
139
+ Gtk::MessageDialog::WARNING,
140
+ Gtk::MessageDialog::BUTTONS_YES_NO,
141
+ "Unsaved configuration will be lost!\nProceed?"
142
+ )
143
+
144
+ if( dialog.run == Gtk::Dialog::RESPONSE_YES )
145
+ @configFile = nil
146
+
147
+ @config.clear
148
+ @config.load( DEFAULT_CONFIG )
149
+
150
+ initialize_config
151
+ end
152
+
153
+ dialog.destroy
154
+ end
155
+
156
+ # Is called when the File-Open menu is selected.
157
+ def on_menu_open( widget )
158
+ dialog = Gtk::FileChooserDialog.new(
159
+ 'Open',
160
+ @window1,
161
+ Gtk::FileChooser::ACTION_OPEN,
162
+ nil,
163
+ [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
164
+ [Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT]
165
+ )
166
+
167
+ if( dialog.run == Gtk::Dialog::RESPONSE_ACCEPT )
168
+ @configFile = dialog.filename
169
+
170
+ @config.clear
171
+ @config.load( DEFAULT_CONFIG )
172
+ @config.load( @configFile )
173
+
174
+ initialize_config
175
+ end
176
+
177
+ dialog.destroy
178
+ end
179
+
180
+ # Is called when the File-Save menu is selected.
181
+ def on_menu_save( widget )
182
+ if( not @configFile.nil? )
183
+ save!
184
+ else
185
+ on_menu_save_as( nil )
186
+ end
187
+ end
188
+
189
+ # Is called when the File-SaveAs menu is selected.
190
+ def on_menu_save_as( widget )
191
+ dialog = Gtk::FileChooserDialog.new(
192
+ 'Save As..',
193
+ @window1,
194
+ Gtk::FileChooser::ACTION_SAVE,
195
+ nil,
196
+ [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
197
+ [Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT]
198
+ )
199
+
200
+ if( dialog.run == Gtk::Dialog::RESPONSE_ACCEPT )
201
+ @configFile = dialog.filename
202
+ save!
203
+ end
204
+
205
+ dialog.destroy
206
+ end
207
+
208
+ # Is called when the File-Quit menu is selected.
209
+ def on_menu_quit( widget )
210
+ Gtk.main_quit
211
+ end
212
+
213
+ # Convert GUI config to +Config+, open the config file and write a YAML
214
+ # version of the Hash.
215
+ def save!
216
+ @config.server.host = @entry_server_host.text
217
+ @config.server.port = @entry_server_port.text.to_i
218
+
219
+ @config.update_interval = @entry_update_interval.text.to_i
220
+
221
+ @config.start_paused = @checkbutton_add_paused.active?
222
+
223
+ @config.feeds = @listbox.items
224
+
225
+ # If STDERR is selected.
226
+ if( @combobox_logtype.active == 0 )
227
+ # Delete log_target from config hash, so $stderr is chosen on load.
228
+ @config.delete( 'log_target' )
229
+ else
230
+ # Set log_target to entry text.
231
+ @config.log_target = @entry_log_filepath.text
232
+ end
233
+
234
+ # Try writing to file; dialog on permission error.
235
+ begin
236
+ File.open( @configFile, 'w' ) do |f|
237
+ f.write( @config.to_yaml )
238
+ end
239
+ rescue Errno::EACCES
240
+ dialog = Gtk::MessageDialog.new(
241
+ @window1,
242
+ Gtk::Dialog::DESTROY_WITH_PARENT,
243
+ Gtk::MessageDialog::ERROR,
244
+ Gtk::MessageDialog::BUTTONS_CLOSE,
245
+ "Permission denied:\n" + @configFile
246
+ )
247
+
248
+ dialog.run
249
+ dialog.destroy
250
+
251
+ on_menu_save_as( nil )
252
+ end
253
+ end
254
+ end
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: transmission-rss
3
3
  version: !ruby/object:Gem::Version
4
- hash: 13
5
4
  prerelease: false
6
5
  segments:
7
6
  - 0
7
+ - 1
8
8
  - 0
9
- - 9
10
- version: 0.0.9
9
+ version: 0.1.0
11
10
  platform: ruby
12
11
  authors:
13
12
  - henning mueller
@@ -15,7 +14,7 @@ autorequire:
15
14
  bindir: bin
16
15
  cert_chain: []
17
16
 
18
- date: 2010-08-23 00:00:00 +02:00
17
+ date: 2010-09-11 00:00:00 +02:00
19
18
  default_executable:
20
19
  dependencies: []
21
20
 
@@ -33,7 +32,12 @@ extra_rdoc_files: []
33
32
  files:
34
33
  - bin/transmission-rss
35
34
  - lib/transmission-rss.rb
35
+ - lib/transmission-rss/config-editor/listbox.rb
36
+ - lib/transmission-rss/config-editor/listbox-original.rb
37
+ - lib/transmission-rss/config-editor/main.rb
38
+ - lib/transmission-rss/config-editor/main.glade
36
39
  - lib/transmission-rss/log.rb
40
+ - lib/transmission-rss/config-editor.rb
37
41
  - lib/transmission-rss/aggregator.rb
38
42
  - lib/transmission-rss/config.rb
39
43
  - lib/transmission-rss/hash.rb
@@ -53,7 +57,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
53
57
  requirements:
54
58
  - - ">="
55
59
  - !ruby/object:Gem::Version
56
- hash: 3
57
60
  segments:
58
61
  - 0
59
62
  version: "0"
@@ -62,7 +65,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
62
65
  requirements:
63
66
  - - ">="
64
67
  - !ruby/object:Gem::Version
65
- hash: 3
66
68
  segments:
67
69
  - 0
68
70
  version: "0"