1 /* Gnome M usic Player Client (GMPC)
2 * Copyright (C) 2004-2012 Qball Cow <qball@gmpclient.org>
3 * Project homepage: http://gmpclient.org/
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 along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include <glib/gstdio.h>
25 #include <libmpd/libmpd.h>
30 #include "gmpc-extras.h"
34 class Gmpc:Meta:Text:View from Gtk:Text:View {
35 private GtkTextBuffer *buffer = {gtk_text_buffer_new(NULL)} unrefwith g_object_unref;
36 private mpd_Song *song = {NULL} destroywith mpd_freeSong;
37 private MetaDataType type = {META_ARTIST_TXT};
38 private gulong meta_id = {0};
39 private GtkWidget *edit = {NULL};
40 private GtkWidget *cancel = {NULL};
41 private MetaData *met = {NULL} destroywith meta_data_free;
43 public gboolean force_ro = {FALSE};
44 public gboolean use_monospace = {FALSE};
48 key_press_event(self, GdkEventKey *key, gpointer user_data)
50 if(key->keyval == GDK_KEY_Escape)
52 if(self->_priv->cancel){
53 gtk_widget_activate(self->_priv->cancel);
62 gtk_text_view_set_buffer(GTK_TEXT_VIEW(self), self->_priv->buffer);
63 gtk_text_view_set_editable(GTK_TEXT_VIEW(self), FALSE);
64 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(self), GTK_WRAP_WORD);
65 g_signal_connect(G_OBJECT(self), "key-press-event", G_CALLBACK(self_key_press_event), NULL);
67 g_signal_connect(G_OBJECT(self), "populate-popup", G_CALLBACK(self_menu_populate_callback), NULL);
68 self->_priv->meta_id= g_signal_connect(G_OBJECT(gmw), "data-changed", G_CALLBACK(self_meta_callback), self);
72 Gmpc:Meta:Text:View * new (int type)
75 gmi->_priv->type = type;
76 gtk_text_buffer_create_tag (gmi->_priv->buffer, "not_editable",
80 gtk_text_buffer_create_tag (gmi->_priv->buffer, "monospace",
81 "family", "Monospace",
89 menu_populate_callback(self, GtkMenu *menu, gpointer data)
93 GtkWidget *item = gtk_separator_menu_item_new();
94 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
97 item = gtk_image_menu_item_new_with_label(_("Refetch"));
98 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), gtk_image_new_from_stock(GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU));
99 g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(self_query_refetch),self);
100 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
102 item = gtk_image_menu_item_new_with_label(_("Select file"));
103 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU));
104 g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(self_select_file),self);
105 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
107 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_CLEAR, NULL);
108 g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(self_clear_entry),self);
109 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
111 item = gtk_image_menu_item_new_with_label(_("Metadata selector"));
112 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), gtk_image_new_from_stock(GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU));
113 g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(self_select_metadata_editor),self);
114 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
116 gtk_widget_show_all(GTK_WIDGET(menu));
121 select_metadata_editor(self)
123 gmpc_meta_data_edit_window_new(self->_priv->song,self->_priv->type);
130 if(self->_priv->song)
134 ret = meta_data_get_path(self->_priv->song, self->_priv->type|META_QUERY_NO_CACHE, &met, NULL, NULL);
135 if(ret == META_DATA_FETCHING) {
136 self_set_text_fetching(self);
137 }else if (ret == META_DATA_AVAILABLE) {
138 self_set_text_from_metadata(self, met);
140 self_set_text_na(self);
151 query_text_from_song(self, mpd_Song *song)
153 MetaData *met = NULL;
156 if(self->_priv->song)
158 mpd_freeSong(self->_priv->song);
160 self->_priv->song = mpd_songDup(song);
163 ret = meta_data_get_path(self->_priv->song, self->_priv->type, &met, NULL, NULL);
164 if(ret == META_DATA_FETCHING)
166 self_set_text_fetching(self);
167 }else if (ret == META_DATA_AVAILABLE) {
168 self_set_text_from_metadata(self,met);
170 self_set_text_na(self);
179 cancel_button_clicked(self, GtkWidget *button)
181 gtk_text_view_set_editable(GTK_TEXT_VIEW(self), FALSE);
184 MetaData *met = self->_priv->met;
185 self->_priv->met = NULL;
186 /* Create a copy as self_set_from_path modifies self->_priv->path */
187 self_set_text_from_metadata(self, met);
189 }else self_set_text_na(self);
193 edit_button_clicked(self, GtkWidget *button)
195 if(gtk_text_view_get_editable(GTK_TEXT_VIEW(self)))
198 gchar *content = NULL;
199 GtkTextIter iter_start, iter_end;
201 /* Get start and end of text, and store it */
202 gtk_text_buffer_get_start_iter(self->_priv->buffer, &iter_start);
203 gtk_text_buffer_get_end_iter(self->_priv->buffer, &iter_end);
204 content = gtk_text_buffer_get_text(self->_priv->buffer, &iter_start, &iter_end, FALSE);
205 gtk_text_view_set_editable(GTK_TEXT_VIEW(self), FALSE);
207 /* Copy path, because signal callback my destroy path first */
208 met = meta_data_new();
209 met->plugin_name = g_strdup("User set");
210 met->type = self->_priv->type;
212 met->content_type = META_DATA_CONTENT_TEXT;
213 met->content = g_strstrip(content);met->size = -1;
215 met->content_type = META_DATA_CONTENT_EMPTY;
217 meta_data_set_entry(self->_priv->song, met);
221 gtk_widget_show(self->_priv->cancel);
222 gtk_text_view_set_editable(GTK_TEXT_VIEW(self), TRUE);
223 gtk_button_set_label(GTK_BUTTON(button), GTK_STOCK_SAVE);
229 set_text(self,const gchar *text, gsize length, int ro)
232 GtkTextIter end_iter;
233 gtk_text_buffer_set_text(self->_priv->buffer, text, length);
235 gtk_text_buffer_get_start_iter(self->_priv->buffer, &iter);
236 gtk_text_buffer_get_end_iter(self->_priv->buffer, &end_iter);
237 if(self->use_monospace) {
238 gtk_text_buffer_apply_tag_by_name(self->_priv->buffer, "monospace",&iter, &end_iter);
240 if(!ro && !self->force_ro)
242 GtkTextChildAnchor *anch;
243 self->_priv->edit = gtk_button_new_from_stock(GTK_STOCK_EDIT);
244 self->_priv->cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
246 gtk_text_view_set_editable(GTK_TEXT_VIEW(self), FALSE);
247 gtk_widget_set_no_show_all(self->_priv->cancel, TRUE);
248 /* Add 2 empty lines at the end, and make it non-editable */
249 gtk_text_buffer_get_end_iter(self->_priv->buffer, &end_iter);
250 gtk_text_buffer_insert_with_tags_by_name(self->_priv->buffer, &end_iter, "\n\n", -1,"not_editable", NULL);
252 /* Add edit widget */
253 anch = gtk_text_buffer_create_child_anchor(self->_priv->buffer, &end_iter);
254 gtk_text_buffer_get_iter_at_child_anchor(self->_priv->buffer, &iter,anch);
255 /* Make widget non-editable */
256 gtk_text_buffer_apply_tag_by_name(self->_priv->buffer, "not_editable",&iter, &end_iter);
257 /* attach widget to anchor */
258 gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(self), self->_priv->edit, anch);
260 /* Add edit widget */
261 gtk_text_buffer_get_end_iter(self->_priv->buffer, &end_iter);
262 anch = gtk_text_buffer_create_child_anchor(self->_priv->buffer, &end_iter);
263 gtk_text_buffer_get_iter_at_child_anchor(self->_priv->buffer, &iter,anch);
264 /* Make widget non-editable */
265 gtk_text_buffer_apply_tag_by_name(self->_priv->buffer, "not_editable",&iter, &end_iter);
266 /* attach widget to anchor */
267 gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(self), self->_priv->cancel, anch);
270 gtk_widget_show(self->_priv->edit);
271 g_signal_connect_swapped(self->_priv->edit, "clicked",G_CALLBACK(self_edit_button_clicked), self);
272 g_signal_connect_swapped(self->_priv->cancel, "clicked",G_CALLBACK(self_cancel_button_clicked), self);
274 gtk_text_buffer_get_start_iter(self->_priv->buffer, &iter);
275 gtk_text_buffer_place_cursor(self->_priv->buffer, &iter);
278 signal last NONE (STRING)
280 set_text_from_path(self,const gchar *path)
284 if(g_file_get_contents (path,&content,&size,NULL))
286 gchar *new_data = NULL;
288 if(g_utf8_validate(content, size, NULL))
290 self_set_text(self, content, size, FALSE);
294 new_data = g_locale_to_utf8(content, size, NULL, &new_size, NULL);
297 self_set_text(self, new_data, new_size,FALSE);
302 new_data = g_strdup_printf("%s: '%s' %s", _("Failed to open file:"), path,_("because of encoding issues"));
303 self_set_text(self, new_data, -1,TRUE);
313 set_text_from_metadata(self, MetaData *met)
315 if(met->content_type == META_DATA_CONTENT_URI) {
316 const gchar *path = meta_data_get_uri(met);
317 self_set_text_from_path(self, path);
318 }else if(met->content_type == META_DATA_CONTENT_TEXT) {
319 const gchar *text = meta_data_get_text(met);
320 self_set_text(self,text, -1, FALSE);
321 }else if (met->content_type == META_DATA_CONTENT_HTML) {
322 /* TODO: make utf-8 compatible */
323 gchar *text = meta_data_get_text_from_html(met);
326 /* Get byte length */
327 self_set_text(self,text, -1, FALSE);
330 self_set_text_na(self);
333 if(self->_priv->met) meta_data_free(self->_priv->met);
334 self->_priv->met = meta_data_dup(met);
337 signal last NONE (NONE)
339 set_text_fetching(self)
341 if(self->_priv->met) meta_data_free(self->_priv->met);
342 self->_priv->met = NULL;
343 if(self->_priv->type == META_SONG_TXT)
345 self_set_text(self, _("Fetching Lyrics"), -1,TRUE);
347 else if (self->_priv->type == META_ARTIST_TXT)
349 self_set_text(self, _("Fetching Artist Info"), -1,TRUE);
351 else if (self->_priv->type == META_SONG_GUITAR_TAB)
353 self_set_text(self, _("Fetching Guitar tab"), -1,TRUE);
357 self_set_text(self, _("Fetching Album Info"), -1,TRUE);
361 signal last NONE (NONE)
365 if(self->_priv->met) meta_data_free(self->_priv->met);
366 self->_priv->met = NULL;
368 self_set_text(self, _("Not Available"), -1,FALSE);
373 meta_callback(GmpcMetaWatcher *mw , mpd_Song *song, MetaDataType type, MetaDataResult ret, MetaData *met,gpointer data)
379 if(self->_priv->type != type)
382 // Compare if callback is about 'our' song.
383 // TODO: optimize, by keeping checksum of current song around?
385 char *ck_a = mpd_song_checksum_type(self->_priv->song, self->_priv->type);
386 char *ck_b = mpd_song_checksum_type(song, self->_priv->type);
387 if(ck_a == NULL || ck_b == NULL || strcmp(ck_a, ck_b) != 0) {
396 if(ret == META_DATA_AVAILABLE) {
397 self_set_text_from_metadata(self,met);
398 } else if (ret == META_DATA_FETCHING) {
399 self_set_text_fetching(self);
401 self_set_text_na(self);
406 finalize (G:Object *obj)
413 dispose (G:Object *obj)
415 Self *self = GMPC_META_TEXT_VIEW(obj);
416 if(self->_priv->meta_id)
418 g_signal_handler_disconnect(G_OBJECT(gmw),self->_priv->meta_id);
419 self->_priv->meta_id = 0;
428 meta_data_clear_entry(self->_priv->song, self->_priv->type);
435 mpd_Song *song = mpd_songDup(self->_priv->song);
436 MetaDataType type = self->_priv->type;
437 GtkFileFilter *gff = gtk_file_filter_new();
438 GtkWidget *fcd = gtk_file_chooser_dialog_new(_("Select File"),NULL,
439 GTK_FILE_CHOOSER_ACTION_OPEN,
440 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
441 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
443 gtk_file_filter_set_name(gff, _("Text Document"));
444 gtk_file_filter_add_mime_type(gff, "text/plain");
445 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fcd), gff);
446 gff = gtk_file_filter_new();
447 gtk_file_filter_set_name(gff, _("All"));
448 gtk_file_filter_add_pattern(gff, "*");
449 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fcd), gff);
451 p = cfg_get_single_value_as_string(config, "MetaData", "text-file-chooser");
453 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(fcd), p);
456 gtk_widget_show_all(fcd);
457 switch(gtk_dialog_run(GTK_DIALOG(fcd)))
459 case GTK_RESPONSE_ACCEPT:
461 gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fcd));
462 MetaData *met = meta_data_new();
464 met->plugin_name = g_strdup("User set");
465 met->content_type = META_DATA_CONTENT_URI;
466 met->content = filename; met->size =-1;
468 meta_data_set_entry(self->_priv->song, met);
469 cfg_set_single_value_as_string(config, "MetaData", "text-file-chooser", filename);
476 gtk_widget_destroy(fcd);