1 /* Gnome M usic Player Client (GMPC)
2 * Copyright (C) 2004-2009 Qball Cow <qball@sarine.nl>
3 * Project homepage: http://gmpc.wikia.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 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>
31 #include "vala/gmpc-test-plugin.h"
32 #include "metadata-cache.h"
35 class Gmpc:Meta:Text:View from Gtk:Text:View {
36 private GtkTextBuffer *buffer = {gtk_text_buffer_new(NULL)} unrefwith g_object_unref;
37 private mpd_Song *song = {NULL} destroywith mpd_freeSong;
38 private MetaDataType type = {META_ARTIST_TXT};
39 private gulong meta_id = {0};
40 private GtkWidget *edit = {NULL};
41 private GtkWidget *cancel = {NULL};
42 private MetaData *met = {NULL} destroywith meta_data_free;
44 public gboolean use_monospace = {FALSE};
47 key_press_event(self, GdkEventKey *key, gpointer user_data)
49 if(key->keyval == GDK_Escape)
51 if(self->_priv->cancel){
52 gtk_widget_activate(self->_priv->cancel);
61 gtk_text_view_set_buffer(GTK_TEXT_VIEW(self), self->_priv->buffer);
62 gtk_text_view_set_editable(GTK_TEXT_VIEW(self), FALSE);
63 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(self), GTK_WRAP_WORD);
64 g_signal_connect(G_OBJECT(self), "key-press-event", G_CALLBACK(self_key_press_event), NULL);
66 g_signal_connect(G_OBJECT(self), "populate-popup", G_CALLBACK(self_menu_populate_callback), NULL);
67 self->_priv->meta_id= g_signal_connect(G_OBJECT(gmw), "data-changed", G_CALLBACK(self_meta_callback), self);
71 GtkWidget * new (int type)
74 gmi->_priv->type = type;
75 gtk_text_buffer_create_tag (gmi->_priv->buffer, "not_editable",
79 gtk_text_buffer_create_tag (gmi->_priv->buffer, "monospace",
80 "family", "Monospace",
83 return (GtkWidget *)gmi;
88 menu_populate_callback(self, GtkMenu *menu, gpointer data)
92 GtkWidget *item = gtk_separator_menu_item_new();
93 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
96 item = gtk_image_menu_item_new_with_label(_("Refetch"));
97 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), gtk_image_new_from_stock(GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU));
98 g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(self_query_refetch),self);
99 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
101 item = gtk_image_menu_item_new_with_label(_("Select file"));
102 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU));
103 g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(self_select_file),self);
104 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
106 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_CLEAR, NULL);
107 g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(self_clear_entry),self);
108 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
110 item = gtk_image_menu_item_new_with_label(_("Metadata selector"));
111 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), gtk_image_new_from_stock(GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU));
112 g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(self_select_metadata_editor),self);
113 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
115 gtk_widget_show_all(GTK_WIDGET(menu));
120 select_metadata_editor(self)
122 gmpc_meta_data_edit_window_new(self->_priv->song,self->_priv->type);
129 if(self->_priv->song)
133 ret = gmpc_meta_watcher_get_meta_path(gmw,self->_priv->song, self->_priv->type|META_QUERY_NO_CACHE, &met);
134 if(ret == META_DATA_FETCHING) {
135 self_set_text_fetching(self);
136 }else if (ret == META_DATA_AVAILABLE) {
137 self_set_text_from_metadata(self, met);
139 self_set_text_na(self);
150 query_text_from_song(self, mpd_Song *song)
152 MetaData *met = NULL;
155 if(self->_priv->song)
157 mpd_freeSong(self->_priv->song);
159 self->_priv->song = mpd_songDup(song);
162 ret = gmpc_meta_watcher_get_meta_path(gmw,self->_priv->song, self->_priv->type, &met);
163 if(ret == META_DATA_FETCHING)
165 self_set_text_fetching(self);
166 }else if (ret == META_DATA_AVAILABLE) {
167 self_set_text_from_metadata(self,met);
169 self_set_text_na(self);
178 cancel_button_clicked(self, GtkWidget *button)
180 gtk_text_view_set_editable(GTK_TEXT_VIEW(self), FALSE);
183 MetaData *met = self->_priv->met;
184 self->_priv->met = NULL;
185 /* Create a copy as self_set_from_path modifies self->_priv->path */
186 self_set_text_from_metadata(self, met);
188 }else self_set_text_na(self);
192 edit_button_clicked(self, GtkWidget *button)
194 if(gtk_text_view_get_editable(GTK_TEXT_VIEW(self)))
197 gchar *content = NULL;
198 GtkTextIter iter_start, iter_end;
200 /* Get start and end of text, and store it */
201 gtk_text_buffer_get_start_iter(self->_priv->buffer, &iter_start);
202 gtk_text_buffer_get_end_iter(self->_priv->buffer, &iter_end);
203 content = gtk_text_buffer_get_text(self->_priv->buffer, &iter_start, &iter_end, FALSE);
204 gtk_text_view_set_editable(GTK_TEXT_VIEW(self), FALSE);
206 /* Copy path, because signal callback my destroy path first */
207 met = meta_data_new();
208 met->plugin_name = "User set";
209 met->type = self->_priv->type;
211 met->content_type = META_DATA_CONTENT_TEXT;
212 met->content = g_strstrip(content);met->size = -1;
214 met->content_type = META_DATA_CONTENT_EMPTY;
216 meta_data_set_cache(self->_priv->song, META_DATA_AVAILABLE, met);
217 gmpc_meta_watcher_data_changed(gmw, self->_priv->song, self->_priv->type, META_DATA_AVAILABLE, 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);
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 const gchar *text = meta_data_get_html(met);
326 /* Get byte length */
327 gsize length = strlen(text);
328 gchar *data = g_malloc0(length+1);
331 for(i=0; i<length;i++){
332 if(text[i] == '<') depth++;
333 else if(text[i] == '>') depth--;
340 self_set_text(self,data, -1, FALSE);
343 self_set_text_na(self);
346 if(self->_priv->met) meta_data_free(self->_priv->met);
347 self->_priv->met = meta_data_dup(met);
350 signal last NONE (NONE)
352 set_text_fetching(self)
354 if(self->_priv->met) meta_data_free(self->_priv->met);
355 self->_priv->met = NULL;
356 if(self->_priv->type == META_SONG_TXT)
358 self_set_text(self, _("Fetching Lyrics"), -1,TRUE);
360 else if (self->_priv->type == META_ARTIST_TXT)
362 self_set_text(self, _("Fetching Artist Info"), -1,TRUE);
364 else if (self->_priv->type == META_SONG_GUITAR_TAB)
366 self_set_text(self, _("Fetching Guitar tab"), -1,TRUE);
370 self_set_text(self, _("Fetching Album Info"), -1,TRUE);
374 signal last NONE (NONE)
378 if(self->_priv->met) meta_data_free(self->_priv->met);
379 self->_priv->met = NULL;
381 self_set_text(self, _("Not Available"), -1,FALSE);
386 meta_callback(GmpcMetaWatcher *mw , mpd_Song *song, MetaDataType type, MetaDataResult ret, MetaData *met,gpointer data)
392 if(self->_priv->type != type)
395 if(!gmpc_meta_watcher_match_data(self->_priv->type, self->_priv->song, song))
399 if(ret == META_DATA_AVAILABLE) {
400 self_set_text_from_metadata(self,met);
401 } else if (ret == META_DATA_FETCHING) {
402 self_set_text_fetching(self);
404 self_set_text_na(self);
409 finalize (G:Object *obj)
416 dispose (G:Object *obj)
418 Self *self = GMPC_META_TEXT_VIEW(obj);
419 if(self->_priv->meta_id)
421 g_signal_handler_disconnect(G_OBJECT(gmw),self->_priv->meta_id);
422 self->_priv->meta_id = 0;
431 MetaData *met = meta_data_new();
432 met->type = self->_priv->type;
433 met->plugin_name = "User set";
434 met->content_type = META_DATA_CONTENT_EMPTY;
435 meta_data_set_cache(self->_priv->song, META_DATA_UNAVAILABLE, met);
436 gmpc_meta_watcher_data_changed(gmw, self->_priv->song, self->_priv->type, META_DATA_UNAVAILABLE, NULL);
443 mpd_Song *song = mpd_songDup(self->_priv->song);
444 MetaDataType type = self->_priv->type;
445 GtkFileFilter *gff = gtk_file_filter_new();
446 GtkWidget *fcd = gtk_file_chooser_dialog_new(_("Select File"),NULL,
447 GTK_FILE_CHOOSER_ACTION_OPEN,
448 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
449 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
451 gtk_file_filter_set_name(gff, _("Text Document"));
452 gtk_file_filter_add_mime_type(gff, "text/plain");
453 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fcd), gff);
454 gff = gtk_file_filter_new();
455 gtk_file_filter_set_name(gff, _("All"));
456 gtk_file_filter_add_pattern(gff, "*");
457 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fcd), gff);
459 gtk_widget_show_all(fcd);
460 switch(gtk_dialog_run(GTK_DIALOG(fcd)))
462 case GTK_RESPONSE_ACCEPT:
464 gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fcd));
465 MetaData *met = meta_data_new();
467 met->plugin_name = "User set";
468 met->content_type = META_DATA_CONTENT_URI;
469 met->content = filename; met->size =-1;
470 meta_data_set_cache(song, META_DATA_AVAILABLE, met);
471 gmpc_meta_watcher_data_changed(gmw, song, type, META_DATA_AVAILABLE, met);
478 gtk_widget_destroy(fcd);