lyrics-ebook 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/LICENSE +340 -0
  2. data/README +46 -0
  3. data/bin/lyrics-ebook +47 -0
  4. data/lib/eeepub/LICENSE +20 -0
  5. data/lib/eeepub/README.md +77 -0
  6. data/lib/eeepub/Rakefile +49 -0
  7. data/lib/eeepub/VERSION +1 -0
  8. data/lib/eeepub/examples/files/bar.html +42 -0
  9. data/lib/eeepub/examples/files/foo.html +42 -0
  10. data/lib/eeepub/examples/simple_epub.rb +25 -0
  11. data/lib/eeepub/lib/eeepub.rb +17 -0
  12. data/lib/eeepub/lib/eeepub/container_item.rb +108 -0
  13. data/lib/eeepub/lib/eeepub/easy.rb +100 -0
  14. data/lib/eeepub/lib/eeepub/maker.rb +127 -0
  15. data/lib/eeepub/lib/eeepub/ncx.rb +68 -0
  16. data/lib/eeepub/lib/eeepub/ocf.rb +112 -0
  17. data/lib/eeepub/lib/eeepub/opf.rb +148 -0
  18. data/lib/eeepub/spec/eeepub/easy_spec.rb +66 -0
  19. data/lib/eeepub/spec/eeepub/maker_spec.rb +124 -0
  20. data/lib/eeepub/spec/eeepub/ncx_spec.rb +80 -0
  21. data/lib/eeepub/spec/eeepub/ocf_spec.rb +43 -0
  22. data/lib/eeepub/spec/eeepub/opf_spec.rb +262 -0
  23. data/lib/eeepub/spec/eeepub_spec.rb +4 -0
  24. data/lib/eeepub/spec/spec_helper.rb +12 -0
  25. data/lib/lyrics_ebook.rb +71 -0
  26. data/lib/lyrics_ebook/cache2html.rb +33 -0
  27. data/lib/lyrics_ebook/cache_manager.rb +77 -0
  28. data/lib/lyrics_ebook/lyric.rb +32 -0
  29. data/lib/lyrics_ebook/lyric_download.rb +50 -0
  30. data/lib/lyrics_ebook/lyrics2html.rb +106 -0
  31. data/lib/lyrics_ebook/mini_lyrics_parser.rb +26 -0
  32. data/lib/lyrics_ebook/mp3_parser.rb +37 -0
  33. data/lib/wiki_lyrics/amarok/COPYING +340 -0
  34. data/lib/wiki_lyrics/amarok/README +51 -0
  35. data/lib/wiki_lyrics/amarok/amarok.rb +355 -0
  36. data/lib/wiki_lyrics/amarok/pluginadapter.rb +113 -0
  37. data/lib/wiki_lyrics/amarok/plugins.rb +95 -0
  38. data/lib/wiki_lyrics/amarok/wikilyrics.rb +578 -0
  39. data/lib/wiki_lyrics/amarok/wikilyrics.spec +4 -0
  40. data/lib/wiki_lyrics/amarok/wikipluginadapter.rb +392 -0
  41. data/lib/wiki_lyrics/cli/optionsparser.rb +228 -0
  42. data/lib/wiki_lyrics/cli/pluginadapter.rb +56 -0
  43. data/lib/wiki_lyrics/cli/plugins.rb +79 -0
  44. data/lib/wiki_lyrics/cli/wikilyrics.rb +178 -0
  45. data/lib/wiki_lyrics/cli/wikipluginadapter.rb +140 -0
  46. data/lib/wiki_lyrics/docs/COPYING +340 -0
  47. data/lib/wiki_lyrics/docs/ChangeLog +293 -0
  48. data/lib/wiki_lyrics/docs/HOWTO.pdf +0 -0
  49. data/lib/wiki_lyrics/docs/HOWTO.tex +151 -0
  50. data/lib/wiki_lyrics/docs/HOWTO.txt +139 -0
  51. data/lib/wiki_lyrics/docs/README +45 -0
  52. data/lib/wiki_lyrics/docs/TODO +12 -0
  53. data/lib/wiki_lyrics/gui/gui-gtk.rb +945 -0
  54. data/lib/wiki_lyrics/gui/gui-qt3.rb +785 -0
  55. data/lib/wiki_lyrics/gui/gui-qt4.rb +845 -0
  56. data/lib/wiki_lyrics/gui/gui-tk.rb +1104 -0
  57. data/lib/wiki_lyrics/gui/gui.rb +268 -0
  58. data/lib/wiki_lyrics/gui/test.rb +74 -0
  59. data/lib/wiki_lyrics/i18n/README +1 -0
  60. data/lib/wiki_lyrics/i18n/en.rb +181 -0
  61. data/lib/wiki_lyrics/i18n/es.rb +181 -0
  62. data/lib/wiki_lyrics/i18n/i18n.rb +126 -0
  63. data/lib/wiki_lyrics/i18n/sk.rb +174 -0
  64. data/lib/wiki_lyrics/itrans/COPYRIGHT +31 -0
  65. data/lib/wiki_lyrics/itrans/itrans +0 -0
  66. data/lib/wiki_lyrics/itrans/itrans.txt +8 -0
  67. data/lib/wiki_lyrics/itrans/lyric.txt +23 -0
  68. data/lib/wiki_lyrics/itrans/udvng.ifm +206 -0
  69. data/lib/wiki_lyrics/lyrics.rb +567 -0
  70. data/lib/wiki_lyrics/lyrics_AZLyrics.rb +113 -0
  71. data/lib/wiki_lyrics/lyrics_DarkLyrics.rb +124 -0
  72. data/lib/wiki_lyrics/lyrics_Giitaayan.rb +124 -0
  73. data/lib/wiki_lyrics/lyrics_Jamendo.rb +166 -0
  74. data/lib/wiki_lyrics/lyrics_LeosLyrics.rb +142 -0
  75. data/lib/wiki_lyrics/lyrics_LoudSongs.rb +135 -0
  76. data/lib/wiki_lyrics/lyrics_LyricWiki.rb +329 -0
  77. data/lib/wiki_lyrics/lyrics_LyricsDownload.rb +118 -0
  78. data/lib/wiki_lyrics/lyrics_LyricsMania.rb +141 -0
  79. data/lib/wiki_lyrics/lyrics_Lyriki.rb +287 -0
  80. data/lib/wiki_lyrics/lyrics_SeekLyrics.rb +108 -0
  81. data/lib/wiki_lyrics/lyrics_Sing365.rb +103 -0
  82. data/lib/wiki_lyrics/lyrics_TerraLetras.rb +126 -0
  83. data/lib/wiki_lyrics/mediawikilyrics.rb +1464 -0
  84. data/lib/wiki_lyrics/tests/plugin_tests.rb +161 -0
  85. data/lib/wiki_lyrics/tests/tests.rb +74 -0
  86. data/lib/wiki_lyrics/utils/formdata.rb +56 -0
  87. data/lib/wiki_lyrics/utils/htmlentities.rb +291 -0
  88. data/lib/wiki_lyrics/utils/http.rb +198 -0
  89. data/lib/wiki_lyrics/utils/itrans.rb +160 -0
  90. data/lib/wiki_lyrics/utils/kde.rb +88 -0
  91. data/lib/wiki_lyrics/utils/logger.rb +123 -0
  92. data/lib/wiki_lyrics/utils/strings.rb +378 -0
  93. data/lib/wiki_lyrics/utils/xmlhash.rb +111 -0
  94. data/lib/wiki_lyrics/wikilyrics.kdevelop +152 -0
  95. data/lib/wiki_lyrics/wikilyrics.kdevses +18 -0
  96. data/lib/wiki_lyrics/win/gtk_fix.bat +3 -0
  97. data/lib/wiki_lyrics/win/tk_fix.bat +3 -0
  98. data/lib/wiki_lyrics/win/wikilyrics.bat +4 -0
  99. data/lib/wiki_lyrics/win/win_fix.rb +52 -0
  100. data/templates/art_.erb.xhtml +29 -0
  101. data/templates/artists.erb.xhtml +19 -0
  102. data/templates/cover.erb.xhtml +22 -0
  103. data/test/cache_manager_test.rb +73 -0
  104. data/test/data/artist name/title name.lyric +6 -0
  105. data/test/lyric_test.rb +41 -0
  106. metadata +194 -0
@@ -0,0 +1,151 @@
1
+ \documentclass[english,11pt,notitlepage]{article}
2
+ \usepackage[T1]{fontenc}
3
+ \usepackage[latin1]{inputenc}
4
+ \usepackage{amsmath}
5
+ \usepackage{babel}
6
+
7
+ \usepackage[a4paper,headheight=16pt,scale={0.8,0.8},hoffset=0cm]{geometry}
8
+
9
+ \newcommand{\noun}[1]{\textsc{#1}}
10
+
11
+ \newenvironment{codecomment}
12
+ % {\begin{list}{}{\leftmargin0.5cm\rightmargin0.5cm}\item{}\begin{footnotesize}}
13
+ {\begin{list}{}{\leftmargin0cm\rightmargin0cm}\item{}\begin{small}}
14
+ {\end{small}\end{list}}
15
+
16
+ \title{Wiki-Lyrics HOW-TO}
17
+
18
+ \begin{document}
19
+
20
+ \maketitle
21
+
22
+ \section{Introduction}
23
+
24
+ \subsection{What is Wiki-Lyrics?}
25
+
26
+ \noindent It's an Amarok lyrics script. However, it has some features that makes it ``special'':
27
+
28
+ \begin{itemize}
29
+ \item It can get lyrics (and suggestions) from \emph{a lot of sites}.
30
+ \item It can be easily extended to support other sites.
31
+ \item It automatically searches suggestions for the requested lyrics.
32
+ \item It can upload lyrics, cover art and album data to Lyriki and/or LyricWiki (wiki's for lyrics).
33
+ \end{itemize}
34
+
35
+ \subsection{How does it works}
36
+
37
+ \noindent Wiki-Lyrics itself has a sort of plugin system: in the main script's directory there are some Ruby files named \texttt{lyrics\_*.rb}, each one defining a plugin (a special Ruby class) that can get the lyrics from \emph{one site}. The main script calls them when it needs to fetch lyrics and/or suggestions.
38
+
39
+ \section{The plugins}
40
+
41
+ \subsection{Writing a plugin}
42
+
43
+ \noindent First few lines are ``standard'':
44
+
45
+ \begin{verbatim}
46
+ $LOAD_PATH << File.expand_path(File.dirname(__FILE__))
47
+ require 'utils/strings'
48
+ require 'lyrics'
49
+ \end{verbatim}
50
+
51
+ \noindent This is to import some classes and modules we'll have to use.
52
+
53
+ \noindent Now we can create our class, let's call it FooLyrics:
54
+
55
+ \begin{verbatim}
56
+ class FooLyrics < Lyrics
57
+ \end{verbatim}
58
+
59
+ \noindent First two methods provide basic information about the site and are self explanatory:
60
+
61
+ \begin{verbatim}
62
+ def FooLyrics.site_host()
63
+ return 'www.lyrics.foo.bar'
64
+ end
65
+
66
+ def FooLyrics.site_name()
67
+ return 'Foo'
68
+ end
69
+ \end{verbatim}
70
+
71
+ \noindent OK, that was easy. We just created 2 class-methods that return the URL and the name of the site we are getting the lyrics from. Now, let's write some real code. Though the \texttt{Lyrics} class can be extended in several ways, in the most common scenario (and the one we'll explain here), there are 4 additional methods our class should implement:
72
+
73
+ \begin{verbatim}
74
+ def build_lyrics_fetch_data( artist, title, album=nil, year=nil )
75
+ a = CGI.escape( artist )
76
+ t = CGI.escape( title )
77
+ return { 'url'=>"http://#{site_host()}/index.php?a=#{a}&t=#{t}.html" }
78
+ end
79
+ \end{verbatim}
80
+ \begin{codecomment}
81
+ The method receives song information and must return a hash with at least one key, \texttt{'url'}, and optionally another one, \texttt{'post'}. As the method name suggests, the data returned by this method will be used to fetch the lyrics page for the song in question, with the \texttt{'url'} key being the URL of the page to retrieve. Normally the page will be retrieved through a \texttt{GET} request but if some parameters need to be posted to the site in order to get the lyrics, you can provide them with the optional key \texttt{'post'}, which expects a hash. When \texttt{'post'} is specified, the page will be retrieved through a \texttt{POST} request and the data provided will be sent to the site (note that \texttt{GET} parameters are specified as part of the URL).
82
+ \end{codecomment}
83
+
84
+ \hskip
85
+
86
+ \begin{verbatim}
87
+ def parse_lyrics( url, body, artist, title, album=nil, year=nil )
88
+ lyrics_data = {}
89
+ # isolate the lyrics in the received page body
90
+ ...
91
+ lyrics_data['lyrics'] = body
92
+ return lyrics_data
93
+ end
94
+ \end{verbatim}
95
+ \begin{codecomment}
96
+ The method receives the fetched page and it's URL and the information received by \texttt{build\_lyrics\_fetch\_data}. It also has to return a hash with at least one key, \texttt{'lyrics'}, and other optional keys: \texttt{'artist'}, \texttt{'title'}, \texttt{'album'}, \texttt{'year'} and \texttt{'custom\_data'}. If the page contains the lyrics to the requested song, they must be returned in the \texttt{'lyrics'} key as plain text (not html, though you can leave formatting tags such as \texttt{<b>}, \texttt{<i>} or \texttt{<u>}); otherwise the \texttt{'lyrics'} key should be set to \texttt{nil}.
97
+ You might wonder why return any of the optional keys that are already provided as arguments. The reason is that Wiki-Lyrics does not only retrieves lyrics but can also submit song information to Lyriki and LyricWiki. Instead of only providing the lyrics to the song, Wiki-Lyrics tries to provide those sites with as much (relevant) information as possible. To account for the fact that some information can be missing from the files metadata (or just be inaccurate), all plugins should try to obtain as much information as possible from the lyrics page. The keys that are expected along the lyrics for every plugin (\texttt{'artist'}, \texttt{'title'}, \texttt{'album'} and \texttt{'year'}) will be automatically filled with the values provided to the method if not filled by the plugin (so don't bother filling them unless the data is obtained from the page itself). Any other information available can be returned in the \texttt{'custom\_data'} key, which must contain yet another hash. For example both, Lyriki and LyricWiki plugins return song credits and lyricist this way (as \texttt{'credits'} and \texttt{'lyricist'} keys respectively).
98
+ \end{codecomment}
99
+
100
+ \hskip
101
+
102
+ \begin{verbatim}
103
+ def build_suggestions_fetch_data( artist, title, album=nil, year=nil )
104
+ a = CGI.escape( artist )
105
+ return { 'url'=>"http://#{site_host()}/index.php?search=#{a}.html" }
106
+ end
107
+ \end{verbatim}
108
+ \begin{codecomment}
109
+ This method is similar to \texttt{build\_lyrics\_fetch\_data} but, instead of the URL of the lyrics page, it must return the URL where suggestions for the given song can be found (same things noted before for the \texttt{'post'} key apply here).
110
+ \end{codecomment}
111
+
112
+ \hskip
113
+
114
+ \begin{verbatim}
115
+ def parse_suggestions( url, body, artist, title, album=nil, year=nil )
116
+ suggestions = []
117
+ # isolate the suggestions in the received page body
118
+ body.split( /separator regexp/ ) # split the suggestions in the body
119
+ # analyze the suggestion and extract it's url, artist and title
120
+ suggestions << { 'url'=> s_url, 'artist'=>s_artist, 'title'=>s_title }
121
+ end
122
+ return suggestions
123
+ end
124
+ \end{verbatim}
125
+ \begin{codecomment}
126
+ As the name suggests, this method must parse the suggestions found in the received page body and return them as an array of hashes (one hash per suggestion), each hash containing \texttt{'url'}, \texttt{'artist'} and \texttt{'title'} keys.
127
+ \end{codecomment}
128
+
129
+ \subsubsection{Useful functions}
130
+
131
+ \noindent Wiki-Lyrics includes some functions to help you in some ``standard'' operations. Some of those are listed below\footnote{For other commonly used functions take a look at the Ruby modules in the \texttt{utils} directory.}:
132
+
133
+ \begin{itemize}
134
+ \item \texttt{Strings.latin12utf8(text)} convert text from latin1 to utf-8
135
+ \item \texttt{Strings.utf82latin1(text)} convert text from utf-8 to latin1
136
+ \item \texttt{build\_google\_feeling\_lucky\_url(artist, title)} returns the URL of the ``feeling lucky'' google search
137
+ \end{itemize}
138
+
139
+ \subsection{Save and install the plugin}
140
+ To have Wiki-Lyrics automatically detect plugins, these have to be saved under the script's main directory following the \texttt{lyrics\_PLUGINCLASSNAME.rb} filename convention (\emph{mandatory}, otherwise the plugin won't be loaded).
141
+
142
+ \noindent Countinuing our example, we would have to save our plugin as \texttt{lyrics\_FooLyrics.rb} under Wiki-Lyrics main directory, usually \texttt{\$HOME/.kde/share/apps/amarok/scripts/wiki\_lyrics}.
143
+
144
+ \section{Enjoy}
145
+
146
+ \noindent Now open Amarok, start Wiki-Lyrics script and configure it to use your new plugin.
147
+
148
+ \noindent And, of course, sing along ;-)
149
+
150
+
151
+ \end{document}
@@ -0,0 +1,139 @@
1
+ Wiki-Lyrics HOW-TO
2
+ March 28, 2007
3
+
4
+ 1 Introduction
5
+
6
+ 1.1 What is Wiki-Lyrics?
7
+
8
+ It's an Amarok lyrics script. However, it has some features that makes it "special":
9
+
10
+ • It can get lyrics (and suggestions) from a lot of sites.
11
+ • It can be easily extended to support other sites.
12
+ • It automatically searches suggestions for the requested lyrics.
13
+ • It can upload lyrics, cover art and album data to Lyriki and/or LyricWiki (wiki's for lyrics).
14
+
15
+
16
+ 1.2 How does it works
17
+
18
+ Wiki-Lyrics himself has a sort of plugin system: in the main script's directory there are some Ruby
19
+ files named lyrics_*.rb, each one defining a plugin (a special Ruby class) that can get the lyrics
20
+ from one site. The main script calls them when it needs to fetch lyrics and/or suggestions.
21
+
22
+
23
+ 2 The plugins
24
+
25
+ 2.1 Writing a plugin
26
+
27
+ First few lines are "standard":
28
+
29
+ $LOAD_PATH << File.expand_path(File.dirname(__FILE__))
30
+ require 'utils/strings'
31
+ require 'lyrics'
32
+ This is to import some classes and modules we'll have to use.
33
+ Now we can create our class, let's call it FooLyrics:
34
+
35
+ class FooLyrics < Lyrics
36
+
37
+ First two methods provide basic information about the site and are self explanatory:
38
+
39
+ def FooLyrics.site_host()
40
+ return 'www.lyrics.foo.bar'
41
+ end
42
+
43
+ def FooLyrics.site_name()
44
+ return 'Foo'
45
+ end
46
+
47
+ OK, that was easy. We just created 2 class-methods that return the URL and the name of the site
48
+ we are getting the lyrics from. Now, let's write some real code. Though the Lyrics class can be
49
+ extended in several ways, in the most common scenario (and the one we'll explain here), there are
50
+ 4 additional methods our class should implement:
51
+
52
+ def build_lyrics_fetch_data( artist, title, album=nil, year=nil )
53
+ a = CGI.escape( artist )
54
+ t = CGI.escape( title )
55
+ return { 'url'=>"http://#{site_host()}/index.php?a=#{a}&t=#{t}.html" }
56
+ end
57
+
58
+ The method receives song information and must return a hash with at least one key, 'url', and optionally
59
+ another one, 'post'. As the method name suggests, the data returned by this method will be used to fetch
60
+ the lyrics page for the song in question, with the 'url' key being the URL of the page to retrieve. Normally
61
+ the page will be retrieved through a GET request but if some parameters need to be posted to the site in order
62
+ to get the lyrics, you can provide them with the optional key 'post', which expects a hash. When 'post'
63
+ is specified, the page will be retrieved through a POST request and the data provided will be sent to the site
64
+ (note that GET parameters are specied as part of the URL).
65
+
66
+
67
+ def parse_lyrics( url, body, artist, title, album=nil, year=nil )
68
+ lyrics_data = {}
69
+ # isolate the lyrics in the received page body
70
+ ...
71
+ lyrics_data['lyrics'] = body
72
+ return lyrics_data
73
+ end
74
+
75
+ The method receives the fetched page and it's URL and the information received by build_lyrics_fetch_data.
76
+ It also has to return a hash with at least one key, 'lyrics', and other optional keys: 'artist', 'title',
77
+ 'album', 'year' and 'custom_data'. If the page contains the lyrics to the requested song, they must be
78
+ returned in the 'lyrics' key as plain text (not html, though you can leave formatting tags such as <b>, <i>
79
+ or <u>); otherwise the 'lyrics' key should be set to nil. You might wonder why return any of the optional
80
+ keys that are already provided as arguments. The reason is that Wiki-Lyrics does not only retrieves lyrics but
81
+ can also submit song information to Lyriki and LyricWiki. Instead of only providing the lyrics to the song,
82
+ Wiki-Lyrics tries to provide those sites with as much (relevant) information as possible. To account for the
83
+ fact that some information can be missing from the les metadata (or just be inaccurate), all plugins should
84
+ try to obtain as much information as possible from the lyrics page. The keys that are expected along the
85
+ lyrics for every plugin ('artist', 'title', 'album' and 'year') will be automatically lled with the values
86
+ provided to the method if not lled by the plugin (so don't bother lling them unless the data is obtained
87
+ from the page itself). Any other information available can be returned in the 'custom_data' key, which must
88
+ contain yet another hash. For example both, Lyriki and LyricWiki plugins return song credits and lyricist this
89
+ way (as 'credits' and 'lyricist' keys respectively).
90
+
91
+
92
+ def build_suggestions_fetch_data( artist, title, album=nil, year=nil )
93
+ a = CGI.escape( artist )
94
+ return { 'url'=>"http://#{site_host()}/index.php?search=#{a}.html" }
95
+ end
96
+
97
+ This method is similar to build_lyrics_fetch_data but, instead of the URL of the lyrics page, it must
98
+ return the URL where suggestions for the given song can be found (same things noted before for the 'post'
99
+ key apply here).
100
+
101
+
102
+ def parse_suggestions( url, body, artist, title, album=nil, year=nil )
103
+ suggestions = []
104
+ # isolate the suggestions in the received page body
105
+ body.split( /separator regexp/ ) # split the suggestions in the body
106
+ # analyze the suggestion and extract it's url, artist and title
107
+ suggestions << { 'url'=> s_url, 'artist'=>s_artist, 'title'=>s_title }
108
+ end
109
+ return suggestions
110
+ end
111
+
112
+ As the name suggests, this method must parse the suggestions found in the received page body and return
113
+ them as an array of hashes (one hash per suggestion), each hash containing 'url', 'artist' and 'title'
114
+ keys.
115
+
116
+
117
+ 2.1.1 Useful functions
118
+
119
+ Wiki-Lyrics includes some functions to help you in some "standard" operations. Some of those are
120
+ listed below :
121
+
122
+ • Strings.latin12utf8(text) convert text from latin1 to utf-8
123
+ • Strings.utf82latin1(text) convert text from utf-8 to latin1
124
+ • build_google_feeling_lucky_url(artist, title) returns the URL of the "feeling lucky" google search
125
+
126
+
127
+ 2.2 Save and install the plugin
128
+
129
+ To have Wiki-Lyrics automatically detect plugins, these have to be saved under the script's main
130
+ directory following the lyrics_PLUGINCLASSNAME.rb filename convention (mandatory, otherwise the
131
+ plugin won't be loaded).
132
+
133
+ Countinuing our example, we would have to save our plugin as lyrics_FooLyrics.rb under Wiki-Lyrics main
134
+ directory, usually $HOME/.kde/share/apps/amarok/scripts/wiki_lyrics.
135
+
136
+
137
+ 3 Enjoy
138
+ Now open Amarok, start Wiki-Lyrics script and congure it to use your new plugin.
139
+ And, of course, sing along ;-)
@@ -0,0 +1,45 @@
1
+ Wiki-Lyrics v0.13.4
2
+ ===================
3
+
4
+ About:
5
+ ======
6
+ Wiki-Lyrics is a set of Ruby classes and scripts to interface with various
7
+ lyrics sites and submit content to Lyriki and LyricWiki, wikis for lyrics.
8
+ Currently there's only 2 interfaces: an Amarok script and a standalone
9
+ interface. However, the standalone interface can be used from Winamp and
10
+ other Windows players with the help of the AMIP plugin, and directly from
11
+ other Linux players such as Audacious.
12
+ Visit http://www.lyriki.com/Help:Wiki-Lyrics_Script for more information.
13
+
14
+ Supported sites:
15
+ ================
16
+ - Lyriki (www.lyriki.com)
17
+ - LyricWiki (www.lyricwiki.org)
18
+ - AZ Lyrics (www.azlyrics.com)
19
+ - DarkLyrics (www.darklyrics.com)
20
+ - Giitaayan (www.giitaayan.com)
21
+ - Jamendo (www.jamendo.com)
22
+ - Leos Lyrics (www.leoslyrics.com)
23
+ - Lyrics Download (www.lyricsdownload.com)
24
+ - Lyrics Mania (www.lyricsmania.com)
25
+ - LoudSon.gs (www.loudson.gs)
26
+ - Seek Lyrics (www.seeklyrics.com)
27
+ - Sing365 (www.sing365.com)
28
+ - Terra Letras (letras.terra.com.br)
29
+
30
+ Dependencies:
31
+ =============
32
+ - Ruby 1.8
33
+ - Amarok 1.4 (required only to use the Amarok script)
34
+ - QtRuby, Ruby/GTK or Ruby/Tk (required to see dialogs and submit content)
35
+
36
+ Authors:
37
+ ========
38
+ - Sergio Pistone (sergio_pistone@yahoo.com.ar)
39
+ - Eduardo Robles Elvira (edulix@gmail.com) (Jamendo)
40
+ - Swapan Sarkar (swapan@yahoo.com) (Giitaayan)
41
+ - Davide Lo Re (boyska@gmail.com) (Lyrics Mania)
42
+
43
+ Translators:
44
+ ============
45
+ - Jozef Riha (Slovak)
@@ -0,0 +1,12 @@
1
+ * add method to indicate site's encoding and convert to utf8 before processing if needed
2
+ * add method to preprocess fetched content (siplify spaces, escape entities, etc.)
3
+ * try to guess the text language (to apply metadata capitalization accordingly)
4
+
5
+ * other possible sites:
6
+ http://www.getlyrics.com/
7
+ http://www.lyrics4all.net/
8
+ http://www.songs-lyrics.net/
9
+ http://www.lyricskeeper.com/
10
+ http://lyrics.astraweb.com
11
+ http://www.planetadeletras.com/
12
+ http://www.songs-lyrics.net/
@@ -0,0 +1,945 @@
1
+ # Copyright (C) 2006-2008 by Sergio Pistone
2
+ # sergio_pistone@yahoo.com.ar
3
+ #
4
+ # This program is free software; you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation; either version 2 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, write to the
16
+ # Free Software Foundation, Inc.,
17
+ # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
+
19
+ require "gtk2"
20
+ require "date"
21
+ require File.expand_path( File.dirname( __FILE__ ) + "/../i18n/i18n" )
22
+
23
+ Gtk.init()
24
+
25
+ module GTK
26
+
27
+ def GTK.empty_string?( text )
28
+ return text ? (text.empty? ? true : text.strip().empty? ) : true
29
+ end
30
+
31
+ begin
32
+ require "gtksourceview1"
33
+ class TextView < Gtk::SourceView
34
+ # def initialize( *args )
35
+ # super( *args )
36
+ # show_line_markers = false
37
+ # show_line_numbers = false
38
+ # end
39
+ end
40
+ class TextBuffer < Gtk::SourceBuffer
41
+ end
42
+ rescue LoadError => e
43
+ class TextView < Gtk::TextView
44
+ end
45
+ class TextBuffer < Gtk::TextBuffer
46
+ end
47
+ end
48
+
49
+ class BaseDialog < Gtk::Window
50
+
51
+ attr_reader :accepted
52
+ attr_reader :values
53
+
54
+ def initialize( values, close_on_escape=true )
55
+ super()
56
+ @values = values
57
+ set_window_position( Gtk::Window::POS_CENTER )
58
+ signal_connect( "destroy" ) { Gtk.main_quit() }
59
+ if close_on_escape
60
+ self.signal_connect( "key_press_event" ) do |widget, event|
61
+ destroy() if event.keyval == Gdk::Keyval::GDK_Escape
62
+ end
63
+ end
64
+ end
65
+
66
+ def create_action_buttons( mode="right", accept_text=nil, cancel_text=nil )
67
+ accept_text = GTK.empty_string?( accept_text ) ? I18n.get( "gui.common.accept" ) : accept_text
68
+ cancel_text = GTK.empty_string?( cancel_text ) ? I18n.get( "gui.common.cancel" ) : cancel_text
69
+ @accept_button = Gtk::Button.new( accept_text )
70
+ @cancel_button = Gtk::Button.new( cancel_text )
71
+ @accept_button.signal_connect( "clicked" ) { accept() }
72
+ @cancel_button.signal_connect( "clicked" ) { destroy() }
73
+ if mode == "left"
74
+ hbox = Gtk::HButtonBox.new();
75
+ hbox.layout_style = Gtk::ButtonBox::START
76
+ hbox.add( @accept_button )
77
+ hbox.add( @cancel_button )
78
+ elsif mode == "split"
79
+ hbox = Gtk::HBox.new();
80
+ hbox.pack_start( @accept_button )
81
+ hbox.pack_start( @cancel_button )
82
+ else
83
+ hbox = Gtk::HButtonBox.new();
84
+ hbox.layout_style = Gtk::ButtonBox::END
85
+ hbox.add( @accept_button )
86
+ hbox.add( @cancel_button )
87
+ end
88
+ return hbox
89
+ end
90
+ protected :create_action_buttons
91
+
92
+ def exec()
93
+ @accepted = false
94
+ set_modal( true )
95
+ show_all()
96
+ end
97
+
98
+ def accept()
99
+ @accepted = true
100
+ destroy()
101
+ end
102
+
103
+ end
104
+
105
+
106
+ class PluginsManagerDialog < BaseDialog
107
+
108
+ def initialize( values )
109
+ super( values )
110
+
111
+ set_border_width( 5 )
112
+ set_resizable( true )
113
+ set_default_size( 400, 280 )
114
+
115
+ set_title( I18n.get( "gui.pluginsmanager.title", @values["script_name"] ) )
116
+
117
+ sites_group = Gtk::Frame.new( I18n.get( "gui.pluginsmanager.sites" ) )
118
+
119
+ @move_up_button = Gtk::Button.new( I18n.get( "gui.pluginsmanager.sites.moveup" ) )
120
+ @move_down_button = Gtk::Button.new( I18n.get( "gui.pluginsmanager.sites.movedown" ) )
121
+
122
+ @used_plugins_store = Gtk::ListStore.new( String )
123
+ @used_plugins_view = Gtk::TreeView.new( @used_plugins_store )
124
+ column = Gtk::TreeViewColumn.new( I18n.get( "gui.pluginsmanager.sites.inuse" ), Gtk::CellRendererText.new(), :text => 0 )
125
+ @used_plugins_view.append_column( column )
126
+ used_plugins_viewport = Gtk::Viewport.new(
127
+ @used_plugins_view.focus_hadjustment(),
128
+ @used_plugins_view.focus_vadjustment()
129
+ )
130
+ used_plugins_viewport.set_shadow_type( Gtk::SHADOW_IN )
131
+ used_plugins_viewport.add( @used_plugins_view )
132
+ used_plugins_scrolledwindow = Gtk::ScrolledWindow.new()
133
+ used_plugins_scrolledwindow.add( used_plugins_viewport )
134
+ @values["used_plugins"].each() do |plugin|
135
+ iter = @used_plugins_store.append()
136
+ @used_plugins_store.set_value( iter, 0, plugin )
137
+ end
138
+
139
+ @add_button = Gtk::Button.new( I18n.get( "gui.pluginsmanager.sites.add" ) )
140
+ @remove_button = Gtk::Button.new( I18n.get( "gui.pluginsmanager.sites.remove" ) )
141
+
142
+ @unused_plugins_store = Gtk::ListStore.new( String )
143
+ @unused_plugins_view = Gtk::TreeView.new( @unused_plugins_store )
144
+ column = Gtk::TreeViewColumn.new( I18n.get( "gui.pluginsmanager.sites.available" ), Gtk::CellRendererText.new(), :text=>0 )
145
+ @unused_plugins_view.append_column( column )
146
+ unused_plugins_viewport = Gtk::Viewport.new(
147
+ @unused_plugins_view.focus_hadjustment(),
148
+ @unused_plugins_view.focus_vadjustment()
149
+ )
150
+ unused_plugins_viewport.set_shadow_type( Gtk::SHADOW_IN )
151
+ unused_plugins_viewport.add( @unused_plugins_view )
152
+ unused_plugins_scrolledwindow = Gtk::ScrolledWindow.new()
153
+ unused_plugins_scrolledwindow.add( unused_plugins_viewport )
154
+ @values["unused_plugins"].each() do |plugin|
155
+ iter = @unused_plugins_store.append()
156
+ @unused_plugins_store.set_value( iter, 0, plugin )
157
+ end
158
+
159
+ misc_group = Gtk::Frame.new( I18n.get( "gui.pluginsmanager.misc" ) )
160
+
161
+ @cleanup_lyrics_checkbox = Gtk::CheckButton.new( I18n.get( "gui.pluginsmanager.misc.cleanup" ) )
162
+ @cleanup_lyrics_checkbox.set_active( values["cleanup_lyrics"].to_s() == "true" )
163
+
164
+ @single_threaded_checkbox = Gtk::CheckButton.new( I18n.get( "gui.pluginsmanager.misc.singlethreaded" ) )
165
+ @single_threaded_checkbox.set_active( values["single_threaded"].to_s() == "true" )
166
+
167
+ @write_log_checkbox = Gtk::CheckButton.new( I18n.get( "gui.pluginsmanager.misc.writelog", "$HOME/.wikilyrics.log" ) )
168
+ @write_log_checkbox.set_active( values["write_log"].to_s() == "true" )
169
+
170
+ buttons = create_action_buttons()
171
+
172
+ group_grid = Gtk::Table.new( 4, 4, false )
173
+ group_grid.set_row_spacings( 5 )
174
+ group_grid.set_column_spacings( 5 )
175
+ group_grid.attach( @move_up_button, 0, 1, 1, 2, Gtk::FILL, 0 )
176
+ group_grid.attach( @move_down_button, 0, 1, 2, 3, Gtk::FILL, 0 )
177
+ group_grid.attach( used_plugins_scrolledwindow, 1, 2, 0, 4, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND )
178
+ group_grid.attach( @add_button, 2, 3, 1, 2, Gtk::FILL, 0 )
179
+ group_grid.attach( @remove_button, 2, 3, 2, 3, Gtk::FILL, 0 )
180
+ group_grid.attach( unused_plugins_scrolledwindow, 3, 4, 0, 4, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND )
181
+ sites_group.add( group_grid )
182
+
183
+ misc_group_vbox = Gtk::VBox.new( false, 0 );
184
+ misc_group_vbox.add( @cleanup_lyrics_checkbox )
185
+ misc_group_vbox.add( @single_threaded_checkbox )
186
+ misc_group_vbox.add( @write_log_checkbox )
187
+ misc_group.add( misc_group_vbox )
188
+
189
+ vbox = Gtk::VBox.new( false, 3 );
190
+ vbox.pack_start( sites_group, true )
191
+ vbox.pack_start( misc_group, false )
192
+ vbox.pack_start( buttons, false )
193
+
194
+ add( vbox )
195
+
196
+ @move_up_button.signal_connect( "clicked" ) { move_up() }
197
+ @move_down_button.signal_connect( "clicked" ) { move_down() }
198
+ @add_button.signal_connect( "clicked" ) { add_plugin() }
199
+ @remove_button.signal_connect( "clicked" ) { remove_plugin() }
200
+
201
+ update_accept_button()
202
+ end
203
+
204
+ def move_up()
205
+ sel_iter = @used_plugins_view.selection().selected()
206
+ return if sel_iter == nil || sel_iter == @used_plugins_store.iter_first()
207
+ sel_path = sel_iter.path()
208
+ plugin = sel_iter.get_value( 0 )
209
+ @used_plugins_store.remove( sel_iter )
210
+ sel_path.prev!()
211
+ prev_iter = @used_plugins_store.get_iter( sel_path )
212
+ iter = @used_plugins_store.insert_before( prev_iter )
213
+ @used_plugins_store.set_value( iter, 0, plugin )
214
+ @used_plugins_view.selection().select_iter( iter )
215
+ end
216
+
217
+ def move_down()
218
+ sel_iter = @used_plugins_view.selection().selected()
219
+ return if sel_iter == nil
220
+ plugin = sel_iter.get_value( 0 )
221
+ sel_path = sel_iter.path()
222
+ return if ! sel_iter.next!()
223
+ next_iter = sel_iter
224
+ iter = @used_plugins_store.insert_after( next_iter )
225
+ @used_plugins_store.set_value( iter, 0, plugin )
226
+ @used_plugins_store.remove( @used_plugins_store.get_iter( sel_path ) )
227
+ @used_plugins_view.selection().select_iter( iter )
228
+ end
229
+
230
+ def add_plugin()
231
+ unused_sel_iter = @unused_plugins_view.selection().selected()
232
+ return if unused_sel_iter == nil
233
+ plugin = unused_sel_iter.get_value( 0 )
234
+ @unused_plugins_store.remove( unused_sel_iter )
235
+ iter = @used_plugins_store.append()
236
+ @used_plugins_store.set_value( iter, 0, plugin )
237
+ @used_plugins_view.selection().select_iter( iter )
238
+ update_accept_button()
239
+ end
240
+
241
+ def remove_plugin()
242
+ used_sel_iter = @used_plugins_view.selection().selected()
243
+ return if used_sel_iter == nil
244
+ plugin = used_sel_iter.get_value( 0 )
245
+ @used_plugins_store.remove( used_sel_iter )
246
+ iter = @unused_plugins_store.append()
247
+ @unused_plugins_store.set_value( iter, 0, plugin )
248
+ @unused_plugins_view.selection().select_iter( iter )
249
+ update_accept_button()
250
+ end
251
+
252
+ def update_accept_button()
253
+ @accept_button.set_sensitive( @used_plugins_store.iter_first() )
254
+ end
255
+
256
+ def accept()
257
+ @values = {
258
+ "cleanup_lyrics" => @cleanup_lyrics_checkbox.active?(),
259
+ "single_threaded" => @single_threaded_checkbox.active?(),
260
+ "write_log" => @write_log_checkbox.active?(),
261
+ "used_plugins" => [],
262
+ "script_name" => @values["script_name"],
263
+ "unused_plugins" => []
264
+ }
265
+ iter = @used_plugins_store.iter_first()
266
+ while ( iter )
267
+ @values["used_plugins"].insert( -1, iter.get_value( 0 ) )
268
+ break if ! iter.next!()
269
+ end
270
+ iter = @unused_plugins_store.iter_first()
271
+ while ( iter )
272
+ @values["unused_plugins"].insert( -1, iter.get_value( 0 ) )
273
+ break if ! iter.next!()
274
+ end
275
+ super()
276
+ end
277
+
278
+ end
279
+
280
+
281
+ class WikiPluginDialog < BaseDialog
282
+
283
+ def initialize( values )
284
+ super( values )
285
+
286
+ set_border_width( 5 )
287
+ set_resizable( true )
288
+ set_default_size( 300, 50 )
289
+
290
+ set_title( I18n.get( "gui.wikiplugin.title", values["site_name"] ) )
291
+
292
+ general_group = Gtk::Frame.new( I18n.get( "gui.wikiplugin.general" ) )
293
+
294
+ @submit_checkbox = Gtk::CheckButton.new( I18n.get( "gui.wikiplugin.general.submit", values["site_name"] ) )
295
+ @submit_checkbox.set_active( values["submit"].to_s() == "true" )
296
+
297
+ @review_checkbox = Gtk::CheckButton.new( I18n.get( "gui.wikiplugin.general.review" ) )
298
+ @review_checkbox.set_sensitive( @submit_checkbox.active?() )
299
+ @review_checkbox.set_active( @review_checkbox.sensitive?() && values["review"].to_s() == "true" )
300
+
301
+ @prompt_autogen_checkbox = Gtk::CheckButton.new( I18n.get( "gui.wikiplugin.general.autogen" ) )
302
+ @prompt_autogen_checkbox.set_sensitive( @review_checkbox.active?() )
303
+ @prompt_autogen_checkbox.set_active( @prompt_autogen_checkbox.sensitive?() && values["prompt_autogen"].to_s() == "true" )
304
+
305
+ @prompt_no_lyrics_checkbox = Gtk::CheckButton.new( I18n.get( "gui.wikiplugin.general.nolyrics" ) )
306
+ @prompt_no_lyrics_checkbox.set_sensitive( @review_checkbox.active?() )
307
+ @prompt_no_lyrics_checkbox.set_active( @prompt_no_lyrics_checkbox.sensitive?() && values["prompt_no_lyrics"].to_s() == "true" )
308
+
309
+ login_group = Gtk::Frame.new( I18n.get( "gui.wikiplugin.login" ) )
310
+
311
+ username_label = Gtk::Label.new( I18n.get( "gui.wikiplugin.login.username" ) );
312
+ username_label.set_xalign( 0 )
313
+ @username_lineedit = Gtk::Entry.new()
314
+ @username_lineedit.set_text( values["username"] )
315
+
316
+ password_label = Gtk::Label.new( I18n.get( "gui.wikiplugin.login.password" ) );
317
+ password_label.set_xalign( 0 )
318
+ @password_lineedit = Gtk::Entry.new()
319
+ @password_lineedit.set_text( values["password"] )
320
+ @password_lineedit.visibility = false
321
+
322
+ @username_lineedit.signal_connect( "changed" ) { update_accept_button_state() }
323
+ @submit_checkbox.signal_connect( "toggled" ) { toggle_submit_checked() }
324
+ @review_checkbox.signal_connect( "toggled" ) { toggle_review_checked() }
325
+
326
+ buttons = create_action_buttons()
327
+ update_accept_button_state()
328
+
329
+ general_grid = Gtk::Table.new( 4, 4, false )
330
+ general_grid.set_row_spacings( 3 )
331
+ general_grid.set_column_spacings( 3 )
332
+ general_grid.attach( @submit_checkbox, 0, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, 0 )
333
+ general_grid.attach( @review_checkbox, 0, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, 0, 0 )
334
+ general_grid.attach( @prompt_autogen_checkbox, 0, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, 0 )
335
+ general_grid.attach( @prompt_no_lyrics_checkbox, 0, 2, 3, 4, Gtk::EXPAND|Gtk::FILL, 0 )
336
+ general_group.add( general_grid )
337
+
338
+ login_grid = Gtk::Table.new( 4, 4, false )
339
+ login_grid.set_row_spacings( 3 )
340
+ login_grid.set_column_spacings( 3 )
341
+ login_grid.attach( username_label, 0, 1, 0, 1, Gtk::SHRINK|Gtk::FILL, 0, 5 )
342
+ login_grid.attach( @username_lineedit, 1, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, 0 )
343
+ login_grid.attach( password_label, 0, 1, 1, 2, Gtk::SHRINK|Gtk::FILL, 0, 5 )
344
+ login_grid.attach( @password_lineedit, 1, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, 0 )
345
+ login_group.add( login_grid )
346
+
347
+ vbox = Gtk::VBox.new( false, 3 );
348
+ vbox.add( general_group )
349
+ vbox.add( login_group )
350
+ vbox.pack_start( buttons, false )
351
+
352
+ add( vbox )
353
+ end
354
+
355
+ def toggle_submit_checked()
356
+ checked = @submit_checkbox.active?()
357
+ @review_checkbox.set_sensitive( checked )
358
+ if ! checked
359
+ @review_checkbox.set_active( false )
360
+ @prompt_autogen_checkbox.set_active( false )
361
+ @prompt_no_lyrics_checkbox.set_active( false )
362
+ end
363
+ update_accept_button_state()
364
+ end
365
+
366
+ def toggle_review_checked()
367
+ checked = @review_checkbox.active?()
368
+ @prompt_autogen_checkbox.set_sensitive( checked )
369
+ @prompt_no_lyrics_checkbox.set_sensitive( checked )
370
+ if ! checked
371
+ @prompt_autogen_checkbox.set_active( false )
372
+ @prompt_no_lyrics_checkbox.set_active( false )
373
+ end
374
+ end
375
+
376
+ def update_accept_button_state()
377
+ @accept_button.set_sensitive( ! @submit_checkbox.active?() || ! @username_lineedit.text().empty? )
378
+ end
379
+
380
+ def accept()
381
+ @values = {
382
+ "submit" => @submit_checkbox.active?(),
383
+ "review" => @review_checkbox.active?(),
384
+ "prompt_autogen" => @prompt_autogen_checkbox.active?(),
385
+ "prompt_no_lyrics" => @prompt_no_lyrics_checkbox.active?(),
386
+ "username" => @username_lineedit.text(),
387
+ "password" => @password_lineedit.text(),
388
+ }
389
+ super()
390
+ end
391
+ end
392
+
393
+ class SubmitSongDialog < BaseDialog
394
+
395
+ def initialize( values )
396
+ super( values )
397
+
398
+ set_border_width( 5 )
399
+ set_resizable( true )
400
+ set_default_size( 600, 400 )
401
+ resize( 600, 400 )
402
+
403
+ edit_mode = @values["edit_mode"].to_s() == "true"
404
+
405
+ set_title( I18n.get( "gui.submitsong.title." + (edit_mode ? "edit" : "submit"), values["site_name"] ) )
406
+
407
+ artist_label = Gtk::Label.new( "<b>#{I18n.get( "gui.common.artist" )}</b>" );
408
+ artist_label.set_xalign( 0.9 )
409
+ artist_label.set_use_markup( true )
410
+ @artist_lineedit = Gtk::Entry.new()
411
+ @artist_lineedit.set_text( values["artist"] )
412
+
413
+ title_label = Gtk::Label.new( "<b>#{I18n.get( "gui.common.song" )}</b>" );
414
+ title_label.set_xalign( 0.9 )
415
+ title_label.set_use_markup( true )
416
+ @title_lineedit = Gtk::Entry.new()
417
+ @title_lineedit.set_text( values["title"] )
418
+
419
+ credits_label = Gtk::Label.new( "<b>#{I18n.get( "gui.common.credits" )}</b>" );
420
+ credits_label.set_xalign( 0.9 )
421
+ credits_label.set_use_markup( true )
422
+ @credits_lineedit = Gtk::Entry.new()
423
+ @credits_lineedit.set_text( values["credits"] )
424
+
425
+ lyricist_label = Gtk::Label.new( "<b>#{I18n.get( "gui.common.lyricist" )}</b>" );
426
+ lyricist_label.set_xalign( 0.9 )
427
+ lyricist_label.set_use_markup( true )
428
+ @lyricist_lineedit = Gtk::Entry.new()
429
+ @lyricist_lineedit.set_text( values["lyricist"] )
430
+
431
+ year_label = Gtk::Label.new( "<b>#{I18n.get( "gui.common.year" )}</b>" );
432
+ year_label.set_xalign( 0.9 )
433
+ year_label.set_use_markup( true )
434
+ @year_spinbox = Gtk::SpinButton.new( Gtk::Adjustment.new( values["year"], 1900, Date.today().year, 1, 10, 0 ) )
435
+ @year_spinbox.set_numeric( true )
436
+
437
+ album_label = Gtk::Label.new( "<b>#{I18n.get( "gui.common.album" )}</b>" );
438
+ album_label.set_xalign( 0.9 )
439
+ album_label.set_use_markup( true )
440
+ @album_lineedit = Gtk::Entry.new()
441
+ @album_lineedit.set_text( values["album"] )
442
+
443
+ @instrumental_checkbox = Gtk::CheckButton.new( I18n.get( "gui.submitsong.instrumental" ) )
444
+ @instrumental_checkbox.set_active( values["instrumental"].to_s == "true" )
445
+ @instrumental_checkbox.signal_connect( "toggled" ) { toggle_instrumental_checked() }
446
+
447
+ lyrics_scrolled_window = Gtk::ScrolledWindow.new()
448
+ lyrics_scrolled_window.set_policy( Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC )
449
+ @lyrics_text_buffer = GTK::TextBuffer.new()
450
+ @lyrics_text_buffer.set_text( values["lyrics"] )
451
+ @lyrics_text = GTK::TextView.new()
452
+ @lyrics_text.set_indent( 2 )
453
+ @lyrics_text.set_buffer( @lyrics_text_buffer )
454
+ lyrics_scrolled_window.add( @lyrics_text )
455
+ @lyrics_text.set_sensitive( ! @instrumental_checkbox.active?() )
456
+
457
+ @reviewed_checkbox = Gtk::CheckButton.new( I18n.get( "gui.common.reviewed" ) )
458
+ @reviewed_checkbox.set_active( false )
459
+
460
+ buttons = create_action_buttons( "split", I18n.get( "gui.common.submit" ) )
461
+
462
+ lyrics_frame = Gtk::Frame.new()
463
+ lyrics_frame.add( lyrics_scrolled_window )
464
+ lyrics_frame.set_shadow_type( Gtk::SHADOW_IN )
465
+
466
+ grid = Gtk::Table.new( 9, 4, false )
467
+ grid.set_row_spacings( 3 )
468
+ grid.attach( artist_label, 0, 1, 0, 1, Gtk::SHRINK|Gtk::FILL, 0, 5 )
469
+ grid.attach( @artist_lineedit, 1, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, 0 )
470
+ grid.attach( title_label, 2, 3, 0, 1, Gtk::SHRINK|Gtk::FILL, 0, 5 )
471
+ grid.attach( @title_lineedit, 3, 4, 0, 1, Gtk::EXPAND|Gtk::FILL, 0 )
472
+ grid.attach( credits_label, 0, 1, 1, 2, Gtk::SHRINK|Gtk::FILL, 0, 5 )
473
+ grid.attach( @credits_lineedit, 1, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, 0 )
474
+ grid.attach( lyricist_label, 2, 3, 1, 2, Gtk::SHRINK|Gtk::FILL, 0, 5 )
475
+ grid.attach( @lyricist_lineedit, 3, 4, 1, 2, Gtk::EXPAND|Gtk::FILL, 0 )
476
+ grid.attach( year_label, 0, 1, 2, 3, Gtk::SHRINK|Gtk::FILL, 0, 5 )
477
+ grid.attach( @year_spinbox, 1, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, 0 )
478
+ grid.attach( album_label, 2, 3, 2, 3, Gtk::SHRINK|Gtk::FILL, 0, 5 )
479
+ grid.attach( @album_lineedit, 3, 4, 2, 3, Gtk::EXPAND|Gtk::FILL, 0 )
480
+ grid.attach( @instrumental_checkbox, 0, 4, 3, 4, Gtk::EXPAND|Gtk::FILL, 0 )
481
+ grid.attach( lyrics_frame, 0, 4, 4, 5, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL )
482
+ grid.attach( @reviewed_checkbox, 0, 4, 5, 6, Gtk::EXPAND|Gtk::FILL, 0 )
483
+ grid.attach( buttons, 0, 4, 6, 7, Gtk::EXPAND|Gtk::FILL, 0 )
484
+
485
+ add( grid )
486
+
487
+ end
488
+
489
+ def toggle_instrumental_checked()
490
+ @lyrics_text.set_sensitive( ! @instrumental_checkbox.active?() )
491
+ end
492
+
493
+ def accept()
494
+ @values = {
495
+ "artist" => @artist_lineedit.text(),
496
+ "year" => @year_spinbox.value().to_i(),
497
+ "album" => @album_lineedit.text(),
498
+ "title" => @title_lineedit.text(),
499
+ "lyrics" => @lyrics_text_buffer.text(),
500
+ "instrumental" => @instrumental_checkbox.active?(),
501
+ "lyricist" => @lyricist_lineedit.text(),
502
+ "credits" => @credits_lineedit.text(),
503
+ "reviewed" => @reviewed_checkbox.active?()
504
+ }
505
+ super()
506
+ end
507
+
508
+ end
509
+
510
+
511
+ class SubmitAlbumDialog < BaseDialog
512
+
513
+ def initialize( values )
514
+ super( values )
515
+
516
+ set_border_width( 5 )
517
+ set_resizable( true )
518
+ set_default_size( 600, 400 )
519
+ resize( 600, 400 )
520
+
521
+ set_title( I18n.get( "gui.submitalbum.title", values["site_name"] ) )
522
+
523
+ artist_label = Gtk::Label.new( "<b>#{I18n.get( "gui.common.artist" )}</b>" );
524
+ artist_label.set_xalign( 0.9 )
525
+ artist_label.set_use_markup( true )
526
+ @artist_lineedit = Gtk::Entry.new()
527
+ @artist_lineedit.set_text( values["artist"] )
528
+
529
+ album_label = Gtk::Label.new( "<b>#{I18n.get( "gui.common.album" )}</b>" );
530
+ album_label.set_xalign( 0.9 )
531
+ album_label.set_use_markup( true )
532
+ @album_lineedit = Gtk::Entry.new()
533
+ @album_lineedit.set_text( values["album"] )
534
+
535
+ year_label = Gtk::Label.new( "<b>#{I18n.get( "gui.common.year" )}</b>" );
536
+ year_label.set_xalign( 0.9 )
537
+ year_label.set_use_markup( true )
538
+ @year_spinbox = Gtk::SpinButton.new( Gtk::Adjustment.new( values["year"].to_i(), 1900, Date.today().year, 1, 10, 0 ) )
539
+ @year_spinbox.set_numeric( true )
540
+
541
+ month_label = Gtk::Label.new( "<b>#{I18n.get( "gui.common.month" )}</b>" );
542
+ month_label.set_xalign( 0.9 )
543
+ month_label.set_use_markup( true )
544
+ @month_spinbox = Gtk::SpinButton.new( Gtk::Adjustment.new( values["month"].to_i(), 0, 12, 1, 1, 0 ) )
545
+ @month_spinbox.set_numeric( true )
546
+
547
+ day_label = Gtk::Label.new( "<b>#{I18n.get( "gui.common.day" )}</b>" );
548
+ day_label.set_xalign( 0.9 )
549
+ day_label.set_use_markup( true )
550
+ @day_spinbox = Gtk::SpinButton.new( Gtk::Adjustment.new( values["day"].to_i(), 0, 31, 1, 1, 0 ) )
551
+ @day_spinbox.set_numeric( true )
552
+
553
+ image_label = Gtk::Label.new( "<b>#{I18n.get( "gui.common.image" )}</b>" );
554
+ image_label.set_xalign( 0.9 )
555
+ image_label.set_use_markup( true )
556
+ @image_path_lineedit = Gtk::Entry.new()
557
+ if values.include?( "image_path" )
558
+ @image_path_lineedit.set_text( values["image_path"] )
559
+ @image_button = Gtk::Button.new( "..." )
560
+ @image_button.signal_connect( "clicked" ) { browse_image() }
561
+ else
562
+ @image_path_lineedit.set_text( I18n.get( "gui.submitalbum.coverfound" ) )
563
+ @image_path_lineedit.set_sensitive( false )
564
+ end
565
+
566
+ tracks_scrolled_window = Gtk::ScrolledWindow.new()
567
+ tracks_scrolled_window.set_policy( Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC )
568
+ @tracks_text_buffer = GTK::TextBuffer.new()
569
+ @tracks_text_buffer.set_text( values["tracks"] )
570
+ @tracks_text = GTK::TextView.new()
571
+ @tracks_text.set_indent( 2 )
572
+ @tracks_text.set_buffer( @tracks_text_buffer )
573
+ tracks_scrolled_window.add( @tracks_text )
574
+
575
+ @reviewed_checkbox = Gtk::CheckButton.new( I18n.get( "gui.common.reviewed" ) )
576
+ @reviewed_checkbox.set_active( false )
577
+
578
+ buttons = create_action_buttons( "split", I18n.get( "gui.common.submit" ) )
579
+
580
+ image_hbox = Gtk::HBox.new();
581
+ image_hbox.pack_start( @image_path_lineedit, true, true )
582
+ image_hbox.pack_start( @image_button, false, true ) if values.include?( "image_path" )
583
+
584
+ tracks_frame = Gtk::Frame.new()
585
+ tracks_frame.add( tracks_scrolled_window )
586
+ tracks_frame.set_shadow_type( Gtk::SHADOW_IN )
587
+
588
+ grid = Gtk::Table.new( 9, 4, false )
589
+ grid.set_row_spacings( 3 )
590
+ grid.attach( artist_label, 0, 1, 0, 1, Gtk::SHRINK|Gtk::FILL, 0, 5 )
591
+ grid.attach( @artist_lineedit, 1, 6, 0, 1, Gtk::EXPAND|Gtk::FILL, 0 )
592
+ grid.attach( album_label, 0, 1, 1, 2, Gtk::SHRINK|Gtk::FILL, 0, 5 )
593
+ grid.attach( @album_lineedit, 1, 6, 1, 2, Gtk::EXPAND|Gtk::FILL, 0 )
594
+ grid.attach( year_label, 0, 1, 2, 3, Gtk::SHRINK|Gtk::FILL, 0, 5 )
595
+ grid.attach( @year_spinbox, 1, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, 0 )
596
+ grid.attach( month_label, 2, 3, 2, 3, Gtk::SHRINK|Gtk::FILL, 0, 5 )
597
+ grid.attach( @month_spinbox, 3, 4, 2, 3, Gtk::EXPAND|Gtk::FILL, 0 )
598
+ grid.attach( day_label, 4, 5, 2, 3, Gtk::SHRINK|Gtk::FILL, 0, 5 )
599
+ grid.attach( @day_spinbox, 5, 6, 2, 3, Gtk::EXPAND|Gtk::FILL, 0 )
600
+ grid.attach( image_label, 0, 1, 3, 4, Gtk::SHRINK|Gtk::FILL, 0, 5 )
601
+ grid.attach( image_hbox, 1, 6, 3, 4, Gtk::EXPAND|Gtk::FILL, 0 )
602
+ grid.attach( tracks_frame, 0, 6, 4, 5, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL )
603
+ grid.attach( @reviewed_checkbox, 0, 6, 5, 6, Gtk::EXPAND|Gtk::FILL, 0 )
604
+ grid.attach( buttons, 0, 6, 6, 7, Gtk::EXPAND|Gtk::FILL, 0 )
605
+
606
+ add( grid )
607
+
608
+ end
609
+
610
+ def browse_image()
611
+ dirname = @image_path_lineedit.text().strip()
612
+ dirname = GTK.empty_string?( dirname ) ? (ENV["HOME"] ? ENV["HOME"] : ".") : File.dirname( dirname )
613
+ dialog = Gtk::FileChooserDialog.new(
614
+ I18n.get( "gui.uploadcover.browseimage.title" ), self, Gtk::FileChooser::ACTION_OPEN, nil,
615
+ [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
616
+ [Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT]
617
+ )
618
+
619
+ filter = Gtk::FileFilter.new()
620
+ filter.set_name( I18n.get( "gui.uploadcover.browseimage.images" ) )
621
+ ["jpg", "png", "bmp"].each() { |ext| filter.add_pattern( "*.#{ext}" ).add_pattern( "*.#{ext.upcase}" ) }
622
+ dialog.add_filter( filter )
623
+ filter = Gtk::FileFilter.new()
624
+ filter.set_name( I18n.get( "gui.uploadcover.browseimage.allfiles" ) )
625
+ filter.add_pattern( "*" )
626
+ dialog.add_filter( filter )
627
+
628
+ dialog.set_local_only( true )
629
+ dialog.set_select_multiple( false )
630
+ dialog.set_current_folder( File.expand_path( dirname ) )
631
+ if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
632
+ image_path = GLib.filename_to_utf8( dialog.filename )
633
+ @image_path_lineedit.set_text( image_path.strip() ) if ! GTK.empty_string?( image_path )
634
+ end
635
+ dialog.destroy()
636
+ end
637
+
638
+ def accept()
639
+ aux = {
640
+ "artist" => @artist_lineedit.text(),
641
+ "album" => @album_lineedit.text(),
642
+ "year" => @year_spinbox.value().to_i(),
643
+ "month" => @month_spinbox.value().to_i(),
644
+ "day" => @day_spinbox.value().to_i(),
645
+ "tracks" => @tracks_text_buffer.text(),
646
+ "reviewed" => @reviewed_checkbox.active?()
647
+ }
648
+ aux["image_path"] = @image_path_lineedit.text() if @values.include?( "image_path" )
649
+ @values = aux
650
+ super()
651
+ end
652
+
653
+ end
654
+
655
+
656
+ class UploadCoverDialog < BaseDialog
657
+
658
+ def initialize( values )
659
+ super( values )
660
+
661
+ set_border_width( 5 )
662
+ set_resizable( true )
663
+ set_default_size( 350, 100 )
664
+ set_title( I18n.get( "gui.uploadcover.title", values["site_name"] ) )
665
+
666
+ album_group = Gtk::Frame.new( I18n.get( "gui.common.album" ) )
667
+
668
+ artist_label = Gtk::Label.new( I18n.get( "gui.common.artist" ) )
669
+ artist_label.set_xalign( 0 )
670
+ @artist_lineedit = Gtk::Entry.new()
671
+ @artist_lineedit.set_text( values["artist"] )
672
+
673
+ album_label = Gtk::Label.new( I18n.get( "gui.common.album" ) )
674
+ album_label.set_xalign( 0 )
675
+ @album_lineedit = Gtk::Entry.new()
676
+ @album_lineedit.set_text( values["album"] )
677
+
678
+ year_label = Gtk::Label.new( I18n.get( "gui.common.year" ) );
679
+ year_label.set_xalign( 0 )
680
+ @year_spinbox = Gtk::SpinButton.new( Gtk::Adjustment.new( values["year"], 1900, Date.today().year, 1, 10, 0 ) )
681
+ @year_spinbox.set_numeric( true )
682
+
683
+ image_group = Gtk::Frame.new( I18n.get( "gui.common.image" ) )
684
+
685
+ image_label = Gtk::Label.new( I18n.get( "gui.uploadcover.imagepath" ) )
686
+ image_label.set_use_markup( true )
687
+ @image_path_lineedit = Gtk::Entry.new()
688
+ @image_path_lineedit.set_text( values["image_path"] )
689
+ @image_button = Gtk::Button.new( "..." )
690
+ @image_button.signal_connect( "clicked" ) { browse_image() }
691
+
692
+ buttons = create_action_buttons()
693
+
694
+ album_grid = Gtk::Table.new( 4, 4, false )
695
+ album_grid.set_row_spacings( 3 )
696
+ album_grid.set_column_spacings( 3 )
697
+ album_grid.attach( artist_label, 0, 1, 0, 1, Gtk::SHRINK|Gtk::FILL, 0, 5 )
698
+ album_grid.attach( @artist_lineedit, 1, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, 0 )
699
+ album_grid.attach( album_label, 0, 1, 2, 3, Gtk::SHRINK|Gtk::FILL, 0, 5 )
700
+ album_grid.attach( @album_lineedit, 1, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, 0 )
701
+ album_grid.attach( year_label, 0, 1, 3, 4, Gtk::SHRINK|Gtk::FILL, 0, 5 )
702
+ album_grid.attach( @year_spinbox, 1, 2, 3, 4, Gtk::EXPAND|Gtk::FILL, 0 )
703
+ album_group.add( album_grid )
704
+
705
+ image_grid = Gtk::Table.new( 4, 4, false )
706
+ image_grid.set_row_spacings( 3 )
707
+ image_grid.set_column_spacings( 3 )
708
+ image_grid.attach( image_label, 0, 1, 0, 1, Gtk::SHRINK|Gtk::FILL, 0, 5 )
709
+ image_grid.attach( @image_path_lineedit, 1, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, 0 )
710
+ image_grid.attach( @image_button, 2, 3, 0, 1, 0, 0 )
711
+ image_group.add( image_grid )
712
+
713
+ vbox = Gtk::VBox.new( false, 3 );
714
+ vbox.add( album_group )
715
+ vbox.add( image_group )
716
+ vbox.add( buttons )
717
+
718
+ add( vbox )
719
+
720
+ end
721
+
722
+ def browse_image()
723
+ dirname = @image_path_lineedit.text().strip()
724
+ dirname = GTK.empty_string?( dirname ) ? (ENV["HOME"] ? ENV["HOME"] : ".") : File.dirname( dirname )
725
+ dialog = Gtk::FileChooserDialog.new(
726
+ I18n.get( "gui.uploadcover.browseimage.title" ), self, Gtk::FileChooser::ACTION_OPEN, nil,
727
+ [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
728
+ [Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT]
729
+ )
730
+
731
+ filter = Gtk::FileFilter.new()
732
+ filter.set_name( I18n.get( "gui.uploadcover.browseimage.images" ) )
733
+ ["jpg", "png", "bmp"].each() { |ext| filter.add_pattern( "*.#{ext}" ).add_pattern( "*.#{ext.upcase}" ) }
734
+ dialog.add_filter( filter )
735
+ filter = Gtk::FileFilter.new()
736
+ filter.set_name( I18n.get( "gui.uploadcover.browseimage.allfiles" ) )
737
+ filter.add_pattern( "*" )
738
+ dialog.add_filter( filter )
739
+
740
+ dialog.set_local_only( true )
741
+ dialog.set_select_multiple( false )
742
+ dialog.set_current_folder( File.expand_path( dirname ) )
743
+ if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
744
+ image_path = GLib.filename_to_utf8( dialog.filename )
745
+ @image_path_lineedit.set_text( image_path.strip() ) if ! GTK.empty_string?( image_path )
746
+ end
747
+ dialog.destroy()
748
+ end
749
+
750
+ def accept()
751
+ @values = {
752
+ "artist" => @artist_lineedit.text(),
753
+ "album" => @album_lineedit.text(),
754
+ "year" => @year_spinbox.value().to_i(),
755
+ "image_path" => @image_path_lineedit.text()
756
+ }
757
+ super()
758
+ end
759
+ end
760
+
761
+
762
+ class FixPagesDialog < BaseDialog
763
+
764
+ def initialize( values )
765
+ super( values )
766
+
767
+ @fetch_func = eval( @values["fetch_func"] )
768
+ @fix_func = eval( @values["fix_func"] )
769
+ @submit_func = eval( @values["submit_func"] )
770
+
771
+ set_border_width( 5 )
772
+ set_resizable( true )
773
+ set_default_size( 600, 400 )
774
+ resize( 600, 400 )
775
+
776
+ set_title( "#{values["site_name"]} - #{values["title"]}" )
777
+
778
+ label = Gtk::Label.new( "<b>#{I18n.get( "gui.common.url" )}</b>" );
779
+ label.set_use_markup( true )
780
+ label.set_xalign( 0.9 )
781
+ @url_lineedit = Gtk::Entry.new()
782
+ @url_lineedit.set_text( values["url"] )
783
+
784
+ page_scrolled_window = Gtk::ScrolledWindow.new()
785
+ page_scrolled_window.set_policy( Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC )
786
+ @page_text_buffer = GTK::TextBuffer.new()
787
+ @page_text_buffer.set_text( "" )
788
+ @page_text = GTK::TextView.new()
789
+ @page_text.set_indent( 2 )
790
+ @page_text.set_buffer( @page_text_buffer )
791
+ page_scrolled_window.add( @page_text )
792
+ page_frame = Gtk::Frame.new()
793
+ page_frame.add( page_scrolled_window )
794
+ page_frame.set_shadow_type( Gtk::SHADOW_IN )
795
+
796
+ @fetch_button = Gtk::Button.new( I18n.get( "gui.common.load" ) )
797
+ @fix_button = Gtk::Button.new( I18n.get( "gui.common.fix" ) )
798
+ @submit_button = Gtk::Button.new( I18n.get( "gui.common.submit" ) )
799
+
800
+ hbox = Gtk::HButtonBox.new();
801
+ hbox.layout_style = Gtk::ButtonBox::END
802
+ hbox.add( @fetch_button )
803
+ hbox.add( @fix_button )
804
+ hbox.add( @submit_button )
805
+
806
+ grid = Gtk::Table.new( 3, 2, false )
807
+ grid.set_row_spacings( 3 )
808
+ grid.attach( label, 0, 1, 0, 1, Gtk::SHRINK|Gtk::FILL, 0, 5 )
809
+ grid.attach( @url_lineedit, 1, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, 0 )
810
+ grid.attach( page_frame, 0, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL )
811
+ grid.attach( hbox, 0, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, 0 )
812
+
813
+ add( grid )
814
+
815
+ @fetch_button.signal_connect( "clicked" ) { fetch_page() }
816
+ @fix_button.signal_connect( "clicked" ) { fix_page() }
817
+ @submit_button.signal_connect( "clicked" ) { submit_page() }
818
+ end
819
+
820
+ def fetch_page()
821
+ if (page_content = @fetch_func.call( @url_lineedit.text() ))
822
+ @original_content = page_content
823
+ @original_url = @url_lineedit.text()
824
+ @page_text_buffer.set_text( page_content )
825
+ end
826
+ end
827
+
828
+ def fix_page()
829
+ @page_text_buffer.set_text( @fix_func.call( @page_text_buffer.text() ) ) if @original_content
830
+ end
831
+
832
+ def submit_page()
833
+ if @original_content && @original_content != (page_content = @page_text_buffer.text())
834
+ if @submit_func.call( @original_url, page_content )
835
+ @original_content = page_content
836
+ puts I18n.get( "gui.fixpages.success", @original_url )
837
+ else
838
+ puts I18n.get( "gui.fixpages.error", @original_url )
839
+ end
840
+ end
841
+ end
842
+
843
+ end
844
+
845
+
846
+ class SearchLyricsDialog < BaseDialog
847
+
848
+ def initialize( values )
849
+ super( values )
850
+
851
+ set_border_width( 5 )
852
+ set_resizable( true )
853
+ set_default_size( 300, 50 )
854
+ set_title( I18n.get( "gui.searchlyrics.title" ) )
855
+
856
+ group = Gtk::Frame.new( I18n.get( "gui.searchlyrics.search" ) )
857
+
858
+ artist_label = Gtk::Label.new( I18n.get( "gui.common.artist" ) )
859
+ artist_label.set_xalign( 0 )
860
+ @artist_lineedit = Gtk::Entry.new()
861
+ @artist_lineedit.set_text( values["artist"] )
862
+
863
+ title_label = Gtk::Label.new( I18n.get( "gui.common.song" ) )
864
+ title_label.set_xalign( 0 )
865
+ @title_lineedit = Gtk::Entry.new()
866
+ @title_lineedit.set_text( values["title"] )
867
+
868
+ album_label = Gtk::Label.new( I18n.get( "gui.common.album" ) );
869
+ album_label.set_xalign( 0 )
870
+ @album_lineedit = Gtk::Entry.new()
871
+ @album_lineedit.set_text( values["album"] )
872
+
873
+ year_label = Gtk::Label.new( I18n.get( "gui.common.year" ) );
874
+ year_label.set_xalign( 0 )
875
+ @year_spinbox = Gtk::SpinButton.new( Gtk::Adjustment.new( values["year"], 1900, Date.today().year, 1, 10, 0 ) )
876
+ @year_spinbox.set_numeric( true )
877
+
878
+ buttons = create_action_buttons()
879
+
880
+ grid = Gtk::Table.new( 4, 4, false )
881
+ grid.set_row_spacings( 3 )
882
+ grid.set_column_spacings( 3 )
883
+ grid.attach( artist_label, 0, 1, 0, 1, Gtk::SHRINK|Gtk::FILL, 0, 5 )
884
+ grid.attach( @artist_lineedit, 1, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, 0 )
885
+ grid.attach( title_label, 0, 1, 1, 2, Gtk::SHRINK|Gtk::FILL, 0, 5 )
886
+ grid.attach( @title_lineedit, 1, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, 0 )
887
+ grid.attach( album_label, 0, 1, 2, 3, Gtk::SHRINK|Gtk::FILL, 0, 5 )
888
+ grid.attach( @album_lineedit, 1, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, 0 )
889
+ grid.attach( year_label, 0, 1, 3, 4, Gtk::SHRINK|Gtk::FILL, 0, 5 )
890
+ grid.attach( @year_spinbox, 1, 2, 3, 4, Gtk::EXPAND|Gtk::FILL, 0 )
891
+ group.add( grid )
892
+
893
+ vbox = Gtk::VBox.new( false, 3 );
894
+ vbox.add( group )
895
+ vbox.add( buttons )
896
+
897
+ add( vbox )
898
+
899
+ end
900
+
901
+ def accept()
902
+ @values = {
903
+ "artist" => @artist_lineedit.text(),
904
+ "title" => @title_lineedit.text(),
905
+ "album" => @album_lineedit.text(),
906
+ "year" => @year_spinbox.value().to_i(),
907
+ }
908
+ super()
909
+ end
910
+ end
911
+
912
+
913
+ class ShowLyricsDialog < BaseDialog
914
+
915
+ def initialize( values )
916
+ super( values )
917
+
918
+ set_border_width( 5 )
919
+ set_resizable( true )
920
+ set_default_size( 400, 400 )
921
+ resize( 350, 400 )
922
+
923
+ title = I18n.get( "gui.lyrics.title", @values["title"], @values["artist"] )
924
+ title += " [#{@values["site_name"]}]" if @values["site_name"]
925
+ set_title( title )
926
+
927
+ lyrics_scrolled_window = Gtk::ScrolledWindow.new()
928
+ lyrics_scrolled_window.set_policy( Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC )
929
+ @lyrics_text_buffer = GTK::TextBuffer.new()
930
+ @lyrics_text_buffer.set_text( values["lyrics"] )
931
+ @lyrics_text = GTK::TextView.new()
932
+ @lyrics_text.set_indent( 2 )
933
+ @lyrics_text.set_buffer( @lyrics_text_buffer )
934
+ lyrics_scrolled_window.add( @lyrics_text )
935
+
936
+ lyrics_frame = Gtk::Frame.new()
937
+ lyrics_frame.add( lyrics_scrolled_window )
938
+ lyrics_frame.set_shadow_type( Gtk::SHADOW_IN )
939
+
940
+ add( lyrics_frame )
941
+ end
942
+
943
+ end
944
+
945
+ end