5726f6d8b5d6d7620aa9f5bfa2faee128d91a104
[euphoria.git] / bin / euphoria
1 #!/usr/bin/ruby -w
2
3 # $Id: euphoria,v 1.41 2006/09/07 13:38:26 tilman Exp $
4
5 require "ecore"
6 require "ecore_x"
7 require "ecore_evas"
8 require "edje"
9 require "esmart"
10 require "esmart_draggies"
11 require "esmart_container"
12 require "xmmsclient"
13 require "xmmsclient_ecore"
14 require "singleton"
15 require "yaml"
16 require "find"
17 require "fileutils"
18
19 require "euphoria/playlist"
20 require "euphoria/playlist_item"
21 require "euphoria/slider"
22 require "euphoria/volume_box"
23 require "euphoria/playtime_updater"
24 require "euphoria/playback_status_handler"
25 require "euphoria/file_dialog"
26
27 Config = Struct.new("Config", :ipc_path, :theme, :time_display_elapsed)
28
29 # backwards compatibility w/ DrFeelgood and earlier
30 unless Xmms.respond_to?(:userconfdir)
31         def Xmms.userconfdir
32                 File.join(File.expand_path("~"), ".xmms2")
33         end
34 end
35
36 class Euphoria
37         DATA_DIR = "/usr/local/share/euphoria"
38         DATA_DIR_PRIV = File.join(Xmms.userconfdir,
39                                   "clients",
40                                   "euphoria")
41
42         include Singleton
43
44         attr_reader :xmms, :ee, :edje
45
46         def initialize
47                 @edje = nil
48                 @xmms = nil
49                 @volume_box = nil
50
51                 # settings
52                 unless File.directory?(DATA_DIR_PRIV)
53                         FileUtils.mkdir_p(DATA_DIR_PRIV)
54                 end
55
56                 @config_file = DATA_DIR_PRIV + "/config.yaml"
57
58                 load_config
59                 init_xmms
60                 init_gui
61
62                 blk = lambda { |item| ui_fill_track_info(item) }
63                 @playlist = Playlist.new(@xmms, blk)
64
65                 setup_callbacks
66
67                 @playtime_updater = PlaytimeUpdater.new(xmms) do |current_pos|
68                         ui_refresh_time(current_pos)
69
70                         # refresh the seeker
71                         if !@playlist.current_item.nil?
72                                 d = @playlist.current_item.duration
73                                 if d > 0
74                                         ui_refresh_seeker(current_pos / d.to_f)
75                                 end
76                         end
77                 end
78
79                 PlaybackStatusHandler.new(xmms) do |state|
80                         @playback_state = state
81
82                         signal_playback_state
83                         @playtime_updater.enabled =
84                                 (@playback_state == Xmms::Client::PLAY)
85                 end
86
87                 # is repeat enabled?
88                 s = @xmms.configval_get("playlist.repeat_all").wait.value
89                 @repeat = (s == "1")
90                 @edje.emit_signal("TOGGLE_REPEAT_MODE", "Euphoria") if @repeat
91
92                 @sliders = {}
93
94                 unless @edje.data("has_volume_slider").nil?
95                         @sliders["volume"] = VolumeSlider.new(@edje, @xmms,
96                                                               "player.slider.volume")
97                 end
98
99                 if @edje.part_exists?("vol_display_text")
100                         # FIXME Maybe rename the part to volume_box
101                         @volume_box = VolumeBox.new(@edje, @xmms, "player.volumeBox")
102                 end
103
104 =begin
105                 unless @edje.data("has_eq_slider").nil?
106                         1.upto(10) do |i|
107                                 p = "player.slider.eq.band%02i" % i
108                                 k = "equalizer.gain#{i - 1}"
109                                 @sliders[k] = EqSlider.new(@edje, @xmms, p, k)
110                         end
111                 end
112 =end
113
114                 handle_current_pos(@xmms.playlist_current_pos.wait)
115         end
116
117         def show
118                 if @edje.data("has_separate_playlist").nil?
119                         @playlist.show(@theme, false)
120                 end
121
122                 @ee.show
123         end
124
125         def save_config
126                 File.open(@config_file, "w") { |fo| YAML.dump(@config, fo) }
127         end
128
129         def show_file_dialog
130                 if @file_dialog_theme.nil?
131                         puts "file dialog edje not found"
132                         return
133                 end
134
135                 FileDialog.open(@file_dialog_theme) do |sel|
136                         if File.directory?(sel)
137                                 files = []
138                                 Find.find(sel) { |f| files << f if File.file?(f) }
139
140                                 files.sort.each do |f|
141                                         @xmms.playlist_add("file://" + f).wait
142                                 end
143                         elsif File.exists?(sel)
144                                 @xmms.playlist_add("file://" + sel).wait
145                         end
146                 end
147         end
148
149 private
150         def init_gui
151                 @ee = Ecore::Evas::SoftwareX11.new
152                 @ee.title = "Euphoria"
153                 @ee.name_class = ["Euphoria", "Euphoria"]
154                 @ee.borderless = true
155
156                 #@ee.evas.font_path_append("/usr/X11R6/lib/X11/fonts/TTF")
157                 #@ee.evas.font_path_append(ENV["HOME"] + "/.fonts")
158
159                 @ee.on_pre_render { Edje::thaw }
160                 @ee.on_post_render { Edje::freeze }
161
162                 @ee.on_resize do
163                         x, y, w, h = @ee.geometry
164
165                         @edje.resize(w, h)
166                         @dragger.resize(w, h)
167                 end
168
169                 @dragger = Esmart::Draggies.new(@ee)
170                 @dragger.name = "dragger"
171                 @dragger.button = 1
172                 @dragger.show
173
174                 @theme = find_theme(@config.theme)
175                 raise "Cannot find theme - #{@config.theme}" if @theme.nil?
176
177                 if Edje.collections(@theme).include?("esmart.filedialog")
178                         @file_dialog_theme = @theme
179                 else
180                         @file_dialog_theme = find_theme("file_dialog")
181                 end
182
183                 @edje = Edje::Edje.new(@ee.evas)
184                 @edje.load(@theme, "euphoria")
185                 @edje.name = "edje"
186                 @edje.show
187
188                 w, h = @edje.get_size_max
189                 @ee.set_size_max(w, h)
190
191                 w, h = @edje.get_size_min
192                 @ee.set_size_min(w, h)
193
194                 @ee.resize(w, h)
195
196                 @ee.shaped = !@edje.data("shaped").nil?
197
198                 ui_refresh_seeker(0)
199         end
200
201         def init_xmms
202                 @xmms = Xmms::Client.new("Euphoria")
203                 @xmms.connect(ENV["XMMS_PATH"] || @config.ipc_path)
204                 @xmms.add_to_ecore_mainloop
205
206                 @xmms.on_disconnect do
207                         puts "daemon exited, shutting down"
208                         Ecore.main_loop_quit
209                 end
210         end
211
212         def setup_callbacks
213                 @edje.on_signal("quit") do
214                         Ecore::main_loop_quit
215                 end
216
217                 @edje.on_signal("close") do
218                         Ecore::main_loop_quit
219                 end
220
221                 @edje.on_signal("playback.play") do
222                         case @playback_state
223                                 when Xmms::Client::PLAY
224                                         @xmms.playback_stop.wait
225                                         sleep(0.5) # evil hack, fix xmms2d instead
226                                         @xmms.playback_start.wait
227                                 else
228                                         @xmms.playback_start.wait
229                         end
230                 end
231
232                 @edje.on_signal("playback.stop") do
233                         @xmms.playback_stop.wait
234                 end
235
236                 @edje.on_signal("playback.pause") do
237                         case @playback_state
238                                 when Xmms::Client::PLAY
239                                         @xmms.playback_pause.wait
240                                 when Xmms::Client::PAUSE
241                                         @xmms.playback_start.wait
242                         end
243                 end
244
245                 @edje.on_signal("playback.next") do
246                         @xmms.playlist_set_next_rel(1).wait
247                         @xmms.playback_tickle.wait
248
249                         if @playback_state != Xmms::Client::PLAY
250                                 @xmms.playback_start.wait
251                         end
252                 end
253
254                 @edje.on_signal("playback.prev") do
255                         @xmms.playlist_set_next_rel(-1).wait
256                         @xmms.playback_tickle.wait
257
258                         if @playback_state != Xmms::Client::PLAY
259                                 @xmms.playback_start.wait
260                         end
261                 end
262
263                 @edje.on_signal("TOGGLE_TIME_DISPLAY_MODE") do
264                         @config.time_display_elapsed = !@config.time_display_elapsed
265                 end
266
267                 @edje.on_signal("DEBUG") do |sig, source|
268                         puts source
269                 end
270
271                 @edje.on_signal("playlist.open") { @playlist.show(@theme, true) }
272
273                 @edje.on_signal("file_dialog.open") do
274                         show_file_dialog
275                 end
276
277                 @edje.on_signal("TOGGLE_REPEAT_MODE") do |sig, source|
278                         # don't react on our own emission
279                         next if source == "Euphoria"
280
281                         @repeat = !@repeat
282
283                         v = @repeat ? "1" : "0"
284                         @xmms.configval_set("playlist.repeat_all", v).wait
285                 end
286
287                 @xmms.broadcast_configval_changed.notifier do |res|
288                         k = res.value.keys.first
289                         v = res.value.values.first
290
291                         case k
292                                 when "playlist.repeat_all"
293                                         tmp = (v == "1")
294                                         if tmp != @repeat
295                                                 @repeat = tmp
296                                                 @edje.emit_signal("TOGGLE_REPEAT_MODE", "Euphoria")
297                                         end
298                                 when /equalizer\.gain(\d+)/
299                                         @sliders[k].value = v.to_f
300                         end
301                 end
302
303                 @xmms.broadcast_playback_volume_changed.notifier do |res|
304                         if @edje.data("has_volume_slider")
305                                 @sliders["volume"].value = res.value
306                         end
307
308                         if @edje.part_exists?("vol_display_text")
309                                 @volume_box.value = res.value
310                         end
311                 end
312
313                 @xmms.broadcast_playlist_current_pos.notifier do |res|
314                         handle_current_pos(res)
315                 end
316         end
317
318         def handle_current_pos(res)
319                 begin
320                         pos = res.value
321                 rescue Xmms::Result::ValueError
322                 else
323                         ui_fill_track_info(@playlist[pos])
324                 end
325         end
326
327         def ui_fill_track_info(item)
328                 unless item.nil?
329                         @edje.send_message(item.track_info_message)
330                 end
331         end
332
333         def ui_refresh_time(time)
334                 return unless @edje.part_exists?("player.time")
335
336                 fmt = "%i:%02i"
337
338                 unless @config.time_display_elapsed
339                         time = @playlist.current_item.duration - time
340                         fmt = "-" + fmt
341                 end
342
343                 tmp = fmt % [time / 60, time % 60]
344                 @edje.part("player.time").text = tmp
345         end
346
347         def ui_refresh_seeker(pos)
348                 if @edje.part_exists?("seeker")
349                         @edje.part("seeker").set_drag_value(pos, 0.1)
350                         #@edje.thaw
351                         #@edje.evas.render
352                 end
353         end
354
355         def signal_playback_state
356                 sig = case @playback_state
357                         when Xmms::Client::PAUSE
358                                 "PLAYBACK_STATE_PAUSED"
359                         when Xmms::Client::PLAY
360                                 "PLAYBACK_STATE_PLAYING"
361                         else
362                                 "PLAYBACK_STATE_STOPPED"
363                 end
364
365                 @edje.emit_signal(sig, "Euphoria")
366         end
367
368         def load_config
369                 @config = nil
370
371                 if File.exist?(@config_file)
372                         File.open(@config_file) { |fo| @config = YAML.load(fo) }
373                 end
374
375                 if @config.nil? || !@config.is_a?(Config)
376                         @config = Config.new
377                         @config.ipc_path = nil
378                         @config.theme = "winter"
379                         @config.time_display_elapsed = true
380                 end
381         end
382
383         def find_theme(name)
384                 dirs = [DATA_DIR_PRIV, DATA_DIR]
385                 dirs.each do |dir|
386                         tmp = File.join(dir, "themes/#{name}.edj")
387                         return tmp if File.exist?(tmp)
388                 end
389
390                 nil
391         end
392 end
393
394 $x = Euphoria.instance
395 Euphoria.instance.show
396 Ecore::main_loop_begin
397 Euphoria.instance.save_config