zik 0.16.1

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