transmission-rss 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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"