# music_gui.cpp

Go to the documentation of this file.

    00001 /* $Id: music_gui.cpp 26544 2014-04-29 18:41:19Z frosch $ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
    00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00012 #include "stdafx.h"
    00013 #include "openttd.h"
    00014 #include "base_media_base.h"
    00015 #include "music/music_driver.hpp"
    00016 #include "window_gui.h"
    00017 #include "strings_func.h"
    00018 #include "window_func.h"
    00019 #include "sound_func.h"
    00020 #include "gfx_func.h"
    00021 #include "core/random_func.hpp"
    00022 #include "error.h"
    00023 #include "core/geometry_func.hpp"
    00024 #include "string_func.h"
    00025 #include "settings_type.h"
    00026
    00027 #include "widgets/music_widget.h"
    00028
    00029 #include "table/strings.h"
    00030 #include "table/sprites.h"
    00031
    00037 static const char *GetSongName(int index)
    00038 {
    00039   return BaseMusic::GetUsedSet()->song_name[index];
    00040 }
    00041
    00047 static int GetTrackNumber(int index)
    00048 {
    00049   return BaseMusic::GetUsedSet()->track_nr[index];
    00050 }
    00051
    00053 static byte _music_wnd_cursong = 1;
    00055 static bool _song_is_active = false;
    00056
    00058 static byte _cur_playlist[NUM_SONGS_PLAYLIST + 1];
    00059
    00061 static byte _playlist_all[NUM_SONGS_AVAILABLE + 1];
    00063 static byte _playlist_old_style[NUM_SONGS_CLASS + 1];
    00065 static byte _playlist_new_style[NUM_SONGS_CLASS + 1];
    00067 static byte _playlist_ezy_street[NUM_SONGS_CLASS + 1];
    00068
    00069 assert_compile(lengthof(_settings_client.music.custom_1) == NUM_SONGS_PLAYLIST + 1);
    00070 assert_compile(lengthof(_settings_client.music.custom_2) == NUM_SONGS_PLAYLIST + 1);
    00071
    00073 static byte * const _playlists[] = {
    00074   _playlist_all,
    00075   _playlist_old_style,
    00076   _playlist_new_style,
    00077   _playlist_ezy_street,
    00078   _settings_client.music.custom_1,
    00079   _settings_client.music.custom_2,
    00080 };
    00081
    00087 void ValidatePlaylist(byte *playlist, byte *last)
    00088 {
    00089   while (*playlist != 0 && playlist <= last) {
    00090     /* Song indices are saved off-by-one so 0 is "nothing". */
    00091     if (*playlist <= NUM_SONGS_AVAILABLE && !StrEmpty(GetSongName(*playlist - 1))) {
    00092       playlist++;
    00093       continue;
    00094     }
    00095     for (byte *p = playlist; *p != 0 && p <= last; p++) {
    00096       p[0] = p[1];
    00097     }
    00098   }
    00099
    00100   /* Make sure the list is null terminated. */
    00101   *last = 0;
    00102 }
    00103
    00105 void InitializeMusic()
    00106 {
    00107   uint j = 0;
    00108   for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
    00109     if (StrEmpty(GetSongName(i))) continue;
    00110     _playlist_all[j++] = i + 1;
    00111   }
    00112   /* Terminate the list */
    00113   _playlist_all[j] = 0;
    00114
    00115   /* Now make the 'styled' playlists */
    00116   for (uint k = 0; k < NUM_SONG_CLASSES; k++) {
    00117     j = 0;
    00118     for (uint i = 0; i < NUM_SONGS_CLASS; i++) {
    00119       int id = k * NUM_SONGS_CLASS + i + 1;
    00120       if (StrEmpty(GetSongName(id))) continue;
    00121       _playlists[k + 1][j++] = id + 1;
    00122     }
    00123     /* Terminate the list */
    00124     _playlists[k + 1][j] = 0;
    00125   }
    00126
    00127   ValidatePlaylist(_settings_client.music.custom_1, lastof(_settings_client.music.custom_1));
    00128   ValidatePlaylist(_settings_client.music.custom_2, lastof(_settings_client.music.custom_2));
    00129
    00130   if (BaseMusic::GetUsedSet()->num_available < _music_wnd_cursong) {
    00131     /* If there are less songs than the currently played song,
    00132      * just pause and reset to no song. */
    00133     _music_wnd_cursong = 0;
    00134     _song_is_active = false;
    00135   }
    00136 }
    00137
    00138 static void SkipToPrevSong()
    00139 {
    00140   byte *b = _cur_playlist;
    00141   byte *p = b;
    00142   byte t;
    00143
    00144   if (b[0] == 0) return; // empty playlist
    00145
    00146   do p++; while (p[0] != 0); // find the end
    00147
    00148   t = *--p; // and copy the bytes
    00149   while (p != b) {
    00150     p--;
    00151     p[1] = p[0];
    00152   }
    00153   *b = t;
    00154
    00155   _song_is_active = false;
    00156 }
    00157
    00158 static void SkipToNextSong()
    00159 {
    00160   byte *b = _cur_playlist;
    00161   byte t;
    00162
    00163   t = b[0];
    00164   if (t != 0) {
    00165     while (b[1] != 0) {
    00166       b[0] = b[1];
    00167       b++;
    00168     }
    00169     b[0] = t;
    00170   }
    00171
    00172   _song_is_active = false;
    00173 }
    00174
    00175 static void MusicVolumeChanged(byte new_vol)
    00176 {
    00177   MusicDriver::GetInstance()->SetVolume(new_vol);
    00178 }
    00179
    00180 static void DoPlaySong()
    00181 {
    00182   char filename[MAX_PATH];
    00183   if (FioFindFullPath(filename, lengthof(filename), BASESET_DIR, BaseMusic::GetUsedSet()->files[_music_wnd_cursong - 1].filename) == NULL) {
    00184     FioFindFullPath(filename, lengthof(filename), OLD_GM_DIR, BaseMusic::GetUsedSet()->files[_music_wnd_cursong - 1].filename);
    00185   }
    00186   MusicDriver::GetInstance()->PlaySong(filename);
    00187   SetWindowDirty(WC_MUSIC_WINDOW, 0);
    00188 }
    00189
    00190 static void DoStopMusic()
    00191 {
    00192   MusicDriver::GetInstance()->StopSong();
    00193   SetWindowDirty(WC_MUSIC_WINDOW, 0);
    00194 }
    00195
    00196 static void SelectSongToPlay()
    00197 {
    00198   uint i = 0;
    00199   uint j = 0;
    00200
    00201   memset(_cur_playlist, 0, sizeof(_cur_playlist));
    00202   do {
    00203     const char *filename = BaseMusic::GetUsedSet()->files[_playlists[_settings_client.music.playlist][i] - 1].filename;
    00204     /* We are now checking for the existence of that file prior
    00205      * to add it to the list of available songs */
    00206     if (!StrEmpty(filename) && FioCheckFileExists(filename, BASESET_DIR)) {
    00207       _cur_playlist[j] = _playlists[_settings_client.music.playlist][i];
    00208       j++;
    00209     }
    00210   } while (_playlists[_settings_client.music.playlist][++i] != 0 && j < lengthof(_cur_playlist) - 1);
    00211
    00212   /* Do not shuffle when on the intro-start window, as the song to play has to be the original TTD Theme*/
    00213   if (_settings_client.music.shuffle && _game_mode != GM_MENU) {
    00214     i = 500;
    00215     do {
    00216       uint32 r = InteractiveRandom();
    00217       byte *a = &_cur_playlist[GB(r, 0, 5)];
    00218       byte *b = &_cur_playlist[GB(r, 8, 5)];
    00219
    00220       if (*a != 0 && *b != 0) {
    00221         byte t = *a;
    00222         *a = *b;
    00223         *b = t;
    00224       }
    00225     } while (--i);
    00226   }
    00227 }
    00228
    00229 static void StopMusic()
    00230 {
    00231   _music_wnd_cursong = 0;
    00232   DoStopMusic();
    00233   _song_is_active = false;
    00234   SetWindowWidgetDirty(WC_MUSIC_WINDOW, 0, 9);
    00235 }
    00236
    00237 static void PlayPlaylistSong()
    00238 {
    00239   if (_cur_playlist[0] == 0) {
    00240     SelectSongToPlay();
    00241     /* if there is not songs in the playlist, it may indicate
    00242      * no file on the gm folder, or even no gm folder.
    00243      * Stop the playback, then */
    00244     if (_cur_playlist[0] == 0) {
    00245       _song_is_active = false;
    00246       _music_wnd_cursong = 0;
    00247       _settings_client.music.playing = false;
    00248       return;
    00249     }
    00250   }
    00251   _music_wnd_cursong = _cur_playlist[0];
    00252   DoPlaySong();
    00253   _song_is_active = true;
    00254
    00255   SetWindowWidgetDirty(WC_MUSIC_WINDOW, 0, 9);
    00256 }
    00257
    00258 void ResetMusic()
    00259 {
    00260   _music_wnd_cursong = 1;
    00261   DoPlaySong();
    00262 }
    00263
    00264 void MusicLoop()
    00265 {
    00266   if (!_settings_client.music.playing && _song_is_active) {
    00267     StopMusic();
    00268   } else if (_settings_client.music.playing && !_song_is_active) {
    00269     PlayPlaylistSong();
    00270   }
    00271
    00272   if (!_song_is_active) return;
    00273
    00274   if (!MusicDriver::GetInstance()->IsSongPlaying()) {
    00275     if (_game_mode != GM_MENU) {
    00276       StopMusic();
    00277       SkipToNextSong();
    00278       PlayPlaylistSong();
    00279     } else {
    00280       ResetMusic();
    00281     }
    00282   }
    00283 }
    00284
    00285 static void SelectPlaylist(byte list)
    00286 {
    00287   _settings_client.music.playlist = list;
    00288   InvalidateWindowData(WC_MUSIC_TRACK_SELECTION, 0);
    00289   InvalidateWindowData(WC_MUSIC_WINDOW, 0);
    00290 }
    00291
    00292 struct MusicTrackSelectionWindow : public Window {
    00293   MusicTrackSelectionWindow(WindowDesc *desc, WindowNumber number) : Window(desc)
    00294   {
    00295     this->InitNested(number);
    00296     this->LowerWidget(WID_MTS_LIST_LEFT);
    00297     this->LowerWidget(WID_MTS_LIST_RIGHT);
    00298     this->SetWidgetDisabledState(WID_MTS_CLEAR, _settings_client.music.playlist <= 3);
    00299     this->LowerWidget(WID_MTS_ALL + _settings_client.music.playlist);
    00300   }
    00301
    00302   virtual void SetStringParameters(int widget) const
    00303   {
    00304     switch (widget) {
    00305       case WID_MTS_PLAYLIST:
    00306         SetDParam(0, STR_MUSIC_PLAYLIST_ALL + _settings_client.music.playlist);
    00307         break;
    00308     }
    00309   }
    00310
    00316   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
    00317   {
    00318     if (!gui_scope) return;
    00319     for (int i = 0; i < 6; i++) {
    00320       this->SetWidgetLoweredState(WID_MTS_ALL + i, i == _settings_client.music.playlist);
    00321     }
    00322     this->SetWidgetDisabledState(WID_MTS_CLEAR, _settings_client.music.playlist <= 3);
    00323     this->SetDirty();
    00324   }
    00325
    00326   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    00327   {
    00328     switch (widget) {
    00329       case WID_MTS_PLAYLIST: {
    00330         Dimension d = {0, 0};
    00331
    00332         for (int i = 0; i < 6; i++) {
    00333           SetDParam(0, STR_MUSIC_PLAYLIST_ALL + i);
    00334           d = maxdim(d, GetStringBoundingBox(STR_PLAYLIST_PROGRAM));
    00335         }
    00336         d.width += padding.width;
    00337         d.height += padding.height;
    00338         *size = maxdim(*size, d);
    00339         break;
    00340       }
    00341
    00342       case WID_MTS_LIST_LEFT: case WID_MTS_LIST_RIGHT: {
    00343         Dimension d = {0, 0};
    00344
    00345         for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
    00346           const char *song_name = GetSongName(i);
    00347           if (StrEmpty(song_name)) continue;
    00348
    00349           SetDParam(0, GetTrackNumber(i));
    00350           SetDParam(1, 2);
    00351           SetDParamStr(2, GetSongName(i));
    00352           Dimension d2 = GetStringBoundingBox(STR_PLAYLIST_TRACK_NAME);
    00353           d.width = max(d.width, d2.width);
    00354           d.height += d2.height;
    00355         }
    00356         d.width += padding.width;
    00357         d.height += padding.height;
    00358         *size = maxdim(*size, d);
    00359         break;
    00360       }
    00361     }
    00362   }
    00363
    00364   virtual void DrawWidget(const Rect &r, int widget) const
    00365   {
    00366     switch (widget) {
    00367       case WID_MTS_LIST_LEFT: {
    00368         GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, PC_BLACK);
    00369
    00370         int y = r.top + WD_FRAMERECT_TOP;
    00371         for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
    00372           const char *song_name = GetSongName(i);
    00373           if (StrEmpty(song_name)) continue;
    00374
    00375           SetDParam(0, GetTrackNumber(i));
    00376           SetDParam(1, 2);
    00377           SetDParamStr(2, song_name);
    00378           DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_PLAYLIST_TRACK_NAME);
    00379           y += FONT_HEIGHT_SMALL;
    00380         }
    00381         break;
    00382       }
    00383
    00384       case WID_MTS_LIST_RIGHT: {
    00385         GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, PC_BLACK);
    00386
    00387         int y = r.top + WD_FRAMERECT_TOP;
    00388         for (const byte *p = _playlists[_settings_client.music.playlist]; *p != 0; p++) {
    00389           uint i = *p - 1;
    00390           SetDParam(0, GetTrackNumber(i));
    00391           SetDParam(1, 2);
    00392           SetDParamStr(2, GetSongName(i));
    00393           DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_PLAYLIST_TRACK_NAME);
    00394           y += FONT_HEIGHT_SMALL;
    00395         }
    00396         break;
    00397       }
    00398     }
    00399   }
    00400
    00401   virtual void OnClick(Point pt, int widget, int click_count)
    00402   {
    00403     switch (widget) {
    00404       case WID_MTS_LIST_LEFT: { // add to playlist
    00405         int y = this->GetRowFromWidget(pt.y, widget, 0, FONT_HEIGHT_SMALL);
    00406
    00407         if (_settings_client.music.playlist < 4) return;
    00408         if (!IsInsideMM(y, 0, BaseMusic::GetUsedSet()->num_available)) return;
    00409
    00410         byte *p = _playlists[_settings_client.music.playlist];
    00411         for (uint i = 0; i != NUM_SONGS_PLAYLIST - 1; i++) {
    00412           if (p[i] == 0) {
    00413             /* Find the actual song number */
    00414             for (uint j = 0; j < NUM_SONGS_AVAILABLE; j++) {
    00415               if (GetTrackNumber(j) == y + 1) {
    00416                 p[i] = j + 1;
    00417                 break;
    00418               }
    00419             }
    00420             p[i + 1] = 0;
    00421             this->SetDirty();
    00422             SelectSongToPlay();
    00423             break;
    00424           }
    00425         }
    00426         break;
    00427       }
    00428
    00429       case WID_MTS_LIST_RIGHT: { // remove from playlist
    00430         int y = this->GetRowFromWidget(pt.y, widget, 0, FONT_HEIGHT_SMALL);
    00431
    00432         if (_settings_client.music.playlist < 4) return;
    00433         if (!IsInsideMM(y, 0, NUM_SONGS_PLAYLIST)) return;
    00434
    00435         byte *p = _playlists[_settings_client.music.playlist];
    00436         for (uint i = y; i != NUM_SONGS_PLAYLIST - 1; i++) {
    00437           p[i] = p[i + 1];
    00438         }
    00439
    00440         this->SetDirty();
    00441         SelectSongToPlay();
    00442         break;
    00443       }
    00444
    00445       case WID_MTS_CLEAR: // clear
    00446         for (uint i = 0; _playlists[_settings_client.music.playlist][i] != 0; i++) _playlists[_settings_client.music.playlist][i] = 0;
    00447         this->SetDirty();
    00448         StopMusic();
    00449         SelectSongToPlay();
    00450         break;
    00451
    00452       case WID_MTS_ALL: case WID_MTS_OLD: case WID_MTS_NEW:
    00453       case WID_MTS_EZY: case WID_MTS_CUSTOM1: case WID_MTS_CUSTOM2: // set playlist
    00454         SelectPlaylist(widget - WID_MTS_ALL);
    00455         StopMusic();
    00456         SelectSongToPlay();
    00457         break;
    00458     }
    00459   }
    00460 };
    00461
    00462 static const NWidgetPart _nested_music_track_selection_widgets[] = {
    00463   NWidget(NWID_HORIZONTAL),
    00464     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
    00465     NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_PLAYLIST_MUSIC_PROGRAM_SELECTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    00466   EndContainer(),
    00467   NWidget(WWT_PANEL, COLOUR_GREY),
    00468     NWidget(NWID_HORIZONTAL), SetPIP(2, 4, 2),
    00469       /* Left panel. */
    00470       NWidget(NWID_VERTICAL),
    00471         NWidget(WWT_LABEL, COLOUR_GREY), SetDataTip(STR_PLAYLIST_TRACK_INDEX, STR_NULL),
    00472         NWidget(WWT_PANEL, COLOUR_GREY, WID_MTS_LIST_LEFT), SetMinimalSize(180, 194), SetDataTip(0x0, STR_PLAYLIST_TOOLTIP_CLICK_TO_ADD_TRACK), EndContainer(),
    00473         NWidget(NWID_SPACER), SetMinimalSize(0, 2),
    00474       EndContainer(),
    00475       /* Middle buttons. */
    00476       NWidget(NWID_VERTICAL),
    00477         NWidget(NWID_SPACER), SetMinimalSize(60, 30), // Space above the first button from the title bar.
    00478         NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_MTS_ALL), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_ALL, STR_MUSIC_TOOLTIP_SELECT_ALL_TRACKS_PROGRAM),
    00479         NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_MTS_OLD), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_OLD_STYLE, STR_MUSIC_TOOLTIP_SELECT_OLD_STYLE_MUSIC),
    00480         NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_MTS_NEW), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_NEW_STYLE, STR_MUSIC_TOOLTIP_SELECT_NEW_STYLE_MUSIC),
    00481         NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_MTS_EZY), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_EZY_STREET, STR_MUSIC_TOOLTIP_SELECT_EZY_STREET_STYLE),
    00482         NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_MTS_CUSTOM1), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_CUSTOM_1, STR_MUSIC_TOOLTIP_SELECT_CUSTOM_1_USER_DEFINED),
    00483         NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_MTS_CUSTOM2), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_CUSTOM_2, STR_MUSIC_TOOLTIP_SELECT_CUSTOM_2_USER_DEFINED),
    00484         NWidget(NWID_SPACER), SetMinimalSize(0, 16), // Space above 'clear' button
    00485         NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_MTS_CLEAR), SetFill(1, 0), SetDataTip(STR_PLAYLIST_CLEAR, STR_PLAYLIST_TOOLTIP_CLEAR_CURRENT_PROGRAM_CUSTOM1),
    00486         NWidget(NWID_SPACER), SetFill(0, 1),
    00487       EndContainer(),
    00488       /* Right panel. */
    00489       NWidget(NWID_VERTICAL),
    00490         NWidget(WWT_LABEL, COLOUR_GREY, WID_MTS_PLAYLIST), SetDataTip(STR_PLAYLIST_PROGRAM, STR_NULL),
    00491         NWidget(WWT_PANEL, COLOUR_GREY, WID_MTS_LIST_RIGHT), SetMinimalSize(180, 194), SetDataTip(0x0, STR_PLAYLIST_TOOLTIP_CLICK_TO_REMOVE_TRACK), EndContainer(),
    00492         NWidget(NWID_SPACER), SetMinimalSize(0, 2),
    00493       EndContainer(),
    00494     EndContainer(),
    00495   EndContainer(),
    00496 };
    00497
    00498 static WindowDesc _music_track_selection_desc(
    00499   WDP_AUTO, "music_track", 0, 0,
    00500   WC_MUSIC_TRACK_SELECTION, WC_NONE,
    00501   0,
    00502   _nested_music_track_selection_widgets, lengthof(_nested_music_track_selection_widgets)
    00503 );
    00504
    00505 static void ShowMusicTrackSelection()
    00506 {
    00507   AllocateWindowDescFront<MusicTrackSelectionWindow>(&_music_track_selection_desc, 0);
    00508 }
    00509
    00510 struct MusicWindow : public Window {
    00511   static const int slider_width = 3;
    00512
    00513   MusicWindow(WindowDesc *desc, WindowNumber number) : Window(desc)
    00514   {
    00515     this->InitNested(number);
    00516     this->LowerWidget(_settings_client.music.playlist + WID_M_ALL);
    00517     this->SetWidgetLoweredState(WID_M_SHUFFLE, _settings_client.music.shuffle);
    00518   }
    00519
    00520   virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
    00521   {
    00522     switch (widget) {
    00523       /* Make sure that WID_M_SHUFFLE and WID_M_PROGRAMME have the same size.
    00524        * This can't be done by using NC_EQUALSIZE as the WID_M_INFO is
    00525        * between those widgets and of different size. */
    00526       case WID_M_SHUFFLE: case WID_M_PROGRAMME: {
    00527         Dimension d = maxdim(GetStringBoundingBox(STR_MUSIC_PROGRAM), GetStringBoundingBox(STR_MUSIC_SHUFFLE));
    00528         d.width += padding.width;
    00529         d.height += padding.height;
    00530         *size = maxdim(*size, d);
    00531         break;
    00532       }
    00533
    00534       case WID_M_TRACK_NR: {
    00535         Dimension d = GetStringBoundingBox(STR_MUSIC_TRACK_NONE);
    00536         d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
    00537         d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
    00538         *size = maxdim(*size, d);
    00539         break;
    00540       }
    00541
    00542       case WID_M_TRACK_NAME: {
    00543         Dimension d = GetStringBoundingBox(STR_MUSIC_TITLE_NONE);
    00544         for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
    00545           SetDParamStr(0, GetSongName(i));
    00546           d = maxdim(d, GetStringBoundingBox(STR_MUSIC_TITLE_NAME));
    00547         }
    00548         d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
    00549         d.height += WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
    00550         *size = maxdim(*size, d);
    00551         break;
    00552       }
    00553
    00554       /* Hack-ish: set the proper widget data; only needs to be done once
    00555        * per (Re)Init as that's the only time the language changes. */
    00556       case WID_M_PREV: this->GetWidget<NWidgetCore>(WID_M_PREV)->widget_data = _current_text_dir == TD_RTL ? SPR_IMG_SKIP_TO_NEXT : SPR_IMG_SKIP_TO_PREV; break;
    00557       case WID_M_NEXT: this->GetWidget<NWidgetCore>(WID_M_NEXT)->widget_data = _current_text_dir == TD_RTL ? SPR_IMG_SKIP_TO_PREV : SPR_IMG_SKIP_TO_NEXT; break;
    00558       case WID_M_PLAY: this->GetWidget<NWidgetCore>(WID_M_PLAY)->widget_data = _current_text_dir == TD_RTL ? SPR_IMG_PLAY_MUSIC_RTL : SPR_IMG_PLAY_MUSIC; break;
    00559     }
    00560   }
    00561
    00562   virtual void DrawWidget(const Rect &r, int widget) const
    00563   {
    00564     switch (widget) {
    00565       case WID_M_TRACK_NR: {
    00566         GfxFillRect(r.left + 1, r.top + 1, r.right, r.bottom, PC_BLACK);
    00567         StringID str = STR_MUSIC_TRACK_NONE;
    00568         if (_song_is_active != 0 && _music_wnd_cursong != 0) {
    00569           SetDParam(0, GetTrackNumber(_music_wnd_cursong - 1));
    00570           SetDParam(1, 2);
    00571           str = STR_MUSIC_TRACK_DIGIT;
    00572         }
    00573         DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, str);
    00574         break;
    00575       }
    00576
    00577       case WID_M_TRACK_NAME: {
    00578         GfxFillRect(r.left, r.top + 1, r.right - 1, r.bottom, PC_BLACK);
    00579         StringID str = STR_MUSIC_TITLE_NONE;
    00580         if (_song_is_active != 0 && _music_wnd_cursong != 0) {
    00581           str = STR_MUSIC_TITLE_NAME;
    00582           SetDParamStr(0, GetSongName(_music_wnd_cursong - 1));
    00583         }
    00584         DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, str, TC_FROMSTRING, SA_HOR_CENTER);
    00585         break;
    00586       }
    00587
    00588       case WID_M_MUSIC_VOL: case WID_M_EFFECT_VOL: {
    00589         DrawFrameRect(r.left, r.top + 2, r.right, r.bottom - 2, COLOUR_GREY, FR_LOWERED);
    00590         byte volume = (widget == WID_M_MUSIC_VOL) ? _settings_client.music.music_vol : _settings_client.music.effect_vol;
    00591         int x = (volume * (r.right - r.left) / 127);
    00592         if (_current_text_dir == TD_RTL) {
    00593           x = r.right - x;
    00594         } else {
    00595           x += r.left;
    00596         }
    00597         DrawFrameRect(x, r.top, x + slider_width, r.bottom, COLOUR_GREY, FR_NONE);
    00598         break;
    00599       }
    00600     }
    00601   }
    00602
    00608   virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
    00609   {
    00610     if (!gui_scope) return;
    00611     for (int i = 0; i < 6; i++) {
    00612       this->SetWidgetLoweredState(WID_M_ALL + i, i == _settings_client.music.playlist);
    00613     }
    00614     this->SetDirty();
    00615   }
    00616
    00617   virtual void OnClick(Point pt, int widget, int click_count)
    00618   {
    00619     switch (widget) {
    00620       case WID_M_PREV: // skip to prev
    00621         if (!_song_is_active) return;
    00622         SkipToPrevSong();
    00623         this->SetDirty();
    00624         break;
    00625
    00626       case WID_M_NEXT: // skip to next
    00627         if (!_song_is_active) return;
    00628         SkipToNextSong();
    00629         this->SetDirty();
    00630         break;
    00631
    00632       case WID_M_STOP: // stop playing
    00633         _settings_client.music.playing = false;
    00634         break;
    00635
    00636       case WID_M_PLAY: // start playing
    00637         _settings_client.music.playing = true;
    00638         break;
    00639
    00640       case WID_M_MUSIC_VOL: case WID_M_EFFECT_VOL: { // volume sliders
    00641         int x = pt.x - this->GetWidget<NWidgetBase>(widget)->pos_x;
    00642
    00643         byte *vol = (widget == WID_M_MUSIC_VOL) ? &_settings_client.music.music_vol : &_settings_client.music.effect_vol;
    00644
    00645         byte new_vol = x * 127 / this->GetWidget<NWidgetBase>(widget)->current_x;
    00646         if (_current_text_dir == TD_RTL) new_vol = 127 - new_vol;
    00647         if (new_vol != *vol) {
    00648           *vol = new_vol;
    00649           if (widget == WID_M_MUSIC_VOL) MusicVolumeChanged(new_vol);
    00650           this->SetDirty();
    00651         }
    00652
    00653         _left_button_clicked = false;
    00654         break;
    00655       }
    00656
    00657       case WID_M_SHUFFLE: // toggle shuffle
    00658         _settings_client.music.shuffle ^= 1;
    00659         this->SetWidgetLoweredState(WID_M_SHUFFLE, _settings_client.music.shuffle);
    00660         this->SetWidgetDirty(WID_M_SHUFFLE);
    00661         StopMusic();
    00662         SelectSongToPlay();
    00663         this->SetDirty();
    00664         break;
    00665
    00666       case WID_M_PROGRAMME: // show track selection
    00667         ShowMusicTrackSelection();
    00668         break;
    00669
    00670       case WID_M_ALL: case WID_M_OLD: case WID_M_NEW:
    00671       case WID_M_EZY: case WID_M_CUSTOM1: case WID_M_CUSTOM2: // playlist
    00672         SelectPlaylist(widget - WID_M_ALL);
    00673         StopMusic();
    00674         SelectSongToPlay();
    00675         this->SetDirty();
    00676         break;
    00677     }
    00678   }
    00679 };
    00680
    00681 static const NWidgetPart _nested_music_window_widgets[] = {
    00682   NWidget(NWID_HORIZONTAL),
    00683     NWidget(WWT_CLOSEBOX, COLOUR_GREY),
    00684     NWidget(WWT_CAPTION, COLOUR_GREY), SetDataTip(STR_MUSIC_JAZZ_JUKEBOX_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
    00685     NWidget(WWT_SHADEBOX, COLOUR_GREY),
    00686     NWidget(WWT_STICKYBOX, COLOUR_GREY),
    00687   EndContainer(),
    00688
    00689   NWidget(NWID_HORIZONTAL),
    00690     NWidget(NWID_VERTICAL),
    00691       NWidget(WWT_PANEL, COLOUR_GREY, -1), SetFill(1, 1), EndContainer(),
    00692       NWidget(NWID_HORIZONTAL),
    00693         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_M_PREV), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_SKIP_TO_PREV, STR_MUSIC_TOOLTIP_SKIP_TO_PREVIOUS_TRACK),
    00694         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_M_NEXT), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_SKIP_TO_NEXT, STR_MUSIC_TOOLTIP_SKIP_TO_NEXT_TRACK_IN_SELECTION),
    00695         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_M_STOP), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_STOP_MUSIC, STR_MUSIC_TOOLTIP_STOP_PLAYING_MUSIC),
    00696         NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_M_PLAY), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_PLAY_MUSIC, STR_MUSIC_TOOLTIP_START_PLAYING_MUSIC),
    00697       EndContainer(),
    00698       NWidget(WWT_PANEL, COLOUR_GREY, -1), SetFill(1, 1), EndContainer(),
    00699     EndContainer(),
    00700     NWidget(WWT_PANEL, COLOUR_GREY, WID_M_SLIDERS),
    00701       NWidget(NWID_HORIZONTAL), SetPIP(20, 20, 20),
    00702         NWidget(NWID_VERTICAL),
    00703           NWidget(WWT_LABEL, COLOUR_GREY, -1), SetFill(1, 0), SetDataTip(STR_MUSIC_MUSIC_VOLUME, STR_NULL),
    00704           NWidget(WWT_EMPTY, COLOUR_GREY, WID_M_MUSIC_VOL), SetMinimalSize(67, 0), SetMinimalTextLines(1, 0), SetFill(1, 0), SetDataTip(0x0, STR_MUSIC_TOOLTIP_DRAG_SLIDERS_TO_SET_MUSIC),
    00705           NWidget(NWID_HORIZONTAL),
    00706             NWidget(WWT_LABEL, COLOUR_GREY, -1), SetDataTip(STR_MUSIC_RULER_MIN, STR_NULL),
    00707             NWidget(WWT_LABEL, COLOUR_GREY, -1), SetDataTip(STR_MUSIC_RULER_MARKER, STR_NULL), SetFill(1, 0),
    00708             NWidget(WWT_LABEL, COLOUR_GREY, -1), SetDataTip(STR_MUSIC_RULER_MARKER, STR_NULL), SetFill(1, 0),
    00709             NWidget(WWT_LABEL, COLOUR_GREY, -1), SetDataTip(STR_MUSIC_RULER_MARKER, STR_NULL), SetFill(1, 0),
    00710             NWidget(WWT_LABEL, COLOUR_GREY, -1), SetDataTip(STR_MUSIC_RULER_MARKER, STR_NULL), SetFill(1, 0),
    00711             NWidget(WWT_LABEL, COLOUR_GREY, -1), SetDataTip(STR_MUSIC_RULER_MARKER, STR_NULL), SetFill(1, 0),
    00712             NWidget(WWT_LABEL, COLOUR_GREY, -1), SetDataTip(STR_MUSIC_RULER_MAX, STR_NULL),
    00713           EndContainer(),
    00714         EndContainer(),
    00715         NWidget(NWID_VERTICAL),
    00716           NWidget(WWT_LABEL, COLOUR_GREY, -1), SetFill(1, 0), SetDataTip(STR_MUSIC_EFFECTS_VOLUME, STR_NULL),
    00717           NWidget(WWT_EMPTY, COLOUR_GREY, WID_M_EFFECT_VOL), SetMinimalSize(67, 0), SetMinimalTextLines(1, 0), SetFill(1, 0), SetDataTip(0x0, STR_MUSIC_TOOLTIP_DRAG_SLIDERS_TO_SET_MUSIC),
    00718           NWidget(NWID_HORIZONTAL),
    00719             NWidget(WWT_LABEL, COLOUR_GREY, -1), SetDataTip(STR_MUSIC_RULER_MIN, STR_NULL),
    00720             NWidget(WWT_LABEL, COLOUR_GREY, -1), SetDataTip(STR_MUSIC_RULER_MARKER, STR_NULL), SetFill(1, 0),
    00721             NWidget(WWT_LABEL, COLOUR_GREY, -1), SetDataTip(STR_MUSIC_RULER_MARKER, STR_NULL), SetFill(1, 0),
    00722             NWidget(WWT_LABEL, COLOUR_GREY, -1), SetDataTip(STR_MUSIC_RULER_MARKER, STR_NULL), SetFill(1, 0),
    00723             NWidget(WWT_LABEL, COLOUR_GREY, -1), SetDataTip(STR_MUSIC_RULER_MARKER, STR_NULL), SetFill(1, 0),
    00724             NWidget(WWT_LABEL, COLOUR_GREY, -1), SetDataTip(STR_MUSIC_RULER_MARKER, STR_NULL), SetFill(1, 0),
    00725             NWidget(WWT_LABEL, COLOUR_GREY, -1), SetDataTip(STR_MUSIC_RULER_MAX, STR_NULL),
    00726           EndContainer(),
    00727         EndContainer(),
    00728       EndContainer(),
    00729     EndContainer(),
    00730   EndContainer(),
    00731   NWidget(WWT_PANEL, COLOUR_GREY, WID_M_BACKGROUND),
    00732     NWidget(NWID_HORIZONTAL), SetPIP(6, 0, 6),
    00733       NWidget(NWID_VERTICAL),
    00734         NWidget(NWID_SPACER), SetFill(0, 1),
    00735         NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_M_SHUFFLE), SetMinimalSize(50, 8), SetDataTip(STR_MUSIC_SHUFFLE, STR_MUSIC_TOOLTIP_TOGGLE_PROGRAM_SHUFFLE),
    00736         NWidget(NWID_SPACER), SetFill(0, 1),
    00737       EndContainer(),
    00738       NWidget(NWID_VERTICAL), SetPadding(0, 0, 3, 3),
    00739         NWidget(WWT_LABEL, COLOUR_GREY, WID_M_TRACK), SetFill(0, 0), SetDataTip(STR_MUSIC_TRACK, STR_NULL),
    00740         NWidget(WWT_PANEL, COLOUR_GREY, WID_M_TRACK_NR), EndContainer(),
    00741       EndContainer(),
    00742       NWidget(NWID_VERTICAL), SetPadding(0, 3, 3, 0),
    00743         NWidget(WWT_LABEL, COLOUR_GREY, WID_M_TRACK_TITLE), SetFill(1, 0), SetDataTip(STR_MUSIC_XTITLE, STR_NULL),
    00744         NWidget(WWT_PANEL, COLOUR_GREY, WID_M_TRACK_NAME), SetFill(1, 0), EndContainer(),
    00745       EndContainer(),
    00746       NWidget(NWID_VERTICAL),
    00747         NWidget(NWID_SPACER), SetFill(0, 1),
    00748         NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_M_PROGRAMME), SetMinimalSize(50, 8), SetDataTip(STR_MUSIC_PROGRAM, STR_MUSIC_TOOLTIP_SHOW_MUSIC_TRACK_SELECTION),
    00749         NWidget(NWID_SPACER), SetFill(0, 1),
    00750       EndContainer(),
    00751     EndContainer(),
    00752   EndContainer(),
    00753   NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
    00754     NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_M_ALL), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_ALL, STR_MUSIC_TOOLTIP_SELECT_ALL_TRACKS_PROGRAM),
    00755     NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_M_OLD), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_OLD_STYLE, STR_MUSIC_TOOLTIP_SELECT_OLD_STYLE_MUSIC),
    00756     NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_M_NEW), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_NEW_STYLE, STR_MUSIC_TOOLTIP_SELECT_NEW_STYLE_MUSIC),
    00757     NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_M_EZY), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_EZY_STREET, STR_MUSIC_TOOLTIP_SELECT_EZY_STREET_STYLE),
    00758     NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_M_CUSTOM1), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_CUSTOM_1, STR_MUSIC_TOOLTIP_SELECT_CUSTOM_1_USER_DEFINED),
    00759     NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_M_CUSTOM2), SetFill(1, 0), SetDataTip(STR_MUSIC_PLAYLIST_CUSTOM_2, STR_MUSIC_TOOLTIP_SELECT_CUSTOM_2_USER_DEFINED),
    00760   EndContainer(),
    00761 };
    00762
    00763 static WindowDesc _music_window_desc(
    00764   WDP_AUTO, "music", 0, 0,
    00765   WC_MUSIC_WINDOW, WC_NONE,
    00766   0,
    00767   _nested_music_window_widgets, lengthof(_nested_music_window_widgets)
    00768 );
    00769
    00770 void ShowMusicWindow()
    00771 {
    00772   if (BaseMusic::GetUsedSet()->num_available == 0) ShowErrorMessage(STR_ERROR_NO_SONGS, INVALID_STRING_ID, WL_WARNING);
    00773   AllocateWindowDescFront<MusicWindow>(&_music_window_desc, 0);
    00774 }