The file dialog is broken atm, don't try to load it.
[euphoria.git] / bin / euphoria
1 #!/usr/bin/ruby -w
2
3 require "ecore"
4 require "ecore_x"
5 require "evas"
6 require "ecore_evas"
7 require "edje"
8 require "esmart"
9 require "esmart_draggies"
10 require "esmart_container"
11 require "xmmsclient"
12 require "xmmsclient_ecore"
13 require "singleton"
14 require "yaml"
15 require "find"
16 require "fileutils"
17
18 require "euphoria/playlist"
19 require "euphoria/playlist_item"
20 require "euphoria/slider"
21 require "euphoria/volume_box"
22 require "euphoria/playtime_updater"
23 require "euphoria/playback_status_handler"
24 #require "euphoria/file_dialog"
25
26 Config = Struct.new("Config", :ipc_path, :theme, :time_display_elapsed)
27
28 # backwards compatibility w/ DrFeelgood and earlier
29 unless Xmms.respond_to?(:userconfdir)
30         def Xmms.userconfdir
31                 File.join(File.expand_path("~"), ".xmms2")
32         end
33 end
34
35 class Euphoria
36         DATA_DIR = "/usr/local/share/euphoria"
37         DATA_DIR_PRIV = File.join(Xmms.userconfdir,
38                                   "clients",
39                                   "euphoria")
40
41         include Singleton
42
43         attr_reader :xmms, :ee, :edje
44
45         def initialize
46                 @edje = nil
47                 @xmms = nil
48                 @volume_box = nil
49
50                 # settings
51                 unless File.directory?(DATA_DIR_PRIV)
52                         FileUtils.mkdir_p(DATA_DIR_PRIV)
53                 end
54
55                 @config_file = DATA_DIR_PRIV + "/config.yaml"
56
57                 load_config
58                 init_xmms
59                 init_gui
60
61                 blk = lambda { |item| ui_fill_track_info(item) }
62                 @playlist = Playlist.new(@xmms, blk)
63
64                 setup_callbacks
65
66                 @playtime_updater = PlaytimeUpdater.new(xmms) do |current_pos|
67                         ui_refresh_time(current_pos)
68
69                         # refresh the seeker
70                         if !@playlist.current_item.nil?
71                                 d = @playlist.current_item.duration
72                                 if d > 0
73                                         ui_refresh_seeker(current_pos / d.to_f)
74                                 end
75                         end
76                 end
77
78                 PlaybackStatusHandler.new(xmms) do |state|
79                         @playback_state = state
80
81                         signal_playback_state
82                         @playtime_updater.enabled =
83                                 (@playback_state == Xmms::Client::PLAY)
84                 end
85
86                 # is repeat enabled?
87                 s = @xmms.configval_get("playlist.repeat_all").wait.value
88                 @repeat = (s == "1")
89                 @edje.emit_signal("TOGGLE_REPEAT_MODE", "Euphoria") if @repeat
90
91                 @sliders = {}
92
93                 unless @edje.data("has_volume_slider").nil?
94                         @sliders["volume"] = VolumeSlider.new(@edje, @xmms,
95                                                               "player.slider.volume")
96                 end
97
98                 if @edje.part_exists?("vol_display_text")
99                         # FIXME Maybe rename the part to volume_box
100                         @volume_box = VolumeBox.new(@edje, @xmms, "player.volumeBox")
101                 end
102
103 =begin
104                 unless @edje.data("has_eq_slider").nil?
105                         1.upto(10) do |i|
106                                 p = "player.slider.eq.band%02i" % i
107                                 k = "equalizer.gain#{i - 1}"
108                                 @sliders[k] = EqSlider.new(@edje, @xmms, p, k)
109                         end
110                 end
111 =end
112
113                 handle_current_pos(@xmms.playlist.current_pos.wait)
114         end
115
116         def show
117                 if @edje.data("has_separate_playlist").nil?
118                         @playlist.show(@theme, false)
119                 end
120
121                 @ee.show
122         end
123
124         def save_config
125                 File.open(@config_file, "w") { |fo| YAML.dump(@config, fo) }
126         end
127
128         def show_file_dialog
129                 if @file_dialog_theme.nil?
130                         puts "file dialog edje not found"
131                         return
132                 end
133
134                 FileDialog.open(@file_dialog_theme) do |sel|
135                         if File.directory?(sel)
136                                 files = []
137                                 Find.find(sel) { |f| files << f if File.file?(f) }
138
139                                 files.sort.each do |f|
140                                         @xmms.playlist_add("file://" + f).wait
141                                 end
142                         elsif File.exists?(sel)
143                                 @xmms.playlist_add("file://" + sel).wait
144                         end
145                 end
146         end
147
148 private
149         def init_gui
150                 @ee = Ecore::Evas::SoftwareX11.new
151                 @ee.title = "Euphoria"
152                 @ee.name_class = ["Euphoria", "Euphoria"]
153                 @ee.borderless = true
154
155                 #@ee.evas.font_path_append("/usr/X11R6/lib/X11/fonts/TTF")
156                 #@ee.evas.font_path_append(ENV["HOME"] + "/.fonts")
157                 @ee.evas.font_hinting = Evas::Evas::AUTO
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