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