shroom 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -7,7 +7,7 @@ require 'rake/testtask'
7
7
 
8
8
  spec = Gem::Specification.new do |s|
9
9
  s.name = 'shroom'
10
- s.version = '0.0.4'
10
+ s.version = '0.0.5'
11
11
  s.has_rdoc = true
12
12
  s.extra_rdoc_files = ['README', 'LICENSE']
13
13
  s.summary = 'Shroom is a music player and organizer'
@@ -26,6 +26,7 @@ spec = Gem::Specification.new do |s|
26
26
  s.add_dependency('rbrainz', '>= 0.5.0')
27
27
  s.add_dependency('scrobbler', '>= 0.2.3')
28
28
  s.add_dependency('shared-mime-info', '>= 0.1')
29
+ s.requirements << 'rake'
29
30
  s.requirements << 'libgnome2-ruby'
30
31
  s.requirements << 'libsqlite3-ruby'
31
32
  s.requirements << 'libglade2-ruby'
@@ -63,6 +63,76 @@ module Sh
63
63
  @model.clear
64
64
  fill_model
65
65
  end
66
+
67
+ pbtn_reread_tags = MenuItem.new 'Reread tags'
68
+ menu.append pbtn_reread_tags
69
+ pbtn_reread_tags.signal_connect('activate') do |widget|
70
+ song.read_tags!(true)
71
+ $db.save_song song
72
+ @model.clear
73
+ fill_model
74
+ end
75
+
76
+ pbtn_properties = ImageMenuItem.new Stock::PROPERTIES
77
+ menu.append pbtn_properties
78
+ pbtn_properties.signal_connect('activate') do |widget|
79
+ glade = Global::GLADE['dlg_song']
80
+ dlg_song = glade['dlg_song']
81
+ txt_title = glade['txt_title']
82
+ txt_title.text = song.title
83
+
84
+ # Artists combo box
85
+ sto_artists = ListStore.new(String, Artist)
86
+ cmb_artist = glade['cmb_artist']
87
+ sel = 0
88
+ $db.artists.sort_by{|a| (a.name || '')}.each_with_index do |artist, i|
89
+ iter = sto_artists.append
90
+ iter[0] = artist.name
91
+ iter[1] = artist
92
+ sel = i if song.artist && artist.db_id == song.artist.db_id
93
+ end
94
+ cmb_artist.model = sto_artists
95
+ cmb_artist.active = sel
96
+
97
+ # Albums combo box
98
+ sto_albums = ListStore.new(String, Album)
99
+ cmb_album = glade['cmb_album']
100
+ sel = 0
101
+ $db.albums.sort_by{|a| (a.title || '')}.each_with_index do |album, i|
102
+ iter = sto_albums.append
103
+ iter[0] = album.title
104
+ iter[1] = album
105
+ sel = i if song.album && album.db_id == song.album.db_id
106
+ end
107
+ cmb_album.model = sto_albums
108
+ cmb_album.active = sel
109
+
110
+ glade['chk_image'].sensitive = false
111
+ glade['fbtn_image'].sensitive = false
112
+ glade['btn_add_artist'].sensitive = false
113
+ glade['btn_add_album'].sensitive = false
114
+
115
+ dlg_song.signal_connect('delete-event') do
116
+ dlg_song.hide
117
+ end
118
+ btn_ok = glade['btn_ok']
119
+ ok_handle = btn_ok.signal_connect('clicked') do
120
+ song.title = txt_title.text
121
+ song.artist = cmb_artist.active_iter[1]
122
+ song.album = cmb_album.active_iter[1]
123
+ $db.save_song song
124
+ @model.clear
125
+ fill_model
126
+ dlg_song.hide
127
+ end
128
+ btn_cancel = glade['btn_cancel']
129
+ close_handle = btn_cancel.signal_connect('clicked') do
130
+ dlg_song.hide
131
+ end
132
+ dlg_song.run
133
+ btn_ok.signal_handler_disconnect ok_handle
134
+ btn_cancel.signal_handler_disconnect close_handle
135
+ end
66
136
 
67
137
  menu.show_all
68
138
  menu.popup(nil, nil, event.button, event.time)
@@ -104,7 +174,7 @@ module Sh
104
174
 
105
175
  def fill_model
106
176
  artist_node = album_node = track_node = nil
107
- $db.songs.sort_by {|a| a.to_s}.each do |song|
177
+ $db.songs.sort_by {|a| (a.to_s || '')}.each do |song|
108
178
  artist = song.artist
109
179
  if not artist_node or artist_node[0].db_id != artist.db_id
110
180
  artist_node = @model.append nil
@@ -7,7 +7,7 @@ module Sh
7
7
  def CoverArt.get_cover song
8
8
  if $prefs[:cover_art]
9
9
  artist, album = song.artist.name, song.album.title
10
- path = "#{$cover_dir}/#{artist} - #{album}"
10
+ path = "#{$cover_dir}/#{artist.to_md5}_#{album.to_md5}"
11
11
  return path if File.exists? path
12
12
  doc = lastfm("album.getInfo", {:artist => artist, :album => album})
13
13
  img_url = REXML::XPath.first(doc, '//image[@size="extralarge"]').text
@@ -1,5 +1,6 @@
1
1
  require 'fileutils'
2
2
  require 'yaml'
3
+ require 'libglade2'
3
4
 
4
5
  module Sh
5
6
  KEYS = {
@@ -8,8 +9,10 @@ module Sh
8
9
  }
9
10
 
10
11
  class Global
12
+ SUPPORTED_EXTENSIONS = ['.mp3', '.m4a', '.ogg', '.flac', '.wma', '.wav']
13
+ GLADE = {}
14
+
11
15
  def initialize
12
- #$KCODE = 'u'
13
16
  # Won't work on Windows
14
17
  $home = ENV['HOME']
15
18
  $config_dir = "#{$home}/.shroom"
@@ -28,6 +31,10 @@ module Sh
28
31
  }
29
32
 
30
33
  Global.load_prefs
34
+
35
+ add_toplevel_glade('dlg_preferences')
36
+ add_toplevel_glade('dlg_song')
37
+ GLADE.freeze
31
38
  end
32
39
 
33
40
  def Global.load_prefs
@@ -48,6 +55,11 @@ module Sh
48
55
  def Global.locate(file)
49
56
  find_in_load_path("shroom-res/#{file}") || "shroom-res/#{file}"
50
57
  end
58
+
59
+ private
60
+ def add_toplevel_glade name
61
+ GLADE[name] = GladeXML.new(Global.locate('shroom.glade'), name)
62
+ end
51
63
  end
52
64
  end
53
65
 
@@ -0,0 +1,44 @@
1
+ module Sh
2
+ class MMKeys
3
+ def initialize &block
4
+ raise(ArgumentError, "No callback block provided") unless block
5
+ @supported = try_require('rubygems') and try_require('rbus')
6
+
7
+ if supported?
8
+ bus = RBus.session_bus
9
+
10
+ begin
11
+ @mmkeys = bus.get_object('org.gnome.SettingsDaemon', '/org/gnome/SettingsDaemon/MediaKeys')
12
+ @mmkeys.interface!('org.gnome.SettingsDaemon.MediaKeys')
13
+ @mmkeys.GrabMediaPlayerKeys('Shroom', 0)
14
+ rescue
15
+ begin
16
+ @mmkeys = bus.get_object('org.gnome.SettingsDaemon', '/org/gnome/SettingsDaemon/MediaKeys')
17
+ @mmkeys.interface!('org.gnome.SettingsDaemon.MediaKeys')
18
+ @mmkeys.GrabMediaPlayerKeys('Shroom', 0)
19
+ rescue
20
+ # MediaKeys are not supported after all
21
+ @supported = false
22
+ end
23
+ end
24
+ end
25
+
26
+ # Do we still think that MediaKeys are supported?
27
+ if supported? and @mmkeys
28
+ @mmkeys.connect!(:MediaPlayerKeyPressed) do |app, key|
29
+ if app.downcase == 'shroom'
30
+ block.call key
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ def supported?
37
+ return @supported
38
+ end
39
+
40
+ def destroy
41
+ @mmkeys.ReleaseMediaPlayerKeys('Shroom')
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,36 @@
1
+ module Sh
2
+ class Playlist
3
+ def initialize dir
4
+ @dir = dir
5
+ @songs = []
6
+ end
7
+
8
+ def Playlist.parse(path)
9
+ list = Playlist.new(File.dirname(path))
10
+ open(path) do |f|
11
+ while line = f.gets
12
+ line.strip!
13
+ unless line[0] == ?# or line.empty?
14
+ ref = File.expand_path(line.gsub('\\', '/'), File.dirname(path))
15
+ if File.exists? ref
16
+ if Sh::Global::SUPPORTED_EXTENSIONS.include? File.extname(ref)
17
+ list << ($db.songs(:path => ref).first || Sh::Song.new(ref).read_tags!)
18
+ end
19
+ else
20
+ puts "Not found: " + ref
21
+ end
22
+ end
23
+ end
24
+ end
25
+ return list
26
+ end
27
+
28
+ def <<(song)
29
+ @songs << song
30
+ end
31
+
32
+ def songs
33
+ return @songs.dup
34
+ end
35
+ end
36
+ end
@@ -34,7 +34,12 @@ module Sh
34
34
  end
35
35
 
36
36
  def lookup!
37
- track = lookup_multiple.first
37
+ begin
38
+ track = lookup_multiple.first
39
+ rescue Exception
40
+ return
41
+ end
42
+
38
43
  if track
39
44
  # Don't distinguish between bands with the same name by adding to the name
40
45
  track.artist.disambiguation = false
@@ -74,7 +79,8 @@ module Sh
74
79
  query = Webservice::Query.new
75
80
  filter = Webservice::TrackFilter.new(:artist => artist.name, :title => title, :puid => puids.first)
76
81
  tracks = query.get_tracks(filter).entities
77
- @matches = tracks || []
82
+ @matches = (tracks || [])
83
+ return @matches
78
84
  end
79
85
 
80
86
  public
@@ -17,20 +17,25 @@ module Sh
17
17
  '.wma' => 'audio/x-ms-wma'
18
18
  }[File.extname path]
19
19
 
20
- case @mime
21
- when 'audio/mpeg'
22
- metadata = TagReader.read_mp3 path
23
- when 'audio/mp4'
24
- metadata = TagReader.read_m4a path
25
- when 'audio/ogg'
26
- metadata = TagReader.read_ogg path
27
- when 'audio/x-flac'
28
- metadata = TagReader.read_flac path
29
- when 'audio/x-ms-wma'
30
- metadata = TagReader.read_wma path
31
- when 'audio/x-wav'
32
- metadata = TagReader.read_wave path
20
+ begin
21
+ case @mime
22
+ when 'audio/mpeg'
23
+ metadata = TagReader.read_mp3 path
24
+ when 'audio/mp4'
25
+ metadata = TagReader.read_m4a path
26
+ when 'audio/ogg'
27
+ metadata = TagReader.read_ogg path
28
+ when 'audio/x-flac'
29
+ metadata = TagReader.read_flac path
30
+ when 'audio/x-ms-wma'
31
+ metadata = TagReader.read_wma path
32
+ when 'audio/x-wav'
33
+ metadata = TagReader.read_wave path
34
+ end
35
+ rescue Exception
36
+ puts "Error parsing tags for " + path
33
37
  end
38
+
34
39
  yield metadata if block
35
40
  return metadata
36
41
  end
@@ -21,6 +21,54 @@ class Object
21
21
  end
22
22
  end
23
23
 
24
+ require 'digest/md5'
25
+
26
+ class String
27
+ def to_md5
28
+ return (Digest::MD5.new << self).to_s
29
+ end
30
+ end
31
+
32
+ class StringMatcher
33
+ def initialize str1, str2
34
+ @str1, @str2 = str1, str2
35
+ end
36
+
37
+ def compare
38
+ pairs1 = word_letter_pairs @str1
39
+ pairs2 = word_letter_pairs @str2
40
+ union = pairs1.size + pairs2.size
41
+ intersection = 0
42
+ pairs1.size.times do
43
+ intersection += 1 if pairs2.delete pairs1.pop
44
+ end
45
+ return (2 * intersection).to_f / union
46
+ end
47
+
48
+ def compare_ignore_case
49
+ compare @str1.upcase, @str2.upcase
50
+ end
51
+
52
+ private
53
+ def letter_pairs str
54
+ n_pairs = str.length - 1
55
+ pairs = []
56
+ n_pairs.times do |i|
57
+ pairs[i] = str.slice(i, 2)
58
+ end
59
+ return pairs
60
+ end
61
+
62
+ def word_letter_pairs str
63
+ pairs = []
64
+ words = str.split(/\s/)
65
+ words.each do |word|
66
+ pairs.concat letter_pairs(word)
67
+ end
68
+ return pairs
69
+ end
70
+ end
71
+
24
72
  module Kelp
25
73
  class ProgressDialog < Gtk::Dialog
26
74
  def initialize(title)
@@ -66,8 +114,8 @@ module Kelp
66
114
  @image = Image.new(pixbuf)
67
115
  vbox.pack_start @image, true, true, 8
68
116
  if pixbuf
69
- self.width_request = pixbuf.width + 16
70
- self.height_request = size[1] + pixbuf.height + 16
117
+ self.width_request = pixbuf.width + 16
118
+ self.height_request = size[1] + pixbuf.height + 16
71
119
  end
72
120
  vbox.show_all
73
121
  signal_connect('response') { destroy }
@@ -1,8 +1,9 @@
1
1
  require 'sh_browse'
2
2
  require 'sh_queue'
3
+ require 'sh_playlist'
3
4
  require 'sh_lyrics'
4
5
  require 'sh_cover_art'
5
- require 'libglade2'
6
+ require 'sh_mmkeys'
6
7
  require 'gtk2'
7
8
  include Gtk
8
9
 
@@ -22,8 +23,6 @@ module Sh
22
23
 
23
24
  Notify.init 'Shroom' if @rnotify
24
25
 
25
- @glade= GladeXML.new(Global.locate('shroom.glade'))
26
-
27
26
  if File.exists? $prefs[:library_dir]
28
27
  cancelled = false
29
28
  dialog = Kelp::ProgressDialog.new("Updating database...")
@@ -31,7 +30,7 @@ module Sh
31
30
  new_songs = []
32
31
  Dir[$prefs[:library_dir]+'/**/*'].each do |path|
33
32
  ext = File.extname path
34
- if ['.mp3', '.m4a', '.ogg', '.flac'].include? ext
33
+ if Sh::Global::SUPPORTED_EXTENSIONS.include? ext
35
34
  new_songs << Song.new(path) unless $db.contains? path
36
35
  end
37
36
  end
@@ -66,7 +65,7 @@ module Sh
66
65
  @window.signal_connect('destroy') do
67
66
  quit
68
67
  end
69
-
68
+
70
69
  @status_icon = StatusIcon.new
71
70
  @status_icon.pixbuf = icon
72
71
  @status_icon.visible = true
@@ -136,34 +135,10 @@ module Sh
136
135
  end if @player
137
136
  end
138
137
  btn_next.signal_connect('clicked') do |btn|
139
- if @queue
140
- playing = @player.playing?
141
- @player.stop
142
- @queue_pos += 1
143
- if @queue_pos < @queue.size
144
- prepare_song
145
- play if playing
146
- else
147
- @queue_pos = 0
148
- prepare_song
149
- @btn_play.active = false
150
- end
151
- end
138
+ next_song
152
139
  end
153
140
  btn_prev.signal_connect('clicked') do |btn|
154
- if @queue
155
- playing = @player.playing?
156
- @player.stop
157
- @queue_pos -= 1
158
- if @queue_pos >= 0
159
- prepare_song
160
- play if playing
161
- else
162
- @queue_pos = 0
163
- prepare_song
164
- @btn_play.active = false
165
- end
166
- end
141
+ prev_song
167
142
  end
168
143
 
169
144
  # Horizontally split area
@@ -232,6 +207,32 @@ module Sh
232
207
  i += 1
233
208
  end
234
209
 
210
+ # Multimedia keys
211
+ MMKeys.new do |key|
212
+ case key
213
+ when 'Stop'
214
+ stop
215
+ when 'Next'
216
+ next_song
217
+ when 'Previous'
218
+ prev_song
219
+ when 'Play'
220
+ if playing?
221
+ pause
222
+ else
223
+ play
224
+ end
225
+ when 'PlayPause'
226
+ if playing?
227
+ pause
228
+ else
229
+ play
230
+ end
231
+ when 'Pause'
232
+ pause
233
+ end
234
+ end
235
+
235
236
  # Show everything
236
237
  @window.show_all
237
238
  end
@@ -240,6 +241,7 @@ module Sh
240
241
  def create_menu
241
242
  menu_items = [
242
243
  ['/_File'],
244
+ ['/File/Import playlist', '<StockItem>', '<control>I', Stock::OPEN, lambda {import_playlist}],
243
245
  ['/File/sep1', '<Separator>', nil, nil, lambda {}],
244
246
  ['/File/Quit', '<StockItem>', '<control>Q', Stock::QUIT, lambda {Gtk.main_quit}],
245
247
  ['/_Edit'],
@@ -254,20 +256,37 @@ module Sh
254
256
  item_factory.get_widget('<main>')
255
257
  end
256
258
 
259
+ def import_playlist
260
+ dialog = Gtk::FileChooserDialog.new("Open File",
261
+ @window,
262
+ Gtk::FileChooser::ACTION_OPEN,
263
+ nil,
264
+ [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
265
+ [Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT])
266
+
267
+ if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
268
+ stop
269
+ self.queue = Sh::Playlist.parse(dialog.filename).songs
270
+ play
271
+ end
272
+ dialog.destroy
273
+ end
274
+
257
275
  def show_preferences
258
- dlg_prefs = @glade.get_widget("dlg_preferences")
276
+ glade = Sh::Global::GLADE['dlg_preferences']
277
+ dlg_prefs = glade['dlg_preferences']
259
278
  dlg_prefs.signal_connect('delete-event') do
260
279
  dlg_prefs.hide
261
280
  end
262
- fbtn_library = @glade.get_widget("fbtn_library")
281
+ fbtn_library = glade['fbtn_library']
263
282
  fbtn_library.current_folder = $prefs[:library_dir]
264
- chk_lastfm = @glade.get_widget("chk_lastfm")
283
+ chk_lastfm = glade['chk_lastfm']
265
284
  chk_lastfm.active = $prefs[:lastfm]
266
- txt_lastfm_user = @glade.get_widget("txt_lastfm_user")
285
+ txt_lastfm_user = glade['txt_lastfm_user']
267
286
  txt_lastfm_user.text = $prefs[:lastfm_user]
268
- txt_lastfm_pass = @glade.get_widget("txt_lastfm_pass")
287
+ txt_lastfm_pass = glade['txt_lastfm_pass']
269
288
  txt_lastfm_pass.text = $prefs[:lastfm_password]
270
- btn_ok = @glade.get_widget("btn_ok")
289
+ btn_ok = glade['btn_ok']
271
290
  ok_handle = btn_ok.signal_connect('clicked') do
272
291
  $prefs[:library_dir] = fbtn_library.filename
273
292
  $prefs[:lastfm] = chk_lastfm.active?
@@ -276,16 +295,13 @@ module Sh
276
295
  Sh::Global.save_prefs
277
296
  dlg_prefs.hide
278
297
  end
279
- btn_close = @glade.get_widget("btn_close")
280
- close_handle = btn_close.signal_connect('clicked') do
298
+ btn_cancel = glade['btn_cancel']
299
+ close_handle = btn_cancel.signal_connect('clicked') do
281
300
  dlg_prefs.hide
282
301
  end
283
- dlg_prefs.show
284
- while dlg_prefs.visible?
285
- Gtk.main_iteration
286
- end
302
+ dlg_prefs.run
287
303
  btn_ok.signal_handler_disconnect ok_handle
288
- btn_close.signal_handler_disconnect close_handle
304
+ btn_cancel.signal_handler_disconnect close_handle
289
305
  end
290
306
 
291
307
  def show_about
@@ -305,18 +321,7 @@ module Sh
305
321
  @player.demolish if @player
306
322
  @player = Sh::Player.new @queue[@queue_pos]
307
323
  @player.on_finished do |player|
308
- if player == @player
309
- @player.stop
310
- @queue_pos += 1
311
- if @queue_pos < @queue.size
312
- prepare_song
313
- play
314
- else
315
- @queue_pos = 0
316
- prepare_song
317
- @btn_play.active = false
318
- end
319
- end
324
+ next_song
320
325
  end
321
326
  on_song_changed
322
327
  end
@@ -346,7 +351,13 @@ module Sh
346
351
  # Cover art
347
352
  if $prefs[:cover_art]
348
353
  @pixbuf = nil
349
- (@pixbuf = Gdk::Pixbuf.new(song.album.image_path)) rescue Exception
354
+ begin
355
+ if File.exists? song.album.image_path
356
+ @pixbuf = Gdk::Pixbuf.new(song.album.image_path)
357
+ end
358
+ rescue
359
+ @pixbuf = nil
360
+ end
350
361
  if @pixbuf
351
362
  @note.pixbuf_icon = @pixbuf.scale(48, 48) if @rnotify
352
363
  @img_cover.pixbuf = @pixbuf.scale(132, 132)
@@ -411,18 +422,64 @@ module Sh
411
422
  end
412
423
 
413
424
  def play
414
- stop
415
425
  if @player
416
426
  @btn_play.active = true
417
427
  @player.play
418
428
  end
419
429
  end
420
430
 
431
+ def pause
432
+ if @player
433
+ @btn_play.active = false
434
+ @player.pause
435
+ end
436
+ end
437
+
438
+ def playing?
439
+ return @player && @player.playing?
440
+ end
441
+
442
+ def paused?
443
+ return @player && @player.paused?
444
+ end
445
+
421
446
  def stop
422
447
  @btn_play.active = false
423
448
  @player.stop if @player
424
449
  end
425
450
 
451
+ def next_song
452
+ if @queue
453
+ playing = @player.playing?
454
+ @player.stop
455
+ @queue_pos += 1
456
+ if @queue_pos < @queue.size
457
+ prepare_song
458
+ play if playing
459
+ else
460
+ @queue_pos = 0
461
+ prepare_song
462
+ @btn_play.active = false
463
+ end
464
+ end
465
+ end
466
+
467
+ def prev_song
468
+ if @queue
469
+ playing = @player.playing?
470
+ @player.stop
471
+ @queue_pos -= 1
472
+ if @queue_pos >= 0
473
+ prepare_song
474
+ play if playing
475
+ else
476
+ @queue_pos = 0
477
+ prepare_song
478
+ @btn_play.active = false
479
+ end
480
+ end
481
+ end
482
+
426
483
  def show
427
484
  Gtk.main
428
485
  end
@@ -87,6 +87,7 @@
87
87
  <packing>
88
88
  <property name="left_attach">1</property>
89
89
  <property name="right_attach">2</property>
90
+ <property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
90
91
  </packing>
91
92
  </child>
92
93
  <child>
@@ -101,6 +102,7 @@
101
102
  <property name="right_attach">2</property>
102
103
  <property name="top_attach">1</property>
103
104
  <property name="bottom_attach">2</property>
105
+ <property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
104
106
  </packing>
105
107
  </child>
106
108
  <child>
@@ -152,6 +154,20 @@
152
154
  <widget class="GtkHButtonBox" id="dialog-action_area1">
153
155
  <property name="visible">True</property>
154
156
  <property name="layout_style">end</property>
157
+ <child>
158
+ <widget class="GtkButton" id="btn_cancel">
159
+ <property name="label">gtk-cancel</property>
160
+ <property name="visible">True</property>
161
+ <property name="can_focus">True</property>
162
+ <property name="receives_default">True</property>
163
+ <property name="use_stock">True</property>
164
+ </widget>
165
+ <packing>
166
+ <property name="expand">False</property>
167
+ <property name="fill">False</property>
168
+ <property name="position">0</property>
169
+ </packing>
170
+ </child>
155
171
  <child>
156
172
  <widget class="GtkButton" id="btn_ok">
157
173
  <property name="label">gtk-ok</property>
@@ -160,6 +176,235 @@
160
176
  <property name="receives_default">True</property>
161
177
  <property name="use_stock">True</property>
162
178
  </widget>
179
+ <packing>
180
+ <property name="expand">False</property>
181
+ <property name="fill">False</property>
182
+ <property name="position">1</property>
183
+ </packing>
184
+ </child>
185
+ </widget>
186
+ <packing>
187
+ <property name="expand">False</property>
188
+ <property name="pack_type">end</property>
189
+ <property name="position">0</property>
190
+ </packing>
191
+ </child>
192
+ </widget>
193
+ </child>
194
+ </widget>
195
+ <widget class="GtkDialog" id="dlg_song">
196
+ <property name="border_width">5</property>
197
+ <property name="type_hint">normal</property>
198
+ <property name="has_separator">False</property>
199
+ <child internal-child="vbox">
200
+ <widget class="GtkVBox" id="dialog-vbox1">
201
+ <property name="visible">True</property>
202
+ <property name="orientation">vertical</property>
203
+ <property name="spacing">2</property>
204
+ <child>
205
+ <widget class="GtkTable" id="table1">
206
+ <property name="visible">True</property>
207
+ <property name="n_rows">5</property>
208
+ <property name="n_columns">3</property>
209
+ <property name="row_spacing">4</property>
210
+ <child>
211
+ <widget class="GtkLabel" id="lbl_title">
212
+ <property name="visible">True</property>
213
+ <property name="label" translatable="yes">Title:</property>
214
+ </widget>
215
+ <packing>
216
+ <property name="x_options"></property>
217
+ <property name="y_options"></property>
218
+ </packing>
219
+ </child>
220
+ <child>
221
+ <widget class="GtkEntry" id="txt_title">
222
+ <property name="visible">True</property>
223
+ <property name="can_focus">True</property>
224
+ <property name="invisible_char">&#x25CF;</property>
225
+ </widget>
226
+ <packing>
227
+ <property name="left_attach">1</property>
228
+ <property name="right_attach">3</property>
229
+ <property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
230
+ <property name="y_options"></property>
231
+ </packing>
232
+ </child>
233
+ <child>
234
+ <widget class="GtkButton" id="btn_add_album">
235
+ <property name="label" translatable="yes">gtk-add</property>
236
+ <property name="visible">True</property>
237
+ <property name="can_focus">True</property>
238
+ <property name="receives_default">True</property>
239
+ <property name="use_stock">True</property>
240
+ </widget>
241
+ <packing>
242
+ <property name="left_attach">2</property>
243
+ <property name="right_attach">3</property>
244
+ <property name="top_attach">2</property>
245
+ <property name="bottom_attach">3</property>
246
+ <property name="x_options">GTK_FILL</property>
247
+ <property name="y_options"></property>
248
+ </packing>
249
+ </child>
250
+ <child>
251
+ <widget class="GtkComboBox" id="cmb_album">
252
+ <property name="visible">True</property>
253
+ <property name="active">0</property>
254
+ <property name="items" translatable="yes">Unknown</property>
255
+ </widget>
256
+ <packing>
257
+ <property name="left_attach">1</property>
258
+ <property name="right_attach">2</property>
259
+ <property name="top_attach">2</property>
260
+ <property name="bottom_attach">3</property>
261
+ <property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
262
+ <property name="y_options"></property>
263
+ </packing>
264
+ </child>
265
+ <child>
266
+ <widget class="GtkLabel" id="lbl_album">
267
+ <property name="visible">True</property>
268
+ <property name="label" translatable="yes">Album:</property>
269
+ </widget>
270
+ <packing>
271
+ <property name="top_attach">2</property>
272
+ <property name="bottom_attach">3</property>
273
+ <property name="x_options"></property>
274
+ <property name="y_options"></property>
275
+ </packing>
276
+ </child>
277
+ <child>
278
+ <widget class="GtkLabel" id="lbl_artist">
279
+ <property name="visible">True</property>
280
+ <property name="label" translatable="yes">Artist:</property>
281
+ </widget>
282
+ <packing>
283
+ <property name="top_attach">1</property>
284
+ <property name="bottom_attach">2</property>
285
+ <property name="x_options"></property>
286
+ <property name="y_options"></property>
287
+ </packing>
288
+ </child>
289
+ <child>
290
+ <widget class="GtkComboBox" id="cmb_artist">
291
+ <property name="visible">True</property>
292
+ <property name="active">0</property>
293
+ <property name="items" translatable="yes">Unknown</property>
294
+ </widget>
295
+ <packing>
296
+ <property name="left_attach">1</property>
297
+ <property name="right_attach">2</property>
298
+ <property name="top_attach">1</property>
299
+ <property name="bottom_attach">2</property>
300
+ <property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
301
+ <property name="y_options"></property>
302
+ </packing>
303
+ </child>
304
+ <child>
305
+ <widget class="GtkButton" id="btn_add_artist">
306
+ <property name="label" translatable="yes">gtk-add</property>
307
+ <property name="visible">True</property>
308
+ <property name="can_focus">True</property>
309
+ <property name="receives_default">True</property>
310
+ <property name="use_stock">True</property>
311
+ </widget>
312
+ <packing>
313
+ <property name="left_attach">2</property>
314
+ <property name="right_attach">3</property>
315
+ <property name="top_attach">1</property>
316
+ <property name="bottom_attach">2</property>
317
+ <property name="x_options">GTK_FILL</property>
318
+ <property name="y_options"></property>
319
+ </packing>
320
+ </child>
321
+ <child>
322
+ <widget class="GtkLabel" id="lbl_image">
323
+ <property name="visible">True</property>
324
+ <property name="label" translatable="yes">Image:</property>
325
+ </widget>
326
+ <packing>
327
+ <property name="top_attach">3</property>
328
+ <property name="bottom_attach">4</property>
329
+ <property name="x_options"></property>
330
+ <property name="y_options"></property>
331
+ </packing>
332
+ </child>
333
+ <child>
334
+ <widget class="GtkImage" id="img_image">
335
+ <property name="visible">True</property>
336
+ <property name="stock">gtk-missing-image</property>
337
+ </widget>
338
+ <packing>
339
+ <property name="left_attach">1</property>
340
+ <property name="right_attach">2</property>
341
+ <property name="top_attach">3</property>
342
+ <property name="bottom_attach">4</property>
343
+ </packing>
344
+ </child>
345
+ <child>
346
+ <widget class="GtkVBox" id="vbox1">
347
+ <property name="visible">True</property>
348
+ <property name="orientation">vertical</property>
349
+ <child>
350
+ <widget class="GtkCheckButton" id="chk_image">
351
+ <property name="label" translatable="yes">Separate image</property>
352
+ <property name="visible">True</property>
353
+ <property name="can_focus">True</property>
354
+ <property name="receives_default">False</property>
355
+ <property name="draw_indicator">True</property>
356
+ </widget>
357
+ <packing>
358
+ <property name="expand">False</property>
359
+ <property name="fill">False</property>
360
+ <property name="position">0</property>
361
+ </packing>
362
+ </child>
363
+ <child>
364
+ <widget class="GtkFileChooserButton" id="fbtn_image">
365
+ <property name="visible">True</property>
366
+ <property name="title" translatable="yes">Select A Image</property>
367
+ </widget>
368
+ <packing>
369
+ <property name="expand">False</property>
370
+ <property name="fill">False</property>
371
+ <property name="position">1</property>
372
+ </packing>
373
+ </child>
374
+ </widget>
375
+ <packing>
376
+ <property name="left_attach">2</property>
377
+ <property name="right_attach">3</property>
378
+ <property name="top_attach">3</property>
379
+ <property name="bottom_attach">4</property>
380
+ </packing>
381
+ </child>
382
+ <child>
383
+ <placeholder/>
384
+ </child>
385
+ <child>
386
+ <placeholder/>
387
+ </child>
388
+ <child>
389
+ <placeholder/>
390
+ </child>
391
+ </widget>
392
+ <packing>
393
+ <property name="position">1</property>
394
+ </packing>
395
+ </child>
396
+ <child internal-child="action_area">
397
+ <widget class="GtkHButtonBox" id="dialog-action_area1">
398
+ <property name="visible">True</property>
399
+ <property name="layout_style">end</property>
400
+ <child>
401
+ <widget class="GtkButton" id="btn_cancel">
402
+ <property name="label" translatable="yes">gtk-cancel</property>
403
+ <property name="visible">True</property>
404
+ <property name="can_focus">True</property>
405
+ <property name="receives_default">True</property>
406
+ <property name="use_stock">True</property>
407
+ </widget>
163
408
  <packing>
164
409
  <property name="expand">False</property>
165
410
  <property name="fill">False</property>
@@ -167,8 +412,8 @@
167
412
  </packing>
168
413
  </child>
169
414
  <child>
170
- <widget class="GtkButton" id="btn_close">
171
- <property name="label">gtk-close</property>
415
+ <widget class="GtkButton" id="btn_ok">
416
+ <property name="label" translatable="yes">gtk-ok</property>
172
417
  <property name="visible">True</property>
173
418
  <property name="can_focus">True</property>
174
419
  <property name="receives_default">True</property>
@@ -190,4 +435,96 @@
190
435
  </widget>
191
436
  </child>
192
437
  </widget>
438
+ <widget class="GtkDialog" id="dlg_artist">
439
+ <property name="border_width">5</property>
440
+ <property name="type_hint">normal</property>
441
+ <property name="has_separator">False</property>
442
+ <child internal-child="vbox">
443
+ <widget class="GtkVBox" id="dialog-vbox3">
444
+ <property name="visible">True</property>
445
+ <property name="orientation">vertical</property>
446
+ <property name="spacing">2</property>
447
+ <child>
448
+ <widget class="GtkTable" id="table1">
449
+ <property name="visible">True</property>
450
+ <property name="n_rows">2</property>
451
+ <property name="n_columns">3</property>
452
+ <property name="row_spacing">4</property>
453
+ <child>
454
+ <widget class="GtkLabel" id="lbl_name">
455
+ <property name="visible">True</property>
456
+ <property name="label" translatable="yes">Name:</property>
457
+ </widget>
458
+ <packing>
459
+ <property name="x_options"></property>
460
+ <property name="y_options"></property>
461
+ </packing>
462
+ </child>
463
+ <child>
464
+ <widget class="GtkEntry" id="entry1">
465
+ <property name="visible">True</property>
466
+ <property name="can_focus">True</property>
467
+ <property name="invisible_char">&#x25CF;</property>
468
+ </widget>
469
+ <packing>
470
+ <property name="left_attach">1</property>
471
+ <property name="right_attach">3</property>
472
+ <property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
473
+ <property name="y_options"></property>
474
+ </packing>
475
+ </child>
476
+ <child>
477
+ <placeholder/>
478
+ </child>
479
+ <child>
480
+ <placeholder/>
481
+ </child>
482
+ <child>
483
+ <placeholder/>
484
+ </child>
485
+ </widget>
486
+ <packing>
487
+ <property name="position">1</property>
488
+ </packing>
489
+ </child>
490
+ <child internal-child="action_area">
491
+ <widget class="GtkHButtonBox" id="dialog-action_area3">
492
+ <property name="visible">True</property>
493
+ <property name="layout_style">end</property>
494
+ <child>
495
+ <widget class="GtkButton" id="button2">
496
+ <property name="label" translatable="yes">button</property>
497
+ <property name="visible">True</property>
498
+ <property name="can_focus">True</property>
499
+ <property name="receives_default">True</property>
500
+ </widget>
501
+ <packing>
502
+ <property name="expand">False</property>
503
+ <property name="fill">False</property>
504
+ <property name="position">0</property>
505
+ </packing>
506
+ </child>
507
+ <child>
508
+ <widget class="GtkButton" id="button1">
509
+ <property name="label" translatable="yes">button</property>
510
+ <property name="visible">True</property>
511
+ <property name="can_focus">True</property>
512
+ <property name="receives_default">True</property>
513
+ </widget>
514
+ <packing>
515
+ <property name="expand">False</property>
516
+ <property name="fill">False</property>
517
+ <property name="position">1</property>
518
+ </packing>
519
+ </child>
520
+ </widget>
521
+ <packing>
522
+ <property name="expand">False</property>
523
+ <property name="pack_type">end</property>
524
+ <property name="position">0</property>
525
+ </packing>
526
+ </child>
527
+ </widget>
528
+ </child>
529
+ </widget>
193
530
  </glade-interface>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shroom
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aiden Nibali
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-07-26 00:00:00 +10:00
12
+ date: 2009-07-31 00:00:00 +10:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -131,6 +131,7 @@ files:
131
131
  - lib/sh_main.rb
132
132
  - lib/sh_queue.rb
133
133
  - lib/sh_album.rb
134
+ - lib/sh_playlist.rb
134
135
  - lib/sh_song.rb
135
136
  - lib/sh_artist.rb
136
137
  - lib/sh_cover_art.rb
@@ -138,6 +139,7 @@ files:
138
139
  - lib/sh_lyrics.rb
139
140
  - lib/sh_database.rb
140
141
  - lib/sh_util.rb
142
+ - lib/sh_mmkeys.rb
141
143
  - lib/shroom-res
142
144
  - lib/shroom-res/icon_16x16.png
143
145
  - lib/shroom-res/icon.svg
@@ -165,6 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
165
167
  version: "0"
166
168
  version:
167
169
  requirements:
170
+ - rake
168
171
  - libgnome2-ruby
169
172
  - libsqlite3-ruby
170
173
  - libglade2-ruby