2008-05-05 Paolo Borelli <pborelli@katamail.com>
[nautilus.git] / src / nautilus-notes-viewer.c
blob1ab4c843a081fdb1b6c5638202dfaf86eb4b08d8
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
3 /*
4 * Nautilus
6 * Copyright (C) 2000, 2001 Eazel, Inc.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this library; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 * Author: Andy Hertzfeld <andy@eazel.com>
26 /* notes sidebar panel -- allows editing per-directory notes */
28 #include <config.h>
30 #include "nautilus-notes-viewer.h"
32 #include <eel/eel-debug.h>
33 #include <eel/eel-gtk-extensions.h>
34 #include <eel/eel-string.h>
35 #include <gtk/gtkmain.h>
36 #include <gtk/gtksignal.h>
37 #include <gtk/gtktextbuffer.h>
38 #include <gtk/gtktextview.h>
39 #include <gtk/gtkvbox.h>
40 #include <gtk/gtkscrolledwindow.h>
41 #include <glib/gi18n.h>
42 #include <libnautilus-private/nautilus-file-attributes.h>
43 #include <libnautilus-private/nautilus-file.h>
44 #include <libnautilus-private/nautilus-file-utilities.h>
45 #include <libnautilus-private/nautilus-global-preferences.h>
46 #include <libnautilus-private/nautilus-metadata.h>
47 #include <libnautilus-private/nautilus-clipboard.h>
48 #include <libnautilus-private/nautilus-module.h>
49 #include <libnautilus-private/nautilus-sidebar-provider.h>
50 #include <libnautilus-extension/nautilus-property-page-provider.h>
52 #define SAVE_TIMEOUT (3 * 1000)
54 static void load_note_text_from_metadata (NautilusFile *file,
55 NautilusNotesViewer *notes);
56 static void notes_save_metainfo (NautilusNotesViewer *notes);
57 static void nautilus_notes_viewer_sidebar_iface_init (NautilusSidebarIface *iface);
58 static void on_changed (GtkEditable *editable,
59 NautilusNotesViewer *notes);
60 static void property_page_provider_iface_init (NautilusPropertyPageProviderIface *iface);
61 static void sidebar_provider_iface_init (NautilusSidebarProviderIface *iface);
63 typedef struct {
64 GtkScrolledWindowClass parent;
65 } NautilusNotesViewerClass;
67 typedef struct {
68 GObject parent;
69 } NautilusNotesViewerProvider;
71 typedef struct {
72 GObjectClass parent;
73 } NautilusNotesViewerProviderClass;
76 G_DEFINE_TYPE_WITH_CODE (NautilusNotesViewer, nautilus_notes_viewer, GTK_TYPE_SCROLLED_WINDOW,
77 G_IMPLEMENT_INTERFACE (NAUTILUS_TYPE_SIDEBAR,
78 nautilus_notes_viewer_sidebar_iface_init));
80 static GType nautilus_notes_viewer_provider_get_type (void);
82 G_DEFINE_TYPE_WITH_CODE (NautilusNotesViewerProvider, nautilus_notes_viewer_provider, G_TYPE_OBJECT,
83 G_IMPLEMENT_INTERFACE (NAUTILUS_TYPE_PROPERTY_PAGE_PROVIDER,
84 property_page_provider_iface_init);
85 G_IMPLEMENT_INTERFACE (NAUTILUS_TYPE_SIDEBAR_PROVIDER,
86 sidebar_provider_iface_init));
89 struct _NautilusNotesViewerDetails {
90 GtkWidget *note_text_field;
91 GtkTextBuffer *text_buffer;
92 char *uri;
93 NautilusFile *file;
94 guint save_timeout_id;
95 char *previous_saved_text;
96 GdkPixbuf *icon;
99 static gboolean
100 schedule_save_callback (gpointer data)
102 NautilusNotesViewer *notes;
104 notes = data;
106 /* Zero out save_timeout_id so no one will try to cancel our
107 * in-progress timeout callback.
109 notes->details->save_timeout_id = 0;
111 notes_save_metainfo (notes);
113 return FALSE;
116 static void
117 cancel_pending_save (NautilusNotesViewer *notes)
119 if (notes->details->save_timeout_id != 0) {
120 g_source_remove (notes->details->save_timeout_id);
121 notes->details->save_timeout_id = 0;
125 static void
126 schedule_save (NautilusNotesViewer *notes)
128 cancel_pending_save (notes);
130 notes->details->save_timeout_id = g_timeout_add (SAVE_TIMEOUT, schedule_save_callback, notes);
133 /* notifies event listeners if the notes data actually changed */
134 static void
135 set_saved_text (NautilusNotesViewer *notes, char *new_notes)
137 char *old_text;
139 old_text = notes->details->previous_saved_text;
140 notes->details->previous_saved_text = new_notes;
142 if (eel_strcmp (old_text, new_notes) != 0) {
143 g_signal_emit_by_name (NAUTILUS_SIDEBAR (notes),
144 "tab_icon_changed");
147 g_free (old_text);
150 /* save the metainfo corresponding to the current uri, if any, into the text field */
151 static void
152 notes_save_metainfo (NautilusNotesViewer *notes)
154 char *notes_text;
155 GtkTextIter start_iter;
156 GtkTextIter end_iter;
158 if (notes->details->file == NULL) {
159 return;
162 cancel_pending_save (notes);
164 /* Block the handler, so we don't respond to our own change.
166 g_signal_handlers_block_matched (notes->details->file,
167 G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA,
168 0, 0, NULL,
169 G_CALLBACK (load_note_text_from_metadata),
170 notes);
172 gtk_text_buffer_get_start_iter (notes->details->text_buffer, &start_iter);
173 gtk_text_buffer_get_end_iter (notes->details->text_buffer, &end_iter);
174 notes_text = gtk_text_buffer_get_text (notes->details->text_buffer,
175 &start_iter,
176 &end_iter,
177 FALSE);
179 nautilus_file_set_metadata (notes->details->file,
180 NAUTILUS_METADATA_KEY_ANNOTATION,
181 NULL, notes_text);
183 g_signal_handlers_unblock_matched (notes->details->file,
184 G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA,
185 0, 0, NULL,
186 G_CALLBACK (load_note_text_from_metadata),
187 notes);
189 set_saved_text (notes, notes_text);
192 static void
193 load_note_text_from_metadata (NautilusFile *file,
194 NautilusNotesViewer *notes)
196 char *saved_text;
198 g_assert (NAUTILUS_IS_FILE (file));
199 g_assert (notes->details->file == file);
201 saved_text = nautilus_file_get_metadata (file, NAUTILUS_METADATA_KEY_ANNOTATION, "");
203 /* This fn is called for any change signal on the file, so make sure that the
204 * metadata has actually changed.
206 if (eel_strcmp (saved_text, notes->details->previous_saved_text) != 0) {
207 set_saved_text (notes, saved_text);
208 cancel_pending_save (notes);
210 /* Block the handler, so we don't respond to our own change.
212 g_signal_handlers_block_matched (notes->details->text_buffer,
213 G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA,
214 0, 0, NULL,
215 G_CALLBACK (on_changed),
216 notes);
217 gtk_text_buffer_set_text (notes->details->text_buffer, saved_text, -1);
218 g_signal_handlers_unblock_matched (notes->details->text_buffer,
219 G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA,
220 0, 0, NULL,
221 G_CALLBACK (on_changed),
222 notes);
223 } else {
224 g_free (saved_text);
228 static void
229 done_with_file (NautilusNotesViewer *notes)
231 cancel_pending_save (notes);
233 if (notes->details->file != NULL) {
234 nautilus_file_monitor_remove (notes->details->file, notes);
235 g_signal_handlers_disconnect_matched (notes->details->file,
236 G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA,
237 0, 0, NULL,
238 G_CALLBACK (load_note_text_from_metadata),
239 notes);
240 nautilus_file_unref (notes->details->file);
244 static void
245 notes_load_metainfo (NautilusNotesViewer *notes)
247 NautilusFileAttributes attributes;
249 done_with_file (notes);
250 notes->details->file = nautilus_file_get_by_uri (notes->details->uri);
252 /* Block the handler, so we don't respond to our own change.
254 g_signal_handlers_block_matched (notes->details->text_buffer,
255 G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA,
256 0, 0, NULL,
257 G_CALLBACK (on_changed),
258 notes);
259 gtk_text_buffer_set_text (notes->details->text_buffer, "", -1);
260 g_signal_handlers_unblock_matched (notes->details->text_buffer,
261 G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA,
262 0, 0, NULL,
263 G_CALLBACK (on_changed),
264 notes);
266 if (notes->details->file == NULL) {
267 return;
270 attributes = NAUTILUS_FILE_ATTRIBUTE_METADATA;
271 nautilus_file_monitor_add (notes->details->file, notes, attributes);
273 if (nautilus_file_check_if_ready (notes->details->file, attributes)) {
274 load_note_text_from_metadata (notes->details->file, notes);
277 g_signal_connect (notes->details->file, "changed",
278 G_CALLBACK (load_note_text_from_metadata), notes);
281 static void
282 loading_uri_callback (NautilusSidebar *sidebar,
283 const char *location,
284 NautilusNotesViewer *notes)
286 if (strcmp (notes->details->uri, location) != 0) {
287 notes_save_metainfo (notes);
288 g_free (notes->details->uri);
289 notes->details->uri = g_strdup (location);
290 notes_load_metainfo (notes);
294 static gboolean
295 on_text_field_focus_out_event (GtkWidget *widget,
296 GdkEventFocus *event,
297 gpointer callback_data)
299 NautilusNotesViewer *notes;
301 notes = callback_data;
302 notes_save_metainfo (notes);
303 return FALSE;
306 static void
307 on_changed (GtkEditable *editable, NautilusNotesViewer *notes)
309 schedule_save (notes);
312 static void
313 nautilus_notes_viewer_init (NautilusNotesViewer *sidebar)
315 char *image_path;
316 NautilusNotesViewerDetails *details;
318 details = g_new0 (NautilusNotesViewerDetails, 1);
319 sidebar->details = details;
321 details->uri = g_strdup ("");
323 image_path = nautilus_pixmap_file ("note-indicator.png");
324 if (image_path) {
325 details->icon = gdk_pixbuf_new_from_file (image_path, NULL);
326 g_free (image_path);
329 /* create the text container */
330 details->text_buffer = gtk_text_buffer_new (NULL);
331 details->note_text_field = gtk_text_view_new_with_buffer (details->text_buffer);
333 gtk_text_view_set_editable (GTK_TEXT_VIEW (details->note_text_field), TRUE);
334 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (details->note_text_field),
335 GTK_WRAP_WORD);
336 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sidebar),
337 GTK_POLICY_NEVER,
338 GTK_POLICY_AUTOMATIC);
339 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sidebar),
340 GTK_SHADOW_IN);
341 gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (sidebar), NULL);
342 gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (sidebar), NULL);
343 gtk_container_add (GTK_CONTAINER (sidebar), details->note_text_field);
345 g_signal_connect (details->note_text_field, "focus_out_event",
346 G_CALLBACK (on_text_field_focus_out_event), sidebar);
347 g_signal_connect (details->text_buffer, "changed",
348 G_CALLBACK (on_changed), sidebar);
350 gtk_widget_show_all (GTK_WIDGET (sidebar));
354 static void
355 nautilus_notes_viewer_finalize (GObject *object)
357 NautilusNotesViewer *sidebar;
359 sidebar = NAUTILUS_NOTES_VIEWER (object);
361 done_with_file (sidebar);
362 if (sidebar->details->icon != NULL) {
363 g_object_unref (sidebar->details->icon);
365 g_free (sidebar->details->uri);
366 g_free (sidebar->details->previous_saved_text);
367 g_free (sidebar->details);
369 G_OBJECT_CLASS (nautilus_notes_viewer_parent_class)->finalize (object);
373 static void
374 nautilus_notes_viewer_class_init (NautilusNotesViewerClass *class)
376 G_OBJECT_CLASS (class)->finalize = nautilus_notes_viewer_finalize;
379 static const char *
380 nautilus_notes_viewer_get_sidebar_id (NautilusSidebar *sidebar)
382 return NAUTILUS_NOTES_SIDEBAR_ID;
385 static char *
386 nautilus_notes_viewer_get_tab_label (NautilusSidebar *sidebar)
388 return g_strdup (_("Notes"));
391 static char *
392 nautilus_notes_viewer_get_tab_tooltip (NautilusSidebar *sidebar)
394 return g_strdup (_("Show Notes"));
397 static GdkPixbuf *
398 nautilus_notes_viewer_get_tab_icon (NautilusSidebar *sidebar)
400 NautilusNotesViewer *notes;
402 notes = NAUTILUS_NOTES_VIEWER (sidebar);
404 if (notes->details->previous_saved_text != NULL &&
405 notes->details->previous_saved_text[0] != '\0') {
406 return g_object_ref (notes->details->icon);
409 return NULL;
412 static void
413 nautilus_notes_viewer_is_visible_changed (NautilusSidebar *sidebar,
414 gboolean is_visible)
416 /* Do nothing */
419 static void
420 nautilus_notes_viewer_sidebar_iface_init (NautilusSidebarIface *iface)
422 iface->get_sidebar_id = nautilus_notes_viewer_get_sidebar_id;
423 iface->get_tab_label = nautilus_notes_viewer_get_tab_label;
424 iface->get_tab_tooltip = nautilus_notes_viewer_get_tab_tooltip;
425 iface->get_tab_icon = nautilus_notes_viewer_get_tab_icon;
426 iface->is_visible_changed = nautilus_notes_viewer_is_visible_changed;
429 static void
430 nautilus_notes_viewer_set_parent_window (NautilusNotesViewer *sidebar,
431 NautilusWindowInfo *window)
433 g_signal_connect_object (window, "loading_uri",
434 G_CALLBACK (loading_uri_callback), sidebar, 0);
436 g_free (sidebar->details->uri);
437 sidebar->details->uri = nautilus_window_info_get_current_location (window);
438 notes_load_metainfo (sidebar);
440 nautilus_clipboard_set_up_text_view
441 (GTK_TEXT_VIEW (sidebar->details->note_text_field),
442 nautilus_window_info_get_ui_manager (window));
445 static NautilusSidebar *
446 nautilus_notes_viewer_create_sidebar (NautilusSidebarProvider *provider,
447 NautilusWindowInfo *window)
449 NautilusNotesViewer *sidebar;
451 sidebar = g_object_new (nautilus_notes_viewer_get_type (), NULL);
452 nautilus_notes_viewer_set_parent_window (sidebar, window);
453 g_object_ref (sidebar);
454 gtk_object_sink (GTK_OBJECT (sidebar));
456 return NAUTILUS_SIDEBAR (sidebar);
459 /* nautilus_property_page_provider_get_pages
461 * This function is called by Nautilus when it wants property page
462 * items from the extension.
464 * This function is called in the main thread before a property page
465 * is shown, so it should return quickly.
467 * The function should return a GList of allocated NautilusPropertyPage
468 * items.
470 static GList *
471 get_property_pages (NautilusPropertyPageProvider *provider,
472 GList *files)
474 GList *pages;
475 NautilusPropertyPage *page;
476 NautilusFileInfo *file;
477 char *uri;
478 NautilusNotesViewer *viewer;
481 /* Only show the property page if 1 file is selected */
482 if (!files || files->next != NULL) {
483 return NULL;
486 pages = NULL;
488 file = NAUTILUS_FILE_INFO (files->data);
489 uri = nautilus_file_info_get_uri (file);
491 viewer = g_object_new (nautilus_notes_viewer_get_type (), NULL);
492 g_free (viewer->details->uri);
493 viewer->details->uri = uri;
494 notes_load_metainfo (viewer);
496 page = nautilus_property_page_new
497 ("NautilusNotesViewer::property_page",
498 gtk_label_new (_("Notes")),
499 GTK_WIDGET (viewer));
500 pages = g_list_append (pages, page);
502 return pages;
505 static void
506 property_page_provider_iface_init (NautilusPropertyPageProviderIface *iface)
508 iface->get_pages = get_property_pages;
511 static void
512 sidebar_provider_iface_init (NautilusSidebarProviderIface *iface)
514 iface->create = nautilus_notes_viewer_create_sidebar;
517 static void
518 nautilus_notes_viewer_provider_init (NautilusNotesViewerProvider *sidebar)
522 static void
523 nautilus_notes_viewer_provider_class_init (NautilusNotesViewerProviderClass *class)
527 void
528 nautilus_notes_viewer_register (void)
530 nautilus_module_add_type (nautilus_notes_viewer_provider_get_type ());