1 /***************************************************************************
2 * Copyright (C) 2008-2012 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
33 #include "tag_editor.h"
35 #include "playlist_editor.h"
37 #include "media_library.h"
38 #include "search_engine.h"
40 #include "visualizer.h"
46 std::cout
<< "Couldn't connect to MPD (host = " << Mpd
.GetHostname() << ", port = " << Mpd
.GetPort() << "): " << Mpd
.GetErrorMessage() << std::endl
;
52 void ParseArgv(int argc
, char **argv
)
55 std::string now_playing_format
= "{{{(%l) }{{%a - }%t}}|{%f}}";
57 for (int i
= 1; i
< argc
; ++i
)
59 if (!strcmp(argv
[i
], "-h") || !strcmp(argv
[i
], "--host"))
63 Mpd
.SetHostname(argv
[i
]);
66 if (!strcmp(argv
[i
], "-p") || !strcmp(argv
[i
], "--port"))
70 Mpd
.SetPort(atoi(argv
[i
]));
73 else if (!strcmp(argv
[i
], "-v") || !strcmp(argv
[i
], "--version"))
75 std::cout
<< "ncmpcpp version: " << VERSION
<< "\n\n"
76 << "optional screens compiled-in:\n"
79 << " - tiny tag editor\n"
81 # ifdef HAVE_CURL_CURL_H
84 # ifdef ENABLE_OUTPUTS
87 # ifdef ENABLE_VISUALIZER
93 << "\nencoding detection: "
94 # ifdef HAVE_LANGINFO_H
98 # endif // HAVE_LANGINFO_H
99 << "\nbuilt with support for:"
100 # ifdef HAVE_CURL_CURL_H
114 # ifdef HAVE_TAGLIB_H
123 else if (!strcmp(argv
[i
], "-?") || !strcmp(argv
[i
], "--help"))
126 << "Usage: ncmpcpp [OPTION]...\n"
127 << " -h, --host connect to server at host [localhost]\n"
128 << " -p, --port connect to server at port [6600]\n"
129 << " -c, --config use alternative configuration file\n"
130 << " -s, --screen <name> specify the startup screen\n"
131 << " -?, --help show this help message\n"
132 << " -v, --version display version information\n"
133 << " --now-playing display now playing song [" << now_playing_format
<< "]\n"
135 << " play start playing\n"
136 << " pause pause the currently playing song\n"
137 << " toggle toggle play/pause mode\n"
138 << " stop stop playing\n"
139 << " next play the next song\n"
140 << " prev play the previous song\n"
141 << " volume [+-]<num> adjusts volume by [+-]<num>\n"
149 if (!strcmp(argv
[i
], "-s") || !strcmp(argv
[i
], "--screen"))
152 std::cout
<< "ncmpcpp: no screen specified" << std::endl
;
155 if (!strcmp(argv
[i
], "help"))
156 Config
.startup_screen
= myHelp
;
157 else if (!strcmp(argv
[i
], "playlist"))
158 Config
.startup_screen
= myPlaylist
;
159 else if (!strcmp(argv
[i
], "browser"))
160 Config
.startup_screen
= myBrowser
;
161 else if (!strcmp(argv
[i
], "search-engine"))
162 Config
.startup_screen
= mySearcher
;
163 else if (!strcmp(argv
[i
], "media-library"))
164 Config
.startup_screen
= myLibrary
;
165 else if (!strcmp(argv
[i
], "playlist-editor"))
166 Config
.startup_screen
= myPlaylistEditor
;
167 # ifdef HAVE_TAGLIB_H
168 else if (!strcmp(argv
[i
], "tag-editor"))
169 Config
.startup_screen
= myTagEditor
;
170 # endif // HAVE_TAGLIB_H
171 # ifdef ENABLE_OUTPUTS
172 else if (!strcmp(argv
[i
], "outputs"))
173 Config
.startup_screen
= myOutputs
;
174 # endif // ENABLE_OUTPUTS
175 # ifdef ENABLE_VISUALIZER
176 else if (!strcmp(argv
[i
], "visualizer"))
177 Config
.startup_screen
= myVisualizer
;
178 # endif // ENABLE_VISUALIZER
180 else if (!strcmp(argv
[i
], "clock"))
181 Config
.startup_screen
= myClock
;
182 # endif // ENABLE_CLOCK
184 std::cout
<< "ncmpcpp: invalid screen: " << argv
[i
] << std::endl
;
188 else if (!strcmp(argv
[i
], "--now-playing"))
191 if (!Mpd
.GetErrorMessage().empty())
193 std::cout
<< "Error: " << Mpd
.GetErrorMessage() << std::endl
;
200 if (MPD::Song::isFormatOk("now-playing format", argv
[i
]))
202 // apply additional pair of braces
203 now_playing_format
= "{";
204 now_playing_format
+= argv
[i
];
205 now_playing_format
+= "}";
206 Replace(now_playing_format
, "\\n", "\n");
207 Replace(now_playing_format
, "\\t", "\t");
210 std::cout
<< utf_to_locale_cpy(Mpd
.GetCurrentlyPlayingSong().toString(now_playing_format
)) << "\n";
214 else if (!strcmp(argv
[i
], "play"))
219 else if (!strcmp(argv
[i
], "pause"))
224 else if (!strcmp(argv
[i
], "toggle"))
227 if (!Mpd
.GetErrorMessage().empty())
229 std::cout
<< "Error: " << Mpd
.GetErrorMessage() << std::endl
;
235 else if (!strcmp(argv
[i
], "stop"))
240 else if (!strcmp(argv
[i
], "next"))
245 else if (!strcmp(argv
[i
], "prev"))
250 else if (!strcmp(argv
[i
], "volume"))
254 if (!Mpd
.GetErrorMessage().empty())
256 std::cout
<< "Error: " << Mpd
.GetErrorMessage() << std::endl
;
260 Mpd
.SetVolume(Mpd
.GetVolume()+atoi(argv
[i
]));
263 else if (!strcmp(argv
[i
], "-c") || !strcmp(argv
[i
], "--config"))
265 // this is used in Configuration::CheckForCommandLineConfigFilePath, ignoring here.
270 std::cout
<< "ncmpcpp: invalid option: " << argv
[i
] << std::endl
;
273 if (!Mpd
.GetErrorMessage().empty())
275 std::cout
<< "Error: " << Mpd
.GetErrorMessage() << std::endl
;
283 int CaseInsensitiveStringComparison::operator()(const std::string
&a
, const std::string
&b
)
285 const char *i
= a
.c_str();
286 const char *j
= b
.c_str();
287 if (Config
.ignore_leading_the
)
295 while (!(dist
= tolower(*i
)-tolower(*j
)) && *j
)
300 bool CaseInsensitiveSorting::operator()(const MPD::Item
&a
, const MPD::Item
&b
)
303 if (a
.type
== b
.type
)
307 case MPD::itDirectory
:
308 result
= cmp(ExtractTopName(a
.name
), ExtractTopName(b
.name
)) < 0;
310 case MPD::itPlaylist
:
311 result
= cmp(a
.name
, b
.name
) < 0;
314 switch (Config
.browser_sort_mode
)
317 result
= operator()(a
.song
, b
.song
);
320 result
= a
.song
.getMTime() > b
.song
.getMTime();
323 result
= cmp(a
.song
.toString(Config
.browser_sort_format
), b
.song
.toString(Config
.browser_sort_format
)) < 0;
327 default: // there is no other option, silence compiler
332 result
= a
.type
< b
.type
;
336 std::string
Timestamp(time_t t
)
340 result
[strftime(result
, 31, "%x %X", localtime(&t
))] = 0;
343 result
[strftime(result
, 31, "%x %X", localtime_r(&t
, &info
))] = 0;
348 void UpdateSongList(Menu
<MPD::Song
> *menu
)
351 for (size_t i
= 0; i
< menu
->Size(); ++i
)
353 for (size_t j
= 0; j
< myPlaylist
->Items
->Size(); ++j
)
355 if (myPlaylist
->Items
->at(j
).getHash() == menu
->at(i
).getHash())
368 std::string
FindSharedDir(Menu
<MPD::Song
> *menu
)
371 for (size_t i
= 0; i
< menu
->Size(); ++i
)
372 list
.push_back(&(*menu
)[i
]);
373 return FindSharedDir(list
);
376 std::string
FindSharedDir(const MPD::SongList
&v
)
378 if (v
.empty()) // this should never happen, but in case...
379 FatalError("empty SongList passed to FindSharedDir(const SongList &)!");
381 std::string first
= v
.front()->GetDirectory();
382 for (MPD::SongList::const_iterator it
= ++v
.begin(); it
!= v
.end(); ++it
)
385 std::string dir
= (*it
)->GetDirectory();
386 size_t length
= std::min(first
.length(), dir
.length());
387 while (!first
.compare(j
, 1, dir
, j
, 1) && j
< length
&& j
< i
)
391 return i
? first
.substr(0, i
) : "/";
393 #endif // HAVE_TAGLIB_H
395 std::string
FindSharedDir(const std::string
&one
, const std::string
&two
)
400 while (!one
.compare(i
, 1, two
, i
, 1))
402 i
= one
.rfind("/", i
);
403 return i
!= std::string::npos
? one
.substr(0, i
) : "/";
406 std::string
GetLineValue(std::string
&line
, char a
, char b
, bool once
)
408 int pos
[2] = { -1, -1 };
411 while ((i
= line
.find(x
, i
)) != std::string::npos
&& pos
[1] < 0)
413 if (i
&& line
[i
-1] == '\\')
420 pos
[pos
[0] >= 0] = i
++;
425 std::string result
= pos
[0] >= 0 && pos
[1] >= 0 ? line
.substr(pos
[0], pos
[1]-pos
[0]) : "";
427 // replace \a and \b with a and b respectively
428 char r1
[] = "\\ ", r2
[] = " ";
430 Replace(result
, r1
, r2
);
434 Replace(result
, r1
, r2
);
440 std::string
ExtractTopName(const std::string
&s
)
442 size_t slash
= s
.rfind("/");
443 return slash
!= std::string::npos
? s
.substr(++slash
) : s
;
446 std::string
PathGoDownOneLevel(const std::string
&path
)
448 size_t i
= path
.rfind('/');
449 return i
== std::string::npos
? "/" : path
.substr(0, i
);
452 std::basic_string
<my_char_t
> Scroller(const std::basic_string
<my_char_t
> &str
, size_t &pos
, size_t width
)
454 std::basic_string
<my_char_t
> s(str
);
455 if (!Config
.header_text_scrolling
)
457 std::basic_string
<my_char_t
> result
;
458 size_t len
= Window::Length(s
);
464 std::basic_string
<my_char_t
>::const_iterator b
= s
.begin(), e
= s
.end();
465 for (std::basic_string
<my_char_t
>::const_iterator it
= b
+pos
; it
< e
&& len
< width
; ++it
)
467 if ((len
+= wcwidth(*it
)) > width
)
471 if (++pos
>= s
.length())
473 for (; len
< width
; ++b
)
475 if ((len
+= wcwidth(*b
)) > width
)
485 bool isInteger(const char *s
)
490 for (const char *it
= s
; *it
!= '\0'; ++it
)
491 if (!isdigit(*it
) && (it
!= s
|| *it
!= '-'))