zik 0.16.1

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.
Files changed (84) hide show
  1. data.tar.gz.sig +0 -0
  2. data/ChangeLog +222 -0
  3. data/INSTALL +34 -0
  4. data/Makefile.in +199 -0
  5. data/README +53 -0
  6. data/ZiK.desktop +11 -0
  7. data/ZiK.rb +377 -0
  8. data/configure +269 -0
  9. data/dependencies +142 -0
  10. data/doc/COPYING +340 -0
  11. data/doc/ChangeLog +222 -0
  12. data/doc/html/fr/index.html +128 -0
  13. data/doc/html/index.html +126 -0
  14. data/doc/html/pix/ZiK3.png +0 -0
  15. data/doc/html/pix/capture.png +0 -0
  16. data/doc/html/pix/capture_cd.png +0 -0
  17. data/doc/html/pix/capture_compact.png +0 -0
  18. data/doc/html/pix/capture_radio.png +0 -0
  19. data/doc/html/pix/icon.png +0 -0
  20. data/doc/html/pix/logo.png +0 -0
  21. data/doc/html/styles/main.css +47 -0
  22. data/doc/man/manpage.1 +43 -0
  23. data/gui/assistant.rb +133 -0
  24. data/gui/cdgui.rb +54 -0
  25. data/gui/common.rb +56 -0
  26. data/gui/config.rb +120 -0
  27. data/gui/edit.rb +413 -0
  28. data/gui/error.rb +124 -0
  29. data/gui/gui.rb +1383 -0
  30. data/gui/mod.rb +239 -0
  31. data/gui/preference.rb +496 -0
  32. data/gui/progress.rb +50 -0
  33. data/gui/radiogui.rb +404 -0
  34. data/gui/search.rb +249 -0
  35. data/gui/socket.rb +107 -0
  36. data/mod/brainz.rb +76 -0
  37. data/mod/brainz/dialogs.rb +132 -0
  38. data/mod/brainz/functions.rb +55 -0
  39. data/mod/brainz/ofa.rb +76 -0
  40. data/mod/brainz/song.rb +139 -0
  41. data/mod/brainz/widget.rb +34 -0
  42. data/mod/cover.rb +90 -0
  43. data/mod/cover/application-x-cd-image.svg +666 -0
  44. data/mod/cover/config.rb +79 -0
  45. data/mod/cover/configwidget.rb +35 -0
  46. data/mod/cover/widget.rb +147 -0
  47. data/mod/hotkeys.rb +137 -0
  48. data/mod/hotkeys/config.rb +84 -0
  49. data/mod/hotkeys/configwidget.rb +93 -0
  50. data/mod/interact.rb +131 -0
  51. data/mod/interact/configwidget.rb +107 -0
  52. data/mod/interact/interaction.rb +79 -0
  53. data/mod/notify.rb +128 -0
  54. data/mod/notify/config.rb +96 -0
  55. data/mod/notify/configwidget.rb +76 -0
  56. data/mod/notify/window.rb +56 -0
  57. data/mod/trayicon.rb +83 -0
  58. data/mod/trayicon/config.rb +79 -0
  59. data/mod/trayicon/configwidget.rb +35 -0
  60. data/mod/trayicon/icon.rb +99 -0
  61. data/pix/ZiK.svg +78 -0
  62. data/pix/ZiK3.png +0 -0
  63. data/pix/assistant/capture.png +0 -0
  64. data/pix/gtk-directory.svg +287 -0
  65. data/pix/media-cdrom.svg +292 -0
  66. data/pix/network-wireless.svg +429 -0
  67. data/pix/player_play.svg +286 -0
  68. data/pix/sound.svg +244 -0
  69. data/pix/sound_directory.svg +464 -0
  70. data/player/cdplayer.rb +108 -0
  71. data/player/player.rb +132 -0
  72. data/player/radioplayer.rb +124 -0
  73. data/playlist/cdlist.rb +140 -0
  74. data/playlist/common.rb +343 -0
  75. data/playlist/editlist.rb +50 -0
  76. data/playlist/export.rb +108 -0
  77. data/playlist/playlist.rb +143 -0
  78. data/playlist/radiolist.rb +148 -0
  79. data/playlist/song.rb +122 -0
  80. data/po/ZiK.pot +622 -0
  81. data/po/fr/ZiK.po +658 -0
  82. data/zik +2 -0
  83. metadata +265 -0
  84. metadata.gz.sig +0 -0
data/gui/gui.rb ADDED
@@ -0,0 +1,1383 @@
1
+ # encoding: UTF-8
2
+ =begin
3
+ Copyright 2007-2011 Vincent Carmona
4
+
5
+ This file is part of ZiK.
6
+
7
+ ZiK is free software; you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation; either version 2 of the License, or
10
+ (at your option) any later version.
11
+
12
+ ZiK is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with ZiK; if not, write to the Free Software
19
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
+ =end
21
+
22
+ class Gui < Gtk::Window
23
+ include Pack
24
+
25
+ type_register#needded to implement new signals
26
+ attr_reader :renderer2
27
+ attr_reader :vbox, :selectview, :cm_list, :menu_bar#needded for modules, in gui/mod.rb
28
+ attr_reader :playlist, :config, :list_selection#needded for modules
29
+
30
+ signal_new('play', GLib::Signal::RUN_FIRST, nil, nil, Song)
31
+ def signal_do_play(song); end
32
+ signal_new('stop', GLib::Signal::RUN_FIRST, nil, nil)
33
+ def signal_do_stop; end
34
+
35
+ def initialize(player, playlist, config, cdplayer=nil, cdlist=nil, radioplayer=nil, radiolist=nil)
36
+ @player=player
37
+ @active_view='default'
38
+ @playlist=playlist
39
+ @config=config
40
+ if $cd
41
+ @cdplayer=cdplayer
42
+ @cdlist=cdlist
43
+ end
44
+ if $radio
45
+ @radiolist=radiolist
46
+ @radioplayer=radioplayer
47
+ end
48
+
49
+ @editing=false
50
+ @seeking=false
51
+ @nb_timeout=0
52
+
53
+ #*********************Menu zone*******************************
54
+ #Music menu
55
+ @play_it=Gtk::ImageMenuItem.new(Gtk::Stock::MEDIA_PLAY)
56
+ @play_it.signal_connect('activate'){play}
57
+ @pause_it=Gtk::ImageMenuItem.new(Gtk::Stock::MEDIA_PAUSE)
58
+ @pause_it.signal_connect('activate'){pause}
59
+ @stop_it=Gtk::ImageMenuItem.new(Gtk::Stock::MEDIA_STOP)
60
+ @stop_it.signal_connect('activate'){stop}
61
+ @previous_it=Gtk::ImageMenuItem.new(Gtk::Stock::MEDIA_PREVIOUS)
62
+ @previous_it.signal_connect('activate'){previous_song}
63
+ @next_it=Gtk::ImageMenuItem.new(Gtk::Stock::MEDIA_NEXT)
64
+ @next_it.signal_connect('activate'){next_song}
65
+ @separator_it=Gtk::SeparatorMenuItem.new
66
+ @shuffle_it=Gtk::CheckMenuItem.new(_('Shuffle'))
67
+ @shuffle_it.active=@config['shuffle']
68
+ @shuffle_it.signal_connect('toggled'){self.shuffle=@shuffle_it.active?}
69
+ @repeat_it=Gtk::CheckMenuItem.new(_('Repeat'))
70
+ @repeat_it.active=@config['repeat']
71
+ @repeat_it.signal_connect('toggled'){self.repeat=@repeat_it.active?}
72
+ @separator_it2=Gtk::SeparatorMenuItem.new
73
+ @preference_it=Gtk::ImageMenuItem.new(Gtk::Stock::PREFERENCES)
74
+ @preference_it.signal_connect('activate'){
75
+ if $cd
76
+ Preference_window.show(self, @playlist, @config, :player, @cdlist, @cdplayer, @wcd)
77
+ else
78
+ Preference_window.show(self, @playlist, @config, :player)
79
+ end
80
+ }
81
+ @quit_it=Gtk::ImageMenuItem.new(Gtk::Stock::QUIT)
82
+ @quit_it.signal_connect('activate'){quit}
83
+
84
+ @music_menu=Gtk::Menu.new
85
+ [@play_it, @pause_it, @stop_it,@previous_it, @next_it, @separator_it, @shuffle_it, @repeat_it, @separator_it2,
86
+ @preference_it, @quit_it].each{|it|
87
+ @music_menu.append(it)
88
+ }
89
+
90
+ #Browser menu
91
+ @refreshtree_it=Gtk::ImageMenuItem.new(Gtk::Stock::REFRESH)
92
+ @refreshtree_it.signal_connect('activate'){refresh_treestore(@config['directory'],@config['extension'])}
93
+ @find_it=Gtk::ImageMenuItem.new(Gtk::Stock::FIND)
94
+ @find_it.signal_connect('activate'){BrowseSearchWindow.show(self,@config)}
95
+ @preference_it2=Gtk::ImageMenuItem.new(Gtk::Stock::PREFERENCES)
96
+ @preference_it2.signal_connect('activate'){
97
+ if $cd
98
+ Preference_window.show(self, @playlist, @config, :browse, @cdlist, @cdplayer, @wcd)
99
+ else
100
+ Preference_window.show(self, @playlist, @config, :browse)
101
+ end
102
+ }
103
+
104
+ @browse_menu=Gtk::Menu.new
105
+ [@refreshtree_it, @find_it, @preference_it2].each{|it| @browse_menu.append(it)}
106
+
107
+ #Playlist menu
108
+ @add_it=Gtk::ImageMenuItem.new(Gtk::Stock::ADD)
109
+ @add_it.signal_connect('activate'){import_list}
110
+ @rm_it=Gtk::ImageMenuItem.new(Gtk::Stock::REMOVE)
111
+ @rm_it.signal_connect('activate'){list_rm(@list_selection)}
112
+ @clear_it=Gtk::ImageMenuItem.new(Gtk::Stock::CLEAR)
113
+ @clear_it.signal_connect('activate'){list_clear}
114
+ @find2_it=Gtk::ImageMenuItem.new(Gtk::Stock::FIND)
115
+ @find2_it.signal_connect('activate'){search}
116
+ @edit_it=Gtk::ImageMenuItem.new(Gtk::Stock::EDIT)
117
+ @edit_it.signal_connect('activate'){list_edit}
118
+ @bsave=Gtk::ImageMenuItem.new(Gtk::Stock::SAVE)
119
+ @separator_it2=Gtk::SeparatorMenuItem.new
120
+ @bsave.signal_connect('activate'){list_save}
121
+ @separator_it3=Gtk::SeparatorMenuItem.new
122
+ @bexport=Gtk::MenuItem.new(_("Export songs"))
123
+ @bexport.tooltip_text=_("Copy songs to a directory and write the selected playlist file.")
124
+ @bexport.signal_connect('activate'){export_songs}
125
+ @preference_it3=Gtk::ImageMenuItem.new(Gtk::Stock::PREFERENCES)
126
+ @preference_it3.signal_connect('activate'){
127
+ if $cd
128
+ Preference_window.show(self, @playlist, @config, :playlist, @cdlist, @cdplayer, @wcd)
129
+ else
130
+ Preference_window.show(self,@playlist,@config,:playlist)
131
+ end
132
+ }
133
+
134
+ @playlist_menu=Gtk::Menu.new
135
+ #Do not show @edit_it. See bug 28638
136
+ #Fixme
137
+ # [@add_it, @rm_it, @clear_it, @find2_it, @edit_it, @bsave, @separator_it2, @bexport,
138
+ [@add_it, @rm_it, @clear_it, @find2_it, @bsave, @separator_it2, @bexport,
139
+ @separator_it3, @preference_it3].each{|it| @playlist_menu.append(it)}
140
+
141
+ #Radio menu
142
+ if $radio
143
+ @add_rit=Gtk::ImageMenuItem.new(Gtk::Stock::ADD)
144
+ @add_rit.signal_connect('activate'){@wradio.add_radio}
145
+ @rm_rit=Gtk::ImageMenuItem.new(Gtk::Stock::REMOVE)
146
+ @rm_rit.signal_connect('activate'){
147
+ current_removed=@wradio.rm
148
+ if @active_view=='radio'
149
+ @radioplayer.stop if current_removed
150
+ end
151
+ }
152
+ @clear_rit=Gtk::ImageMenuItem.new(Gtk::Stock::CLEAR)
153
+ @clear_rit.signal_connect('activate'){@radioplayer.stop; @wradio.clear}
154
+ @edit_rit=Gtk::ImageMenuItem.new(Gtk::Stock::EDIT)
155
+ @edit_rit.signal_connect('activate'){radios_edit}
156
+ @import_rit=Gtk::ImageMenuItem.new(_('_Import'))
157
+ @import_rit.image=Gtk::Image.new(Gtk::Stock::ADD, Gtk::IconSize::MENU)
158
+ @import_rit.signal_connect('activate'){@wradio.import}
159
+ @radio_menu=Gtk::Menu.new
160
+ [@add_rit, @rm_rit, @clear_rit, @edit_rit, @import_rit].each{|it| @radio_menu.append(it)}
161
+ end
162
+
163
+ #Help menu
164
+ @help_it=Gtk::ImageMenuItem.new(Gtk::Stock::HELP)
165
+ @help_it.signal_connect('activate'){help}
166
+ @assistant_it=Gtk::MenuItem.new(_('Assistant'))
167
+ @assistant_it.signal_connect('activate'){
168
+ require File.join(Data_dir, 'gui/assistant')
169
+ Assistant.new(@config, self)
170
+ }
171
+ @bug_it=Gtk::MenuItem.new(_('Report a bug'))
172
+ @bug_it.signal_connect('activate'){
173
+ unless system('gnome-open http://rubyforge.org/tracker/?group_id=5494')
174
+ ErrorError.new('Cannot open http://rubyforge.org/tracker/?group_id=5494', true)
175
+ end
176
+ }
177
+ @about_it=Gtk::ImageMenuItem.new(Gtk::Stock::ABOUT)
178
+ @about_it.signal_connect('activate'){about}
179
+
180
+ @help_menu=Gtk::Menu.new
181
+ [@help_it, @assistant_it, @bug_it, @about_it].each{|it| @help_menu.append(it)}
182
+
183
+ #Menu bar
184
+ @music_it=Gtk::MenuItem.new(_('_Music'))
185
+ @music_it.set_submenu(@music_menu)
186
+ @broswe_it=Gtk::MenuItem.new(_('_Browser'))
187
+ @broswe_it.set_submenu(@browse_menu)
188
+ @playlist_it=Gtk::MenuItem.new(_('Play_list'))
189
+ @playlist_it.set_submenu(@playlist_menu)
190
+ if $radio
191
+ @radiolist_it=Gtk::MenuItem.new(_('_Radio'))
192
+ @radiolist_it.set_submenu(@radio_menu)
193
+ end
194
+ @help_it2=Gtk::MenuItem.new(_('_Help'))
195
+ @help_it2.set_submenu(@help_menu)
196
+
197
+ @menu_bar=Gtk::MenuBar.new
198
+ if $radio
199
+ [@music_it, @broswe_it, @playlist_it, @radiolist_it, @help_it2].each{|it| @menu_bar.append(it)}
200
+ else
201
+ [@music_it, @broswe_it, @playlist_it, @help_it2].each{|it| @menu_bar.append(it)}
202
+ end
203
+ #*********************Browser zone****************************
204
+ @treestore=Gtk::TreeStore.new(String,String)
205
+ GLib::Idle.add{
206
+ refresh_treestore(@config['directory'], @config['extension'], true)
207
+ false
208
+ }
209
+
210
+ @renderer=Gtk::CellRendererText.new
211
+ @col=Gtk::TreeViewColumn.new(_("Browser"), @renderer, :text => 0)
212
+ @treeview=Gtk::TreeView.new(@treestore)#See also Gtk::IconView.
213
+ @treeview.append_column(@col)
214
+ @treeview.signal_connect("row-activated"){|view, path, column|
215
+ if @editing
216
+ @edit_wrapper.add_song(@treestore.get_iter(path)[1])
217
+ else
218
+ @playlist.add(@treestore.get_iter(path)[1], @config['extension'])
219
+ refresh_liststore
220
+ end
221
+ }
222
+ @treeview.signal_connect("button_press_event"){|widget, event|
223
+ if event.button==3
224
+ @cm_browse.popup(nil, nil, event.button, event.time)
225
+ end
226
+ }
227
+
228
+ @tree_selection=@treeview.selection
229
+ @tree_selection.mode=Gtk::SELECTION_MULTIPLE
230
+
231
+
232
+ @wtree=Gtk::ScrolledWindow.new
233
+ @wtree.set_policy(Gtk::POLICY_AUTOMATIC,Gtk::POLICY_AUTOMATIC)
234
+ @wtree.add(@treeview)
235
+
236
+ #Drag&drop
237
+ @treeview.enable_model_drag_source(Gdk::Window::BUTTON1_MASK,
238
+ [['brower-selection', Gtk::Drag::TargetFlags::SAME_APP, DragType::BROWSER_SELECTION]],
239
+ Gdk::DragContext::ACTION_COPY)
240
+ #@treeview.signal_connect("drag-data-get"){|widget, context, data, info, time| data.set(Gdk::Selection::TYPE_STRING, 'toto')}
241
+ #data.uris=[uris1,uris2] crash!
242
+ @treeview.enable_model_drag_dest([['text/uri-list', Gtk::Drag::TargetFlags::OTHER_APP, DragType::URI_LIST]],
243
+ Gdk::DragContext::ACTION_COPY)
244
+ @treeview.signal_connect("drag-data-received"){|widget, context, x, y, data, info, time|
245
+ if info==DragType::URI_LIST
246
+ old=@config['directory']
247
+ data.uris.each{|uri|
248
+ path=GLib.filename_from_uri(uri)[0]
249
+ @config['directory']+=path+',' if File.directory?(path)
250
+ }
251
+ end
252
+ refresh_treestore(@config['directory'], @config['extension']) unless old==@config['directory']
253
+ }
254
+
255
+ #Context menu
256
+ @brefresh_browse=Gtk::ImageMenuItem.new(Gtk::Stock::REFRESH)
257
+ @brefresh_browse.signal_connect('activate'){refresh_treestore(@config['directory'],@config['extension'])}
258
+ @bfind_browse=Gtk::ImageMenuItem.new(Gtk::Stock::FIND)
259
+ @bfind_browse.signal_connect('activate'){BrowseSearchWindow.show(self,@config)}
260
+ @bpreference_browse=Gtk::ImageMenuItem.new(Gtk::Stock::PREFERENCES)
261
+ @bpreference_browse.signal_connect('activate'){
262
+ if $cd
263
+ Preference_window.show(self, @playlist, @config, :browse, @cdlist, @cdplayer, @wcd)
264
+ else
265
+ Preference_window.show(self, @playlist, @config, :browse)
266
+ end
267
+ }
268
+
269
+ @cm_browse=Gtk::Menu.new
270
+ @cm_browse.append(@brefresh_browse)
271
+ @cm_browse.append(@bfind_browse)
272
+ @cm_browse.append(@bpreference_browse)
273
+ @cm_browse.show_all
274
+
275
+ #*********************Buttons zone*****************************
276
+ @badd=Gtk::Button.new
277
+ @badd.image=Gtk::Image.new(Gtk::Stock::ADD,Gtk::IconSize::BUTTON)
278
+ @badd.tooltip_text=_('Add selected files to playlist')
279
+ @badd.signal_connect('clicked') {
280
+ if @editing
281
+ @edit_wrapper.add_songs(@tree_selection)
282
+ else
283
+ list_add(@tree_selection, @config['extension'])
284
+ end
285
+ }
286
+
287
+ @brm=Gtk::Button.new
288
+ @brm.image=Gtk::Image.new(Gtk::Stock::REMOVE,Gtk::IconSize::BUTTON)
289
+ @brm.tooltip_text=_('Remove selected songs from playlist')
290
+ @brm.signal_connect('clicked') {@editing ? @edit_wrapper.rm_song : list_rm(@list_selection)}
291
+
292
+ @bclear=Gtk::Button.new
293
+ @bclear.image=Gtk::Image.new(Gtk::Stock::CLEAR,Gtk::IconSize::BUTTON)
294
+ @bclear.tooltip_text=_('Clear playlist')
295
+ @bclear.signal_connect('clicked') {@editing ? @edit_wrapper.clear : list_clear}
296
+
297
+ @bedit=Gtk::Button.new
298
+ @bedit.image=Gtk::Image.new(Gtk::Stock::EDIT,Gtk::IconSize::BUTTON)
299
+ @bedit.tooltip_text=_('Edit playlist')
300
+ @bedit.signal_connect('clicked'){list_edit}
301
+
302
+ @bfind=Gtk::Button.new
303
+ @bfind.image=Gtk::Image.new(Gtk::Stock::FIND, Gtk::IconSize::BUTTON)
304
+ @bfind.tooltip_text=_('Find in playlist')
305
+ @bfind.signal_connect('clicked'){@editing ? @edit_wrapper.search : search}
306
+
307
+ #*********************Playlist zone****************************
308
+ @list_store=Gtk::ListStore.new(String,TrueClass)
309
+ refresh_liststore
310
+
311
+ @renderer2=Gtk::CellRendererText.new
312
+ @renderer2.background=@config['playing_color']
313
+ @col2=Gtk::TreeViewColumn.new(_("Playlist"), @renderer2, :text => 0, :background_set => 1)
314
+ @list_view=Gtk::TreeView.new(@list_store)
315
+ #@list_view.headers_visible=false
316
+ @list_view.append_column(@col2)
317
+ @list_view.signal_connect("row-activated"){|view, path, column|
318
+ @playlist.current=path.to_s.to_i
319
+ stop
320
+ play
321
+ }
322
+ @list_view.signal_connect("button_press_event"){|widget, event|
323
+ if event.button==3
324
+ @cm_list.popup(nil, nil, event.button, event.time)
325
+ end
326
+ }
327
+ compact(@config['compact'])
328
+
329
+ @list_selection=@list_view.selection
330
+ @list_selection.mode=Gtk::SELECTION_MULTIPLE
331
+
332
+ @wlist=Gtk::ScrolledWindow.new
333
+ @wlist.set_policy(Gtk::POLICY_AUTOMATIC,Gtk::POLICY_AUTOMATIC)
334
+ @wlist.add(@list_view)
335
+
336
+ #Drag&drop
337
+ dragtable=[['brower-selection', Gtk::Drag::TargetFlags::SAME_APP, DragType::BROWSER_SELECTION],
338
+ ['text/uri-list', Gtk::Drag::TargetFlags::OTHER_APP, DragType::URI_LIST]]
339
+ @list_view.enable_model_drag_dest(dragtable, Gdk::DragContext::ACTION_COPY)
340
+ @list_view.signal_connect("drag-data-received"){|widget, context, x, y, data, info, time|
341
+ case info
342
+ when DragType::BROWSER_SELECTION
343
+ r=@list_view.get_dest_row(x, y)
344
+ if r.nil?
345
+ list_add(@tree_selection, @config['extension'])
346
+ else
347
+ pos=r[0].to_s.to_i
348
+ case r[1]
349
+ when Gtk::TreeView::DROP_AFTER, Gtk::TreeView::DROP_INTO_OR_AFTER
350
+ pos+=1
351
+ end
352
+ list_add_at(@tree_selection, @config['extension'], pos)
353
+ end
354
+ when DragType::URI_LIST
355
+ r=@list_view.get_dest_row(x, y)
356
+ if r.nil?
357
+ list_add_uris(data.uris, @config['extension'])
358
+ else
359
+ pos=r[0].to_s.to_i
360
+ case r[1]
361
+ when Gtk::TreeView::DROP_AFTER, Gtk::TreeView::DROP_INTO_OR_AFTER
362
+ pos+=1
363
+ end
364
+ list_add_uris_at(data.uris, @config['extension'], pos)
365
+ end
366
+ end
367
+ }
368
+
369
+ #Context menu
370
+ @badd_list=Gtk::ImageMenuItem.new(Gtk::Stock::ADD)
371
+ @badd_list.signal_connect("activate"){import_list}
372
+ @brm_list=Gtk::ImageMenuItem.new(Gtk::Stock::REMOVE)
373
+ @bedit_list=Gtk::ImageMenuItem.new(Gtk::Stock::EDIT)
374
+ @bedit_list.signal_connect("activate"){list_edit}
375
+ @bsave_list=Gtk::ImageMenuItem.new(Gtk::Stock::SAVE)
376
+ @bsave_list.signal_connect("activate"){list_save}
377
+ @brm_list.signal_connect("activate"){list_rm(@list_selection)}
378
+ sep=Gtk::SeparatorMenuItem.new
379
+ @bedit2_list=Gtk::ImageMenuItem.new(_('Edit tag'))
380
+ @bedit2_list.image=Gtk::Image.new(Gtk::Stock::EDIT, Gtk::IconSize::MENU)
381
+ @bedit2_list.signal_connect("activate"){
382
+ s=@list_selection.selected_rows
383
+ unless s.empty?
384
+ i=s[-1].to_s.to_i
385
+ EditSongWindow.new(@playlist.songs[i], self, @config['name_format'])
386
+ end
387
+ }
388
+ @bopen=Gtk::ImageMenuItem.new(Gtk::Stock::OPEN)
389
+ @bopen.signal_connect("activate"){
390
+ s=@list_selection.selected_rows
391
+ unless s.empty?
392
+ i=s[-1].to_s.to_i
393
+ dir=File.dirname(@playlist.songs[i].path)
394
+ dir.gsub!(/(?=[{\[\ \]}*])/, '\\')#gsub(/(?=[{\[\]}*])/, '\\') -> escape { [ ] } *
395
+ ErrorError.new("Cannot open #{dir}", true) unless system("gnome-open #{dir}")
396
+ end
397
+ }
398
+
399
+ @cm_list=Gtk::Menu.new
400
+ [@badd_list, @brm_list, @bedit_list, @bsave_list, sep, @bedit2_list, @bopen].each{|b| @cm_list.append(b)}
401
+
402
+ @cm_list.show_all
403
+
404
+ #*********************Cd zone**********************************
405
+ if $cd
406
+ @wcd=Cd::Window.new(@config, @cdlist)
407
+ @wcd.view.signal_connect("row-activated"){|view, path, column|
408
+ @cdlist.current=path.to_s.to_i
409
+ stop
410
+ play
411
+ }
412
+
413
+ @bcdrefresh=Gtk::Button.new
414
+ @bcdrefresh.image=Gtk::Image.new(Gtk::Stock::REFRESH, Gtk::IconSize::BUTTON)
415
+ @bcdrefresh.signal_connect('clicked'){@cdlist.refresh; @wcd.refresh_store}##{Thread.new($stdout){@list.refresh_on;refresh_store}}
416
+ @bcdpreference=Gtk::Button.new
417
+ @bcdpreference.image=Gtk::Image.new(Gtk::Stock::PREFERENCES, Gtk::IconSize::BUTTON)
418
+ @bcdpreference.signal_connect('clicked'){
419
+ Preference_window.show(self, @playlist, @config, :cd, @cdlist, @cdplayer, @wcd)
420
+ }
421
+
422
+ @cdtable=Gtk::Table.new(4,2)
423
+ @cdtable.column_spacings=($border)
424
+ @cdtable.attach(@bcdrefresh, 0, 1, 1, 2, Gtk::SHRINK, Gtk::SHRINK)
425
+ @cdtable.attach(@bcdpreference, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK)
426
+ @cdtable.attach(@wcd, 1, 2, 0, 4)
427
+ end
428
+
429
+ #*********************Radio zone*******************************
430
+ if $radio
431
+ @wradio=Radio::Window.new(self, @radiolist)
432
+ @wradio.view.signal_connect("row-activated"){|view, path, column|
433
+ @radiolist.current=path.to_s.to_i
434
+ stop
435
+ play
436
+ }
437
+
438
+ @bradioadd=Gtk::Button.new
439
+ @bradioadd.image=Gtk::Image.new(Gtk::Stock::ADD, Gtk::IconSize::BUTTON)
440
+ @bradioadd.tooltip_text=_('Add a radio')
441
+ @bradioadd.signal_connect('clicked'){@wradio.add_radio}
442
+ @bradiorm=Gtk::Button.new
443
+ @bradiorm.image=Gtk::Image.new(Gtk::Stock::REMOVE, Gtk::IconSize::BUTTON)
444
+ @bradiorm.tooltip_text=_('Remove selection')
445
+ @bradiorm.signal_connect('clicked'){
446
+ current_removed=@wradio.rm
447
+ if @active_view=='radio'
448
+ @radioplayer.stop if current_removed
449
+ end
450
+ }
451
+ @bradioclear=Gtk::Button.new
452
+ @bradioclear.image=Gtk::Image.new(Gtk::Stock::CLEAR, Gtk::IconSize::BUTTON)
453
+ @bradioclear.tooltip_text=_('Clear radios list')
454
+ @bradioclear.signal_connect('clicked'){
455
+ @radioplayer.stop
456
+ @wradio.clear
457
+ }
458
+
459
+ @bradioedit=Gtk::Button.new
460
+ @bradioedit.image=Gtk::Image.new(Gtk::Stock::EDIT, Gtk::IconSize::BUTTON)
461
+ @bradioedit.tooltip_text=_('Edit a radio')
462
+ @bradioedit.signal_connect('clicked'){radios_edit}
463
+
464
+ @radiotable=Gtk::Table.new(6,2)
465
+ @radiotable.column_spacings=($border)
466
+ @radiotable.attach(@bradioadd, 0, 1, 1, 2, Gtk::SHRINK, Gtk::SHRINK)
467
+ @radiotable.attach(@bradiorm, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK)
468
+ @radiotable.attach(@bradioclear, 0, 1, 3, 4, Gtk::SHRINK, Gtk::SHRINK)
469
+ @radiotable.attach(@bradioedit, 0, 1, 4, 5, Gtk::SHRINK, Gtk::SHRINK)
470
+ @radiotable.attach(@wradio, 1, 2, 0, 6)
471
+ end
472
+
473
+ #*********************Player zone******************************
474
+ #View selection
475
+ #@icon_theme=Gtk::IconTheme.default
476
+ #@idir=@icon_theme.load_icon('gtk-directory', Gtk::IconSize::BUTTON, Gtk::IconTheme::LOOKUP_FORCE_SVG)
477
+ @viewstore=Gtk::ListStore.new(Gdk::Pixbuf)
478
+ ['sound_directory.svg','sound.svg','gtk-directory.svg','player_play.svg'].each{|image|
479
+ child=@viewstore.append
480
+ child[0]=Gdk::Pixbuf.new(File.join(Pix_dir, image), 24, 24)
481
+ }
482
+ if $cd
483
+ child=@viewstore.append
484
+ child[0]=Gdk::Pixbuf.new(File.join(Pix_dir, 'media-cdrom.svg'), 24, 24)
485
+ end
486
+ if $radio
487
+ child=@viewstore.append
488
+ child[0]=Gdk::Pixbuf.new(File.join(Pix_dir, 'network-wireless.svg'), 24, 24)
489
+ end
490
+ @renderer3=Gtk::CellRendererPixbuf.new
491
+ @selectview=Gtk::ComboBox.new(false)
492
+ @selectview.model=@viewstore
493
+ @selectview.pack_start(@renderer3,false)
494
+ @selectview.set_attributes(@renderer3, :pixbuf => 0)
495
+
496
+ case @config['view']
497
+ when 'playlist' then @selectview.active=View::PLAYLIST
498
+ when 'browser' then @selectview.active=View::BROWSER
499
+ when 'player' then @selectview.active=View::COMPACT
500
+ when 'cd' then @selectview.active=View::CD
501
+ when 'radio' then @selectview.active=View::RADIO
502
+ else @selectview.active=View::ALL
503
+ end
504
+ @selectview.signal_connect('changed'){change_view}
505
+ #Song title
506
+ @song_title=Gtk::Label.new
507
+ @song_title.wrap=true
508
+ @song_title.attributes=Pango::AttrList.new.insert(Pango::AttrWeight.new(Pango::WEIGHT_BOLD))
509
+ @fsong_title=Gtk::Frame.new
510
+ @fsong_title.add(@song_title)
511
+ #volume
512
+ @bvolume=Gtk::VolumeButton.new
513
+ @bvolume.value=@player.volume
514
+ @bvolume.signal_connect('value-changed'){
515
+ @player.volume=@bvolume.value
516
+ @cdplayer.volume=@bvolume.value if $cd
517
+ @radioplayer.volume=@bvolume.value if $radio
518
+ }
519
+
520
+ #buttons
521
+ @iprev=Gtk::Image.new(Gtk::Stock::MEDIA_PREVIOUS,Gtk::IconSize::BUTTON)
522
+ @bprev= Gtk::Button.new;@bprev.image=@iprev
523
+ @bprev.signal_connect('clicked'){previous_song}
524
+ @istop=Gtk::Image.new(Gtk::Stock::MEDIA_STOP,Gtk::IconSize::BUTTON)
525
+ @bstop=Gtk::Button.new;@bstop.image=@istop
526
+ @bstop.signal_connect('clicked'){stop}
527
+ @iplay=Gtk::Image.new(Gtk::Stock::MEDIA_PLAY,Gtk::IconSize::BUTTON)
528
+ @bplay= Gtk::Button.new;@bplay.image=@iplay
529
+ @bplay.signal_connect('clicked'){play}
530
+ @ipause=Gtk::Image.new(Gtk::Stock::MEDIA_PAUSE,Gtk::IconSize::BUTTON)
531
+ @bpause=Gtk::Button.new;@bpause.image=@ipause
532
+ @bpause.signal_connect('clicked'){pause}
533
+ @inext=Gtk::Image.new(Gtk::Stock::MEDIA_NEXT,Gtk::IconSize::BUTTON)
534
+ @bnext= Gtk::Button.new;@bnext.image=@inext
535
+ @bnext.signal_connect('clicked'){next_song}
536
+ #time
537
+ @lposition=Gtk::Label.new('--:--')
538
+ @lposbox=Gtk::EventBox.new
539
+ @lposbox.events=Gdk::Event::BUTTON_PRESS_MASK
540
+ @lposbox.add(@lposition)
541
+ @lposbox.signal_connect('button_press_event'){|widget, event|
542
+ if event.button==1
543
+ @config['remaining_time']=!@config['remaining_time']
544
+ end
545
+ }
546
+ @sposition=Gtk::HScale.new
547
+ @sposition.draw_value=false
548
+ @sposition.signal_connect('button_press_event'){@seeking=true;false}
549
+ @sposition.signal_connect('button_release_event'){
550
+ case active_player
551
+ when 'default'
552
+ player=@player
553
+ when 'cd'
554
+ player=@cdplayer
555
+ when 'radio'
556
+ player=nil
557
+ end
558
+ player.position=@sposition.value.to_i if player
559
+ @seeking=false
560
+ }
561
+ @lduration=Gtk::Label.new('--:--')
562
+ #containers
563
+ @title_container=Gtk::HBox.new(false)
564
+ @title_container.pack_start(@selectview, false, false)
565
+ @title_container.pack_start(@fsong_title, true, true)
566
+ @title_container.pack_start(@bvolume, false, false)
567
+
568
+ @button_container=Gtk::HBox.new(false)
569
+ @button_container.pack_start(@bprev, false, false)
570
+ @button_container.pack_start(@bstop, false, false)
571
+ @button_container.pack_start(@bplay, false, false)
572
+ @button_container.pack_start(@bpause, false, false)
573
+ @button_container.pack_start(@bnext, false, false)
574
+ @button_container.pack_start(@lposbox, false, false)
575
+ @button_container.pack_start(@sposition, true, true)
576
+ @button_container.pack_start(@lduration, false, false)
577
+
578
+ #**********************Key bindings***************************
579
+ @accel=Gtk::AccelGroup.new
580
+ @accel.connect(Gdk::Keyval::GDK_KEY_p, Gdk::Window::CONTROL_MASK,Gtk::ACCEL_VISIBLE){playpause}
581
+ @accel.connect(Gdk::Keyval::GDK_KEY_s, Gdk::Window::CONTROL_MASK,Gtk::ACCEL_VISIBLE){stop}
582
+ @accel.connect(Gdk::Keyval::GDK_KEY_b, Gdk::Window::CONTROL_MASK,Gtk::ACCEL_VISIBLE){previous_song}
583
+ @accel.connect(Gdk::Keyval::GDK_KEY_n, Gdk::Window::CONTROL_MASK,Gtk::ACCEL_VISIBLE){next_song}
584
+ @accel.connect(Gdk::Keyval::GDK_KEY_Down, Gdk::Window::CONTROL_MASK,Gtk::ACCEL_VISIBLE){volume_down}
585
+ @accel.connect(Gdk::Keyval::GDK_KEY_Up, Gdk::Window::CONTROL_MASK,Gtk::ACCEL_VISIBLE){volume_up}
586
+ @accel.connect(Gdk::Keyval::GDK_KEY_f, Gdk::Window::CONTROL_MASK,Gtk::ACCEL_VISIBLE){
587
+ @editing ? @edit_wrapper.search : search
588
+ }
589
+ @accel.connect(Gdk::Keyval::GDK_KEY_d, Gdk::Window::CONTROL_MASK,Gtk::ACCEL_VISIBLE){focus_to_current}
590
+ @accel.connect(Gdk::Keyval::GDK_KEY_F1, nil, Gtk::ACCEL_VISIBLE){help}
591
+ @accel.connect(Gdk::Keyval::GDK_KEY_Delete, nil, Gtk::ACCEL_VISIBLE){
592
+ list_rm(@list_selection) if @list_view.has_focus?
593
+ if @edit_wrapper
594
+ @edit_wrapper.rm_song if @edit_wrapper.has_focus?
595
+ end
596
+ }
597
+
598
+ #*********************Main window******************************
599
+ @table=Gtk::Table.new(8, 2)
600
+ @table.column_spacings=($border)
601
+ @table.attach(@badd, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK)
602
+ @table.attach(@brm, 0, 1, 3, 4, Gtk::SHRINK, Gtk::SHRINK)
603
+ @table.attach(@bclear, 0, 1, 4, 5, Gtk::SHRINK, Gtk::SHRINK)
604
+ @table.attach(@bedit, 0, 1, 5, 6, Gtk::SHRINK, Gtk::SHRINK)
605
+ @table.attach(@bfind, 0, 1, 6, 7, Gtk::SHRINK, Gtk::SHRINK)
606
+ @table.attach(@wlist, 1, 2, 1, 8)
607
+
608
+ @paned=Gtk::HPaned.new
609
+ @paned.position=@config['paned_position']
610
+ @paned.add1(@wtree)
611
+ @paned.add2(@table)
612
+
613
+ @vbox=Gtk::VBox.new
614
+ @vbox.pack_start(@menu_bar, false, false, 0)
615
+ @vbox.pack_start(@paned)
616
+ @vbox.pack_start(@cdtable) if $cd
617
+ @vbox.pack_start(@radiotable) if $radio
618
+ @vbox.pack_start(@title_container, false, false)
619
+ @vbox.pack_start(@button_container, false, false)
620
+
621
+ super()
622
+ self.signal_connect("delete_event") {quit; false}
623
+ self.title=$name
624
+ @icon_Z=Gdk::Pixbuf.new(File.join(Pix_dir,'ZiK.svg'))
625
+ self.icon=(@icon_Z)
626
+ self.add_accel_group(@accel)
627
+
628
+ self.resize(@config['width'],@config['height'])
629
+ self.move(@config['x'],@config['y'])
630
+
631
+ self.add(@vbox)
632
+
633
+ @vbox.show_all
634
+ change_view
635
+ self.show
636
+ end
637
+
638
+ private
639
+
640
+ def change_view
641
+ case @selectview.active
642
+ when View::ALL
643
+ @paned.show; @paned.child1.show; @paned.child2.show
644
+ @badd.sensitive=true
645
+ @playlist_it.show; @broswe_it.show
646
+ @cdtable.hide if $cd
647
+ @radiotable.hide if $radio
648
+ @radiolist_it.hide if $radio
649
+ self.resize(self.size[0],@config['height']) if @config['view']=='player'
650
+ @config['view']='all'
651
+ @active_view='default'
652
+ when View::PLAYLIST
653
+ @paned.show; @paned.child1.hide; @paned.child2.show
654
+ @badd.sensitive=false
655
+ @playlist_it.show; @broswe_it.hide
656
+ @cdtable.hide if $cd
657
+ @radiotable.hide if $radio
658
+ @radiolist_it.hide if $radio
659
+ self.resize(self.size[0],@config['height']) if @config['view']=='player'
660
+ @config['view']='playlist'
661
+ @active_view='default'
662
+ when View::BROWSER
663
+ @paned.show; @paned.child1.show; @paned.child2.hide
664
+ @playlist_it.hide; @broswe_it.show
665
+ @cdtable.hide if $cd
666
+ @radiotable.hide if $radio
667
+ @radiolist_it.hide if $radio
668
+ self.resize(self.size[0],@config['height']) if @config['view']=='player'
669
+ @config['view']='browser'
670
+ @active_view='default'
671
+ when View::COMPACT
672
+ @paned.hide
673
+ @playlist_it.hide; @broswe_it.hide
674
+ @cdtable.hide if $cd
675
+ @radiotable.hide if $radio
676
+ @radiolist_it.hide if $radio
677
+ s=self.size; self.resize(s[0],1)
678
+ @config['height']=s[1]
679
+ @config['view']='player'
680
+ when View::CD
681
+ @paned.hide; @cdtable.show
682
+ @playlist_it.hide; @broswe_it.hide
683
+ @radiotable.hide if $radio
684
+ @radiolist_it.hide if $radio
685
+ self.resize(self.size[0],@config['height']) if @config['view']=='player'
686
+ @config['view']='cd'
687
+ @active_view='cd'
688
+ when View::RADIO
689
+ @paned.hide
690
+ @playlist_it.hide; @broswe_it.hide
691
+ @radiotable.show
692
+ @radiolist_it.show
693
+ @cdtable.hide if $cd
694
+ self.resize(self.size[0],@config['height']) if @config['view']=='player'
695
+ @config['view']='radio'
696
+ @active_view='radio'
697
+ end
698
+ end
699
+
700
+ public
701
+
702
+ def quit
703
+ pos=self.position;@config['x']=pos[0];@config['y']=pos[1]
704
+ s=self.size;@config['width']=s[0];@config['height']=s[1] unless @selectview.active==View::COMPACT
705
+ @config['paned_position']=@paned.position
706
+
707
+ Gtk.main_quit
708
+ end
709
+
710
+ def play(player_wanted=@active_view)
711
+ case player_wanted
712
+ when 'default'
713
+ if $cd
714
+ @cdplayer.stop if @cdplayer.playing?
715
+ end
716
+ if $radio
717
+ @radioplayer.stop if @radioplayer.playing?
718
+ end
719
+ playlist=@playlist
720
+ song=@playlist.songs[@playlist.current]
721
+ player=@player
722
+ store=@list_store
723
+ when 'cd'
724
+ @player.stop if @player.playing?
725
+ if $radio
726
+ @radioplayer.stop if @radioplayer.playing?
727
+ end
728
+ playlist=@cdlist
729
+ song=@cdlist.songs[@cdlist.current]
730
+ player=@cdplayer
731
+ store=@wcd.store
732
+ when 'radio'
733
+ @player.stop if @player.playing?
734
+ if $cd
735
+ @cdplayer.stop if @cdplayer.playing?
736
+ end
737
+ playlist=@radiolist
738
+ song=@radiolist.radios[@radiolist.current]
739
+ player=@radioplayer
740
+ store=@wradio.store
741
+ end
742
+
743
+ if song.nil?
744
+ ErrorError.new("Song n°#{playlist.current} do not exists.")
745
+ else
746
+ player.play(song)
747
+ self.signal_emit('play', song)
748
+ @song_title.text="#{song.name}"
749
+ duration=song.duration
750
+ if duration <= 0
751
+ @lduration.text='--:--'
752
+ @sposition.set_range(0,1200)#Use 20 minutes if song's duration is not known.
753
+ GLib::Timeout.add(600){get_gst_duration(player, song)}#if taglib cannot give the track duration, let's try GStreamer
754
+ elsif duration < 3600
755
+ @lduration.text=Time.at(duration).strftime("%M:%S")
756
+ @sposition.set_range(0,duration)
757
+ else
758
+ @lduration.text=(duration.to_i/3600).to_s+":"+Time.at(duration).strftime("%M:%S")
759
+ @sposition.set_range(0,duration)
760
+ end
761
+ store.each{|model, path, iter|
762
+ if path.to_s.to_i==playlist.current
763
+ iter[1]=true
764
+ else
765
+ iter[1]=false
766
+ end
767
+ }
768
+ nb_timeout=@nb_timeout+=1
769
+ GLib::Timeout.add(400){
770
+ position=player.position
771
+ if position <= 0
772
+ @lposition.text="--:--"
773
+ else
774
+ duration=song.duration#get an uptodate duration
775
+ if duration <= 0
776
+ @lposition.text=Time.at(position).strftime("%M:%S")
777
+ elsif duration < 3600
778
+ if @config['remaining_time']
779
+ t=duration-position; format="-%M:%S"
780
+ else
781
+ t=position; format="%M:%S"
782
+ end
783
+ @lposition.text=Time.at(t).strftime(format)
784
+ else
785
+ if @config['remaining_time']
786
+ t=duration-position
787
+ format="-"
788
+ else
789
+ t=position
790
+ format=""
791
+ end
792
+ @lposition.text=format+(t.to_i/3600).to_s+":"+Time.at(t).strftime("%M:%S")
793
+ end
794
+ end
795
+ @sposition.value=position unless @seeking
796
+ player.playing? && @nb_timeout==nb_timeout
797
+ }
798
+ if player_wanted=='radio'
799
+ @bprev.sensitive=false
800
+ @bnext.sensitive=false
801
+ else
802
+ @bprev.sensitive=true
803
+ @bnext.sensitive=true
804
+ end
805
+ end
806
+ end
807
+
808
+ private
809
+
810
+ def get_gst_duration(player, song, tryagain=true)
811
+ duration=player.duration
812
+ if duration>0
813
+ song.duration=duration
814
+ if duration < 3600
815
+ @lduration.text=Time.at(duration).strftime("%M:%S")
816
+ else
817
+ @lduration.text=(duration.to_i/3600).to_s+":"+Time.at(duration).strftime("%M:%S")
818
+ end
819
+ @sposition.set_range(0,duration)
820
+ else
821
+ GLib::Timeout.add(4000){get_gst_duration(player, song, false)} if tryagain
822
+ end
823
+ false
824
+ end
825
+
826
+ public
827
+
828
+ def stop
829
+ @player.stop
830
+ @cdplayer.stop if $cd
831
+ @radioplayer.stop if $radio
832
+ self.signal_emit('stop')
833
+ end
834
+
835
+ def pause
836
+ @player.pause
837
+ if $cd
838
+ @cdplayer.pause
839
+ end
840
+ if $radio
841
+ @radioplayer.pause
842
+ end
843
+ end
844
+
845
+ def active_player
846
+ active_player=nil
847
+ active_player='default' if @player.playing?
848
+ active_player='cd' if $cd&&@cdplayer.playing?
849
+ active_player='radio' if $radio&&@radioplayer.playing?
850
+ active_player=@active_view unless active_player
851
+ active_player
852
+ end
853
+
854
+ def playing?
855
+ return true if @player.playing?
856
+ return true if $cd&&@cdplayer.playing?
857
+ return true if $radio&&@radioplayer.playing?
858
+ false
859
+ end
860
+
861
+ def playpause
862
+ playing? ? pause : play
863
+ end
864
+
865
+ def previous_song
866
+ player=active_player
867
+ case player
868
+ when 'default'
869
+ playlist=@playlist
870
+ when 'cd'
871
+ playlist=@cdlist
872
+ when 'radio'
873
+ return
874
+ end
875
+ if playlist.previous_song
876
+ stop
877
+ play(player)
878
+ else
879
+ ErrorInfo.new("Begining of the playlist.")
880
+ end
881
+ end
882
+
883
+ def next_song
884
+ player=active_player
885
+ case player
886
+ when 'default'
887
+ playlist=@playlist
888
+ when 'cd'
889
+ playlist=@cdlist
890
+ when 'radio'
891
+ return
892
+ end
893
+ if playlist.next_song
894
+ stop
895
+ play(player)
896
+ else
897
+ ErrorInfo.new("End of the playlist.")
898
+ end
899
+ end
900
+
901
+ def next_song_on_error
902
+ unless @first_song_error
903
+ @first_song_error=@playlist.current
904
+ next_song
905
+ else
906
+ if @playlist.current==@first_song_error
907
+ ErrorError.new("All tracks seems to be inaccessible.")
908
+ else
909
+ next_song
910
+ end
911
+ end
912
+ end
913
+
914
+ def update_gst_registry
915
+ label=Gtk::Label.new(_("Loading new plugins. Please wait..."))
916
+ window=Gtk::Window.new
917
+ window.modal=true
918
+ window.transient_for=self
919
+ window.signal_connect("delete_event") {true}
920
+ window.add(label)
921
+ window.show_all
922
+ stop
923
+ Gst::Registry.update
924
+ window.destroy
925
+ play
926
+ end
927
+
928
+ def volume_up
929
+ inc=@bvolume.adjustment.page_increment
930
+ if @bvolume.value>1-inc
931
+ @bvolume.value=1
932
+ else
933
+ @bvolume.value+=inc
934
+ end
935
+ end
936
+
937
+ def volume_down
938
+ inc=@bvolume.adjustment.page_increment
939
+ if @bvolume.value<inc
940
+ @bvolume.value=0
941
+ else
942
+ @bvolume.value-=inc
943
+ end
944
+ end
945
+
946
+ def shuffle=(tf)
947
+ @config['shuffle']=tf
948
+ @shuffle_it.active=tf
949
+ @playlist.shuffle=tf
950
+ @cdlist.shuffle=tf if $cd
951
+ end
952
+
953
+ def repeat=(tf)
954
+ @config['repeat']=tf
955
+ @repeat_it.active=tf
956
+ @playlist.repeat=tf
957
+ @cdlist.repeat=tf if $cd
958
+ end
959
+
960
+ def refresh_treestore(directories, extension, start=false)
961
+ ErrorInfo.new('Refreshing tree view...')
962
+ self.cursor=Gdk::Cursor::WATCH
963
+
964
+ backup=File.join(User_dir,'tree')
965
+ unless File.exist?(backup)
966
+ Dir.mkdir(backup) if $saveconfig
967
+ end
968
+ BrowseSearchWindow.refresh
969
+ @treestore.clear
970
+
971
+ rexp=Regexp.new('.*\.('+extension.gsub(',','|')+')$', Regexp::IGNORECASE)
972
+ directories.split(',').each_with_index{|directory, nb|
973
+ if File.directory?(directory)
974
+
975
+ fback=File.join(backup, nb.to_s)
976
+ if (not @config['refresh_on_start'] and File.readable?(fback) and start)
977
+ #Read the list of files
978
+ f=File.open(fback,'r')
979
+ files=f.readlines
980
+ f.close
981
+ else
982
+ #List all music files
983
+ files=[]
984
+ Find.find(directory){|f| files.push(f) if f=~rexp}
985
+ @config['sort_case_sensitive'] ? files.sort! : files=files.sort_by{|f| f.downcase}
986
+ if $saveconfig
987
+ if @config['refresh_on_start']
988
+ #Delete list
989
+ ff=Dir.glob(File.join(backup,'*'))
990
+ File.delete(*ff)
991
+ else
992
+ #Save the list
993
+ f=File.open(fback,'w')
994
+ f.puts files
995
+ f.close
996
+ end
997
+ end
998
+ end
999
+
1000
+ #Add root directory
1001
+ root=@treestore.append(nil)
1002
+ root[0]=directory;root[1]=directory
1003
+ hiter={directory=>root}
1004
+
1005
+ if @config['sort_directories_first']
1006
+ files.each{|file|
1007
+ dirname=File.dirname(file)
1008
+ refresh_treestore_dirs(hiter, dirname)
1009
+ }
1010
+ files.each{|file|
1011
+ ext=File.extname(file);name=File.basename(file,ext);dirname=File.dirname(file)
1012
+ refresh_treestore_file(file, name, hiter[dirname])
1013
+ }
1014
+ else
1015
+ files.each{|file|
1016
+ ext=File.extname(file);name=File.basename(file,ext);dirname=File.dirname(file)
1017
+ refresh_treestore_dirs(hiter, dirname)#Create directories from root directory to file
1018
+ refresh_treestore_file(file, name, hiter[dirname])#Add file
1019
+ }
1020
+ end
1021
+ else
1022
+ printf("#{directory} is not a directory...")
1023
+ end
1024
+ }
1025
+ self.cursor=Gdk::Cursor::ARROW
1026
+ ErrorInfo.new('Refreshing done.')
1027
+ end
1028
+
1029
+ private
1030
+
1031
+ def refresh_treestore_dirs(hiter, dir)
1032
+ dirs=[]
1033
+ until ancestor=hiter[dir]
1034
+ dirs.push(dir)
1035
+ dir=File.expand_path(File.join(dir,'..'))
1036
+ end
1037
+ dirs.reverse!.each{|dir|
1038
+ child=@treestore.append(hiter[File.expand_path(File.join(dir,'..'))])
1039
+ child[0]=File.basename(dir);child[1]=dir
1040
+ hiter[dir]=child
1041
+ }
1042
+ end
1043
+
1044
+ def refresh_treestore_file(file, name, iter)
1045
+ child=@treestore.append(iter)
1046
+ child[0]=name;child[1]=file
1047
+ end
1048
+
1049
+ public
1050
+
1051
+ def compact(tf, scale_factor=0.7)
1052
+ #Must be called at initialization time (to set @hcompact)
1053
+ @hcompact={:weight=>@renderer2.weight, :ypad=>@renderer2.ypad,
1054
+ :spacing=>@col2.spacing} unless @hcompact
1055
+ if tf
1056
+ @renderer2.scale=scale_factor
1057
+ @renderer2.weight=@hcompact[:weight]/(scale_factor**0.5)
1058
+ @renderer2.ypad=0
1059
+ @col2.spacing=0
1060
+ else
1061
+ @renderer2.scale=1
1062
+ @renderer2.weight=@hcompact[:weight]
1063
+ @renderer2.ypad=@hcompact[:ypad]
1064
+ @col2.spacing=@hcompact[:spacing]
1065
+ end
1066
+ @col2.queue_resize
1067
+ end
1068
+
1069
+ def refresh_liststore
1070
+ @list_store.clear
1071
+ i=0
1072
+ @playlist.songs.each{|song|
1073
+ child=@list_store.append
1074
+ child[0]=song.name
1075
+ @playlist.current==i ? child[1]=true : child[1]=false
1076
+ i+=1
1077
+ }
1078
+ if active_player=='default'&&!@song_title.nil?#@song_title is not defined when Gui object is created
1079
+ song=@playlist.songs[@playlist.current]
1080
+ @song_title.text="#{song.name}" unless song.nil?
1081
+ end
1082
+ end
1083
+
1084
+ def focus_to_current
1085
+ @list_view.scroll_to_cell(Gtk::TreePath.new(@playlist.current.to_s), nil, true, 0.5, 0.5)
1086
+ end
1087
+
1088
+ def search(songs=@playlist.songs, selection=@list_selection, view=@list_view)
1089
+ proc=Proc.new{
1090
+ @wsearch=ListSearchWidget.new(songs, selection, view)
1091
+ if @editing
1092
+ @table.attach(@wsearch, 2, 8, 0, 1, Gtk::FILL, Gtk::SHRINK)
1093
+ else
1094
+ @table.attach(@wsearch, 1, 2, 0, 1, Gtk::FILL, Gtk::SHRINK)
1095
+ end
1096
+ }
1097
+ unless @wsearch
1098
+ proc.call
1099
+ else
1100
+ proc.call if @wsearch.destroyed?
1101
+ end
1102
+ @wsearch.focus
1103
+ end
1104
+
1105
+ def refresh_playlist(new,force=false)
1106
+ unless new.songs==@playlist.songs
1107
+ if new.empty?
1108
+ @playlist.clear
1109
+ else
1110
+ @playlist.change_to(new)
1111
+ force=true
1112
+ end
1113
+ end
1114
+ refresh_liststore if force
1115
+ end
1116
+
1117
+ def import_list
1118
+ dialog=Gtk::FileChooserDialog.new(_("Import a file"), self,
1119
+ Gtk::FileChooser::ACTION_OPEN, nil,
1120
+ [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL], [Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT])
1121
+ dialog.select_multiple=true
1122
+ dialog.show_hidden=false
1123
+ filter=Gtk::FileFilter.new
1124
+ filter.add_pattern('*.xspf');filter.add_pattern('*.m3u');filter.add_pattern('*.pls')
1125
+ filter.add_mime_type('audio/*')
1126
+ dialog.filter=filter
1127
+ if dialog.run==Gtk::Dialog::RESPONSE_ACCEPT
1128
+ dialog.filenames.each{|file| @playlist.add(file, @config['extension'])}
1129
+ refresh_liststore
1130
+ end
1131
+ dialog.destroy
1132
+ end
1133
+
1134
+ def list_save
1135
+ dialog=Gtk::FileChooserDialog.new(_("Save playlist"), self,
1136
+ Gtk::FileChooser::ACTION_SAVE, nil,
1137
+ [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL], [Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT])
1138
+ dialog.do_overwrite_confirmation=true
1139
+ dialog.filter=Gtk::FileFilter.new.add_pattern('*.m3u').add_pattern('*.xspf')
1140
+ dialog.show_hidden=false
1141
+ if dialog.run==Gtk::Dialog::RESPONSE_ACCEPT
1142
+ f=dialog.filename
1143
+ case
1144
+ when f=~/.*\.xspf$/i
1145
+ @playlist.save_xspf(f, @config['relative_playlist'])
1146
+ when f=~/.*\.m3u$/i
1147
+ @playlist.save_m3u(f, @config['relative_playlist'])
1148
+ else
1149
+ @playlist.save_m3u(f+'.m3u', @config['relative_playlist'])
1150
+ end
1151
+ end
1152
+ dialog.destroy
1153
+ end
1154
+
1155
+ def export_songs
1156
+ dialog=Gtk::FileChooserDialog.new(_("Export songs"), self,
1157
+ Gtk::FileChooser::ACTION_SAVE, nil,
1158
+ [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
1159
+ [Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT])
1160
+ dialog.do_overwrite_confirmation=true
1161
+ dialog.filter=Gtk::FileFilter.new.add_pattern('*.m3u').add_pattern('*.xspf')
1162
+ b=Gtk::RadioButton.new(_("Export all songs in this directory.")).show
1163
+ Gtk::RadioButton.new(b, _("Recreate songs tree under the destination directory.")).show
1164
+ dialog.extra_widget=hpack(b.group)
1165
+ if dialog.run==Gtk::Dialog::RESPONSE_ACCEPT
1166
+ f=dialog.filename
1167
+ if b.active?
1168
+ @exportlist=ExportList.new(@playlist.songs, f)
1169
+ else
1170
+ @exportlist=ExportList.new(@playlist.songs, f, @config['directory'])
1171
+ end
1172
+ @exportlist.write_plalist
1173
+ ErrorInfo.new('Copying audio files...')
1174
+ nb=@exportlist.nb.to_f
1175
+ wprogress=ProgressWindow.new(_("Copying audio files..."), self)
1176
+ wprogress.text="0/"+"#{nb.to_i}"
1177
+ require 'thread'; queue=Queue.new
1178
+ @exportlist.export(queue)
1179
+ GLib::Timeout.add_seconds(1){
1180
+ li=nil; error=nil
1181
+ until queue.empty?
1182
+ case o=queue.pop
1183
+ when Fixnum
1184
+ li=o
1185
+ else
1186
+ error=o
1187
+ break
1188
+ end
1189
+ end
1190
+ if li
1191
+ wprogress.fraction=(li+1)/nb
1192
+ wprogress.text="#{(li+1)}"+"/"+"#{nb.to_i}"
1193
+ end
1194
+ if error
1195
+ wprogress.destroy
1196
+ if error.kind_of?(ErrorsStock)
1197
+ o.show_all(n_("An error occured while exporting songs.\n",
1198
+ "Some errors occured while exporting songs.\n", o.nb))
1199
+ else
1200
+ ErrorError.new(
1201
+ _("An error occured while exporting songs.\nUnknown queued object type (#{o.class})."), true)
1202
+ end
1203
+ false
1204
+ else
1205
+ true
1206
+ end
1207
+ }
1208
+ end
1209
+ dialog.destroy
1210
+ end
1211
+
1212
+ def abort_export_songs
1213
+ @exportlist.abort_export
1214
+ end
1215
+
1216
+ def list_add(selection, extension)
1217
+ selection.selected_each{|model, path, iter| @playlist.add(iter[1], extension)}
1218
+ refresh_liststore
1219
+ end
1220
+
1221
+ def list_add_at(selection, extension, position)
1222
+ elist=Editlist.new([], @config['name_format'])
1223
+ selection.selected_each{|model, path, iter| elist.add(iter[1], extension)}
1224
+ elist.merge(@playlist, position)
1225
+ @playlist.change_to(elist)
1226
+ refresh_liststore
1227
+ end
1228
+
1229
+ def list_add_uris(uris, extension)
1230
+ uris.each{|uri| @playlist.add(GLib.filename_from_uri(uri)[0], extension)}
1231
+ refresh_liststore
1232
+ end
1233
+
1234
+ def list_add_uris_at(uris, extension, position)
1235
+ elist=Editlist.new([], @config['name_format'])
1236
+ uris.each{|uri| elist.add(GLib.filename_from_uri(uri)[0], extension)}
1237
+ elist.merge(@playlist, position)
1238
+ @playlist.change_to(elist)
1239
+ refresh_liststore
1240
+ end
1241
+
1242
+ def list_rm(selection)
1243
+ i=[]
1244
+ selection.selected_each{|model, path, iter| i.push(path.to_s.to_i)}
1245
+ i=i.sort {|x,y| y<=> x}
1246
+ i.each{|num|
1247
+ current_removed=@playlist.rm(num)
1248
+ @player.stop if current_removed
1249
+ }
1250
+ refresh_liststore
1251
+ end
1252
+
1253
+ def list_clear
1254
+ @player.stop
1255
+ @playlist.clear
1256
+ refresh_liststore
1257
+ end
1258
+
1259
+ def list_edit
1260
+ @editing=true
1261
+ begin; @wsearch.destroy; rescue; end
1262
+ elist=Editlist.new(@playlist.songs, @config['name_format'])
1263
+ @edit_wrapper=EditListWrapper.new(self, elist, @config)
1264
+ bcancel=Gtk::Button.new(Gtk::Stock::CANCEL)
1265
+ bok=Gtk::Button.new(Gtk::Stock::OK)
1266
+ pquit=Proc.new{
1267
+ @bedit.sensitive=true
1268
+ @wlist.show
1269
+ @edit_wrapper.destroy
1270
+ bcancel.destroy
1271
+ bok.destroy
1272
+ begin; @wsearch.destroy; rescue; end
1273
+ @playlist_it.set_submenu(@playlist_menu)
1274
+ @table.resize(8, 2)
1275
+ @editing=false
1276
+ }
1277
+ pcancel=Proc.new{
1278
+ if @edit_wrapper.force_refresh_parent
1279
+ @edit_wrapper.refresh_list
1280
+ refresh_liststore
1281
+ end
1282
+ }
1283
+ pok=Proc.new{
1284
+ @edit_wrapper.refresh_list
1285
+ refresh_playlist(@edit_wrapper.list, @edit_wrapper.force_refresh_parent)
1286
+ }
1287
+ bcancel.signal_connect('clicked'){
1288
+ pcancel.call
1289
+ pquit.call
1290
+ }
1291
+ bcancel.show
1292
+ bok.signal_connect('clicked'){
1293
+ pok.call
1294
+ pquit.call
1295
+ }
1296
+ bok.show
1297
+ cancel_it=Gtk::ImageMenuItem.new(Gtk::Stock::CANCEL)
1298
+ cancel_it.signal_connect('activate'){pcancel.call; pquit.call}
1299
+ ok_it=Gtk::ImageMenuItem.new(Gtk::Stock::OK)
1300
+ ok_it.signal_connect('activate'){pok.call; pquit.call}
1301
+ playlist_menu=Gtk::Menu.new
1302
+ playlist_menu.append(cancel_it)
1303
+ playlist_menu.append(ok_it)
1304
+ playlist_menu.show_all
1305
+ @playlist_it.set_submenu(playlist_menu)
1306
+ @table.attach(@edit_wrapper.table, 2, 8, 1, 8)
1307
+ @table.attach(@edit_wrapper.badd, 3, 4, 8, 9, Gtk::SHRINK, Gtk::SHRINK)
1308
+ @table.attach(@edit_wrapper.bsave, 4, 5, 8, 9, Gtk::SHRINK, Gtk::SHRINK)
1309
+ @table.attach(bcancel, 6, 7, 8, 9, Gtk::SHRINK, Gtk::SHRINK)
1310
+ @table.attach(bok, 7, 8, 8, 9, Gtk::SHRINK, Gtk::SHRINK)
1311
+ @bedit.sensitive=false
1312
+ @wlist.hide
1313
+ dialog=Gtk::MessageDialog.new(self, Gtk::Dialog::MODAL, Gtk::MessageDialog::INFO,
1314
+ Gtk::MessageDialog::BUTTONS_CLOSE,
1315
+ _("Do not forget to validate your changes after editing the playlist."))
1316
+ dialog.run; dialog.destroy
1317
+ end
1318
+
1319
+ if $radio
1320
+ def radios_edit
1321
+ elist=Radio::EditList.new(@radiolist.radios)
1322
+ Radio::Edit.new(self, elist, @wradio)
1323
+ end
1324
+
1325
+ def refresh_radiolist(new, force)
1326
+ unless new.radios==@radiolist.radios
1327
+ if new.radios.empty?
1328
+ @radiolist.clear
1329
+ else
1330
+ @radiolist.change_to(new)
1331
+ force=true
1332
+ end
1333
+ end
1334
+ @wradio.refresh_store if force
1335
+ end
1336
+ end
1337
+
1338
+ def cursor=(c)
1339
+ self.window.cursor=Gdk::Cursor.new(c)
1340
+ end
1341
+
1342
+ def help
1343
+ case ENV['LANG']
1344
+ when /^fr/i then index='html/fr/index.html'
1345
+ else index='html/index.html'
1346
+ end
1347
+ unless system("gnome-open #{File.join(Doc_dir,index)}")
1348
+ ErrorError.new(_("Cannot open #{File.join(Doc_dir,index)}"), true)
1349
+ unless index=='html/index.html'
1350
+ unless system("gnome-open #{File.join(Doc_dir,'html/index.html')}")
1351
+ ErrorError.new(_("Cannot open #{File.join(Doc_dir,'html/index.html')}"), true)
1352
+ end
1353
+ end
1354
+ end
1355
+ end
1356
+
1357
+ def about
1358
+ begin
1359
+ f=File.open(File.join(Doc_dir,'COPYING'))
1360
+ copying=''
1361
+ while line=f.gets
1362
+ copying+=line
1363
+ end
1364
+ rescue
1365
+ copying="Error: license file was not found.\n
1366
+ The GNU General Public License, version 2 can be found at:\nhttp://www.gnu.org/licenses/gpl-2.0.html"
1367
+ ensure
1368
+ f.close unless f.nil?
1369
+ end
1370
+ authors=["Vincent Carmona <vinc4mai@gmail.com>"]
1371
+ if $module
1372
+ 3.times{authors.push("")}; authors.push("Modules"); authors.push("")
1373
+ Mod.all.each{|mod| authors.push(" "+mod.title+": "+mod.author)}
1374
+ end
1375
+ Gtk::AboutDialog.show(self, {:name=>$name, :version=>Version, :program_name=>$name,
1376
+ :copyright=>'Copyright 2007-2010 Vincent Carmona', :comments=>_('Audio player'),
1377
+ :website=>'http://zik.rubyforge.org/', :license=>copying,
1378
+ :authors=>authors,
1379
+ :artists=>["Jakub Steiner", "Lapo Calamandrei", "Andreas Nilsson", "Rodney Dawes", "Vincent Carmona"],
1380
+ :translator_credits=>"Français: Vincent Carmona\n",
1381
+ :logo=>Gdk::Pixbuf.new(File.join(Pix_dir,'ZiK3.png'))})
1382
+ end
1383
+ end