summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Hertzfeld <andy@src.gnome.org>2001-04-11 20:45:59 +0000
committerAndy Hertzfeld <andy@src.gnome.org>2001-04-11 20:45:59 +0000
commite1203c8b35f46b38d8c80d44897e8f7430664532 (patch)
treea8eea94247939e521ccb95aecd32b6d20a998b81
parent79995660bd4e6cb6f813f3598bfbeeb1f6ae63fa (diff)
downloadnautilus-e1203c8b35f46b38d8c80d44897e8f7430664532.tar.gz
created the post-1_0_11 branch by merging with head
created the post-1_0_11 branch by merging with head
-rw-r--r--ChangeLog448
-rw-r--r--Makefile.am1
-rw-r--r--components/Makefile.am1
-rw-r--r--components/rss-control/.cvsignore5
-rw-r--r--components/rss-control/Makefile.am49
-rw-r--r--components/rss-control/main.c120
-rw-r--r--components/rss-control/nautilus-rss-control.c872
-rw-r--r--components/rss-control/nautilus-rss-control.h54
-rw-r--r--components/rss-control/nautilus-rss-control.oafinfo20
-rw-r--r--components/services/nautilus-dependent-shared/shared-service-widgets.c212
-rw-r--r--components/throbber/nautilus-throbber.c17
-rw-r--r--components/vcard/.cvsignore5
-rw-r--r--components/vcard/Makefile.am51
-rw-r--r--components/vcard/main.c120
-rw-r--r--components/vcard/nautilus-vcard.c577
-rw-r--r--components/vcard/nautilus-vcard.h54
-rw-r--r--components/vcard/nautilus-vcard.oaf20
-rw-r--r--components/vcard/vcard.c276
-rw-r--r--components/vcard/vcard.h15
-rw-r--r--configure.in2
-rw-r--r--icons/Makefile.am2
-rw-r--r--icons/emblem-note.pngbin0 -> 826 bytes
-rw-r--r--libnautilus-extensions/Makefile.am5
-rw-r--r--libnautilus-extensions/nautilus-annotation.c1312
-rw-r--r--libnautilus-extensions/nautilus-annotation.h45
-rw-r--r--libnautilus-extensions/nautilus-canvas-note-item.c973
-rw-r--r--libnautilus-extensions/nautilus-canvas-note-item.h90
-rw-r--r--libnautilus-extensions/nautilus-file-utilities.c2
-rw-r--r--libnautilus-extensions/nautilus-file.c5
-rw-r--r--libnautilus-extensions/nautilus-file.h1
-rw-r--r--libnautilus-extensions/nautilus-global-preferences.c43
-rw-r--r--libnautilus-extensions/nautilus-global-preferences.h4
-rw-r--r--libnautilus-extensions/nautilus-icon-canvas-item.c467
-rw-r--r--libnautilus-extensions/nautilus-icon-canvas-item.h17
-rw-r--r--libnautilus-extensions/nautilus-icon-container.c133
-rw-r--r--libnautilus-extensions/nautilus-icon-container.h7
-rw-r--r--libnautilus-extensions/nautilus-icon-dnd.c5
-rw-r--r--libnautilus-extensions/nautilus-icon-private.h4
-rw-r--r--libnautilus-extensions/nautilus-link.c29
-rw-r--r--libnautilus-extensions/nautilus-link.h11
-rw-r--r--libnautilus-extensions/nautilus-metadata.h6
-rw-r--r--libnautilus-private/Makefile.am5
-rw-r--r--libnautilus-private/nautilus-annotation.c1312
-rw-r--r--libnautilus-private/nautilus-annotation.h45
-rw-r--r--libnautilus-private/nautilus-canvas-note-item.c973
-rw-r--r--libnautilus-private/nautilus-canvas-note-item.h90
-rw-r--r--libnautilus-private/nautilus-file-utilities.c2
-rw-r--r--libnautilus-private/nautilus-file.c5
-rw-r--r--libnautilus-private/nautilus-file.h1
-rw-r--r--libnautilus-private/nautilus-global-preferences.c43
-rw-r--r--libnautilus-private/nautilus-global-preferences.h4
-rw-r--r--libnautilus-private/nautilus-icon-canvas-item.c467
-rw-r--r--libnautilus-private/nautilus-icon-canvas-item.h17
-rw-r--r--libnautilus-private/nautilus-icon-container.c133
-rw-r--r--libnautilus-private/nautilus-icon-container.h7
-rw-r--r--libnautilus-private/nautilus-icon-dnd.c5
-rw-r--r--libnautilus-private/nautilus-icon-private.h4
-rw-r--r--libnautilus-private/nautilus-link.c29
-rw-r--r--libnautilus-private/nautilus-link.h11
-rw-r--r--libnautilus-private/nautilus-metadata.h6
-rwxr-xr-xnautilus-clean.sh1
-rw-r--r--src/file-manager/Makefile.am2
-rw-r--r--src/file-manager/fm-annotation-window.c400
-rw-r--r--src/file-manager/fm-annotation-window.h65
-rw-r--r--src/file-manager/fm-icon-view.c133
-rw-r--r--src/file-manager/nautilus-icon-view-ui.xml4
-rw-r--r--src/nautilus-property-browser.c3
67 files changed, 9486 insertions, 361 deletions
diff --git a/ChangeLog b/ChangeLog
index 759d8688b..74f79c33b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,168 @@
+2001-04-11 Andy Hertzfeld <andy@eazel.com>
+
+ created the post-1_0_11 branch by merging with head
+
+ * Makefile.am:
+ * components/Makefile.am:
+ * components/rss-control/.cvsignore:
+ * components/rss-control/Makefile.am:
+ * components/rss-control/main.c: (rss_control_object_destroyed),
+ (rss_control_make_object), (main):
+ * components/rss-control/nautilus-rss-control.c:
+ (nautilus_rss_control_initialize_class), (get_bonobo_properties),
+ (set_bonobo_properties), (nautilus_rss_control_initialize),
+ (free_rss_data_item), (nautilus_rss_control_clear_items),
+ (nautilus_rss_control_destroy), (nautilus_rss_control_get_control),
+ (nautilus_rss_control_set_title), (rss_logo_callback),
+ (extract_items), (rss_read_done_callback), (load_rss_file),
+ (nautilus_rss_control_set_uri), (check_for_update),
+ (rss_control_pixbuf_composite), (draw_rss_logo_image),
+ (draw_rss_title), (draw_blue_line), (draw_rss_items),
+ (nautilus_rss_control_draw), (nautilus_rss_control_expose),
+ (nautilus_rss_control_set_prelight_index),
+ (nautilus_rss_control_motion_event),
+ (nautilus_rss_control_leave_event),
+ (nautilus_rss_control_size_request),
+ (nautilus_rss_control_button_press_event):
+ * components/rss-control/nautilus-rss-control.h:
+ * components/rss-control/nautilus-rss-control.oafinfo:
+ * components/throbber/nautilus-throbber.c: (get_bonobo_properties),
+ (set_bonobo_properties), (nautilus_throbber_initialize):
+ * components/vcard/.cvsignore:
+ * components/vcard/Makefile.am:
+ * components/vcard/main.c: (vcard_object_destroyed),
+ (vcard_make_object), (main):
+ * components/vcard/nautilus-vcard.c:
+ (nautilus_vcard_initialize_class), (get_bonobo_properties),
+ (set_bonobo_properties), (nautilus_vcard_initialize),
+ (nautilus_vcard_destroy), (nautilus_vcard_get_control),
+ (vcard_logo_callback), (vcard_read_done_callback), (load_vcard),
+ (nautilus_vcard_set_uri), (vcard_pixbuf_composite),
+ (draw_vcard_logo_image), (draw_vcard_name_and_title),
+ (draw_vcard_addresses), (nautilus_vcard_draw),
+ (nautilus_vcard_expose), (nautilus_vcard_motion_event),
+ (nautilus_vcard_size_request), (nautilus_vcard_leave_event),
+ (nautilus_vcard_button_press_event):
+ * components/vcard/nautilus-vcard.h:
+ * components/vcard/nautilus-vcard.oaf:
+ * components/vcard/vcard.c: (vcard_full_name), (vcard_title),
+ (vcard_logo), (vcard_postal), (vcard_streetaddress),
+ (vcard_city_state_zip), (vcard_company), (vcard_email),
+ (vcard_fax), (vcard_web), (vcard_address_list):
+ * components/vcard/vcard.h:
+ * configure.in:
+ * icons/Makefile.am:
+ * icons/emblem-note.png:
+ * libnautilus-extensions/Makefile.am:
+ * libnautilus-extensions/nautilus-annotation.c: (_byte_reverse),
+ (md5_init), (md5_update), (md5_final), (md5_transform),
+ (digest_file_close_callback), (digest_file_completed),
+ (digest_file_failed), (calculate_checksum_callback),
+ (read_file_open_callback), (calculate_file_digest),
+ (process_digest_requests), (queue_file_digest_request),
+ (get_file_from_digest), (get_annotation_path),
+ (look_up_local_annotation), (has_local_annotation),
+ (save_local_annotations), (add_annotations_to_file),
+ (remember_file), (forget_file), (got_annotations_callback),
+ (fetch_annotations_from_server), (get_annotation_from_server),
+ (got_file_digest), (annotation_is_stale),
+ (nautilus_annotation_get_annotation),
+ (nautilus_annotation_get_display_text),
+ (nautilus_annotation_get_annotation_for_display),
+ (nautilus_annotation_has_annotation), (http_post_simple),
+ (count_annotations), (nautilus_annotation_send_to_server),
+ (nautilus_annotation_add_annotation),
+ (nautilus_annotation_remove_annotation):
+ * libnautilus-extensions/nautilus-annotation.h:
+ * libnautilus-extensions/nautilus-canvas-note-item.c:
+ (nautilus_canvas_note_item_get_type),
+ (nautilus_canvas_note_item_class_init),
+ (nautilus_canvas_note_item_init),
+ (nautilus_canvas_note_item_destroy), (get_bounds),
+ (set_gc_foreground), (set_stipple), (set_outline_gc_width),
+ (update_item_bounding_box),
+ (nautilus_canvas_note_item_set_note_text),
+ (nautilus_canvas_note_item_set_arg), (get_color_arg),
+ (nautilus_canvas_note_item_get_arg),
+ (nautilus_canvas_note_item_realize),
+ (nautilus_canvas_note_item_unrealize),
+ (nautilus_canvas_note_item_translate),
+ (nautilus_canvas_note_item_bounds), (draw_item_aa_text),
+ (nautilus_canvas_note_item_render),
+ (nautilus_canvas_note_item_draw),
+ (nautilus_canvas_note_item_point),
+ (nautilus_canvas_note_item_update):
+ * libnautilus-extensions/nautilus-canvas-note-item.h:
+ * libnautilus-extensions/nautilus-file-utilities.c:
+ (nautilus_get_user_main_directory):
+ * libnautilus-extensions/nautilus-file.c:
+ (prepend_automatic_emblem_names):
+ * libnautilus-extensions/nautilus-file.h:
+ * libnautilus-extensions/nautilus-global-preferences.c:
+ (global_preferences_install_defaults),
+ (global_preferences_create_dialog):
+ * libnautilus-extensions/nautilus-global-preferences.h:
+ * libnautilus-extensions/nautilus-icon-canvas-item.c:
+ (nautilus_icon_canvas_item_initialize),
+ (nautilus_icon_canvas_item_destroy),
+ (nautilus_icon_canvas_item_get_icon_width),
+ (nautilus_icon_canvas_item_get_icon_height),
+ (nautilus_icon_canvas_item_set_arg), (do_control_destroy),
+ (nautilus_icon_canvas_item_get_arg),
+ (nautilus_icon_canvas_item_get_image), (recompute_bounding_box),
+ (nautilus_icon_canvas_item_update_bounds),
+ (draw_or_measure_label_text), (emblem_layout_next),
+ (nautilus_icon_canvas_item_draw), (draw_or_measure_label_text_aa),
+ (nautilus_icon_canvas_item_render), (create_annotation),
+ (remove_annotation), (create_annotation_timeout_callback),
+ (nautilus_icon_canvas_item_set_note_state),
+ (nautilus_icon_canvas_item_event),
+ (nautilus_icon_canvas_item_hit_test_full),
+ (nautilus_icon_canvas_item_point),
+ (nautilus_icon_canvas_item_bounds),
+ (nautilus_icon_canvas_item_get_icon_rectangle),
+ (get_emblem_rectangle), (get_icon_canvas_rectangle),
+ (nautilus_icon_canvas_item_hit_test_rectangle),
+ (nautilus_icon_canvas_item_set_smooth_font),
+ (nautilus_icon_canvas_item_get_control),
+ (nautilus_icon_canvas_item_set_control):
+ * libnautilus-extensions/nautilus-icon-canvas-item.h:
+ * libnautilus-extensions/nautilus-icon-container.c: (destroy),
+ (hit_test_item), (nautilus_icon_container_did_not_drag),
+ (key_press_event), (nautilus_icon_container_initialize_class),
+ (nautilus_icon_container_initialize), (handle_icon_button_press),
+ (activate_selected_items), (nautilus_icon_container_update_icon),
+ (nautilus_icon_container_annotation_changed),
+ (nautilus_icon_container_get_note_text):
+ * libnautilus-extensions/nautilus-icon-container.h:
+ * libnautilus-extensions/nautilus-icon-dnd.c:
+ (nautilus_icon_dnd_begin_drag):
+ * libnautilus-extensions/nautilus-icon-private.h:
+ * libnautilus-extensions/nautilus-link.c:
+ (nautilus_link_local_get_component_info):
+ * libnautilus-extensions/nautilus-link.h:
+ * libnautilus-extensions/nautilus-metadata.h:
+ * nautilus-clean.sh:
+ * src/file-manager/Makefile.am:
+ * src/file-manager/fm-annotation-window.c:
+ (fm_annotation_window_initialize_class),
+ (fm_annotation_window_initialize), (real_destroy),
+ (get_pixbuf_for_annotation_window),
+ (update_annotation_window_icon), (create_image_widget_for_file),
+ (update_annotation_window_title), (set_access_mode),
+ (add_access_menu_item), (create_options_table),
+ (annotation_clicked_callback), (create_annotation_window),
+ (fm_annotation_window_present):
+ * src/file-manager/fm-annotation-window.h:
+ * src/file-manager/fm-icon-view.c: (annotate_callback),
+ (fm_icon_view_merge_menus), (fm_icon_view_update_menus),
+ (get_keyword_by_index), (icon_container_activate_callback),
+ (get_icon_control_callback), (get_icon_text_callback),
+ (get_icon_annotation_callback), (create_icon_container):
+ * src/file-manager/nautilus-icon-view-ui.xml:
+ * src/nautilus-property-browser.c:
+ (nautilus_property_browser_update_contents):
+
2001-04-11 Ramiro Estrugo <ramiro@eazel.com>
* nautilus.spec.in:
@@ -876,6 +1041,289 @@
a nicely formatted text string, for everything else just
ellipsize the result.
+2001-04-11 Andy Hertzfeld <andy@eazel.com>
+
+ * libnautilus-extensions/nautilus-canvas-note-item.c:
+ (nautilus_canvas_note_item_set_note_text):
+ improved sizing of the note item in the aa mode.
+
+ * libnautilus-extensions/nautilus-icon-container.c:
+ (hit_test_item):
+ fixed bug by mapping from window to world coordinates
+
+2001-04-10 Andy Hertzfeld <andy@eazel.com>
+
+ added a way for custom actions to take place when the user clicks
+ on an emblem, and used that to bring up the annotation window
+ when the "note" emblem is clicked on.
+
+ * libnautilus-extensions/nautilus-icon-canvas-item.c,h:
+ (nautilus_icon_canvas_item_event),
+ (nautilus_icon_canvas_item_hit_test_full),
+ (nautilus_icon_canvas_item_point),
+ (nautilus_icon_canvas_item_hit_test_rectangle):
+ renamed the "hit_test" routine to nautilus_icon_canvas_hit_test_full,
+ and made it public.
+
+ * libnautilus-extensions/nautilus-icon-container.c:
+ (hit_test_item), (nautilus_icon_container_did_not_drag),
+ (key_press_event), (nautilus_icon_container_initialize_class),
+ (handle_icon_button_press), (activate_selected_items):
+ added an emblem index parameter to the icon canvas's activate
+ signal, and set it up properly by calling the item hit test
+ routine to determine the emblem index.
+
+ * src/file-manager/fm-icon-view.c: (get_keyword_by_index),
+ (icon_container_activate_callback), (get_icon_annotation_callback):
+ changed the activate callback to bring up the annotation window
+ if the annotation emblem is clicked.
+
+2001-04-10 Andy Hertzfeld <andy@eazel.com>
+
+ * libnautilus-extensions/nautilus-canvas-note-item.c:
+ (nautilus_canvas_note_item_set_note_text),
+ (nautilus_canvas_note_item_update):
+ tweaked note sizing
+
+ * libnautilus-extensions/nautilus-icon-canvas-item.c:
+ (create_annotation), (get_emblem_rectangle):
+ made the annotation be positioned properly by using world coordinates
+ instead of canvas coordinates
+
+2001-04-09 Andy Hertzfeld <andy@eazel.com>
+
+ removed some unneeded generality from the note item
+
+ * libnautilus-extensions/nautilus-canvas-note-item.c:
+ (nautilus_canvas_note_item_set_arg),
+ (nautilus_canvas_note_item_draw),
+ (nautilus_canvas_note_item_point):
+ * libnautilus-extensions/nautilus-canvas-note-item.h:
+
+2001-04-09 Andy Hertzfeld <andy@eazel.com>
+
+ implemented arrow pointer for annotations, to make it more
+ clear what they're annotating.
+
+ * libnautilus-extensions/nautilus-annotation.c:
+ (nautilus_annotation_send_to_server):
+ removed debugging message
+
+ * libnautilus-extensions/nautilus-canvas-note-item.c:
+ (nautilus_canvas_note_item_set_note_text), (draw_item_aa_text),
+ (nautilus_canvas_note_item_update):
+ draw the arrow pointer
+
+ * libnautilus-extensions/nautilus-icon-canvas-item.c:
+ (create_annotation), (get_emblem_rectangle):
+ position the annotation so the arrow points at the right place.
+
+2001-04-09 Andy Hertzfeld <andy@eazel.com>
+
+ * libnautilus-extensions/Makefile.am:
+ linked with ammonite so we can get the user name
+ * libnautilus-extensions/nautilus-annotation.c:
+ (count_annotations), (nautilus_annotation_send_to_server),
+ (nautilus_annotation_add_annotation):
+ made it maintain the annotation count properly when a new annotation
+ is added; also, use the real username fetched from ammonite.
+
+2001-04-08 Andy Hertzfeld <andy@eazel.com>
+
+ * libnautilus-extensions/nautilus-annotation.c:
+ (annotation_is_stale), (nautilus_annotation_get_annotation),
+ (nautilus_annotation_has_annotation),
+ (nautilus_annotation_send_to_server),
+ (nautilus_annotation_add_annotation):
+ made annotation upload work; added "date" property to distinguish
+ multiple annotations/file; refetch annotations periodically
+
+2001-04-06 Andy Hertzfeld <andy@eazel.com>
+
+ * libnautilus-extensions/nautilus-annotation.c:
+ (nautilus_annotation_has_annotation), (http_post_simple),
+ (nautilus_annotation_send_to_server),
+ (nautilus_annotation_add_annotation):
+ implemented client side of annotation upload; still need to do
+ server side before it can be tested.
+
+2001-04-06 Andy Hertzfeld <andy@eazel.com>
+
+ started implementation of optionally sending annotations to the
+ server. Also, put up error when the user tries to annotate
+ a directory
+
+ * libnautilus-extensions/nautilus-annotation.c:
+ (nautilus_annotation_send_to_server),
+ (nautilus_annotation_add_annotation):
+
+ * src/file-manager/fm-annotation-window.c:
+ (fm_annotation_window_initialize), (real_destroy),
+ (set_access_mode), (add_access_menu_item), (create_options_table),
+ (annotation_clicked_callback), (create_annotation_window),
+ (fm_annotation_window_present):
+
+2001-04-05 Andy Hertzfeld <andy@eazel.com>
+
+ merged with HEAD to create the post-1_0_10 branch
+
+ * Makefile.am:
+ * components/Makefile.am:
+ * components/rss-control/.cvsignore:
+ * components/rss-control/Makefile.am:
+ * components/rss-control/main.c: (rss_control_object_destroyed),
+ (rss_control_make_object), (main):
+ * components/rss-control/nautilus-rss-control.c:
+ (nautilus_rss_control_initialize_class), (get_bonobo_properties),
+ (set_bonobo_properties), (nautilus_rss_control_initialize),
+ (free_rss_data_item), (nautilus_rss_control_clear_items),
+ (nautilus_rss_control_destroy), (nautilus_rss_control_get_control),
+ (nautilus_rss_control_set_title), (rss_logo_callback),
+ (extract_items), (rss_read_done_callback), (load_rss_file),
+ (nautilus_rss_control_set_uri), (check_for_update),
+ (rss_control_pixbuf_composite), (draw_rss_logo_image),
+ (draw_rss_title), (draw_blue_line), (draw_rss_items),
+ (nautilus_rss_control_draw), (nautilus_rss_control_expose),
+ (nautilus_rss_control_set_prelight_index),
+ (nautilus_rss_control_motion_event),
+ (nautilus_rss_control_leave_event),
+ (nautilus_rss_control_size_request),
+ (nautilus_rss_control_button_press_event):
+ * components/rss-control/nautilus-rss-control.h:
+ * components/rss-control/nautilus-rss-control.oafinfo:
+ * components/throbber/nautilus-throbber.c: (get_bonobo_properties),
+ (set_bonobo_properties), (nautilus_throbber_initialize):
+ * components/vcard/.cvsignore:
+ * components/vcard/Makefile.am:
+ * components/vcard/main.c: (vcard_object_destroyed),
+ (vcard_make_object), (main):
+ * components/vcard/nautilus-vcard.c:
+ (nautilus_vcard_initialize_class), (get_bonobo_properties),
+ (set_bonobo_properties), (nautilus_vcard_initialize),
+ (nautilus_vcard_destroy), (nautilus_vcard_get_control),
+ (vcard_logo_callback), (vcard_read_done_callback), (load_vcard),
+ (nautilus_vcard_set_uri), (vcard_pixbuf_composite),
+ (draw_vcard_logo_image), (draw_vcard_name_and_title),
+ (draw_vcard_addresses), (nautilus_vcard_draw),
+ (nautilus_vcard_expose), (nautilus_vcard_motion_event),
+ (nautilus_vcard_size_request), (nautilus_vcard_leave_event),
+ (nautilus_vcard_button_press_event):
+ * components/vcard/nautilus-vcard.h:
+ * components/vcard/nautilus-vcard.oaf:
+ * components/vcard/vcard.c: (vcard_full_name), (vcard_title),
+ (vcard_logo), (vcard_postal), (vcard_streetaddress),
+ (vcard_city_state_zip), (vcard_company), (vcard_email),
+ (vcard_fax), (vcard_web), (vcard_address_list):
+ * components/vcard/vcard.h:
+ * configure.in:
+ * icons/Makefile.am:
+ * icons/emblem-note.png:
+ * libnautilus-extensions/Makefile.am:
+ * libnautilus-extensions/nautilus-annotation.c: (_byte_reverse),
+ (md5_init), (md5_update), (md5_final), (md5_transform),
+ (digest_file_close_callback), (digest_file_completed),
+ (digest_file_failed), (calculate_checksum_callback),
+ (read_file_open_callback), (calculate_file_digest),
+ (process_digest_requests), (queue_file_digest_request),
+ (get_file_from_digest), (get_annotation_path),
+ (look_up_local_annotation), (has_local_annotation),
+ (save_local_annotations), (add_annotations_to_file),
+ (remember_file), (forget_file), (got_annotations_callback),
+ (fetch_annotations_from_server), (get_annotation_from_server),
+ (got_file_digest), (nautilus_annotation_get_annotation),
+ (nautilus_annotation_get_display_text),
+ (nautilus_annotation_get_annotation_for_display),
+ (nautilus_annotation_has_annotation),
+ (nautilus_annotation_add_annotation),
+ (nautilus_annotation_remove_annotation):
+ * libnautilus-extensions/nautilus-annotation.h:
+ * libnautilus-extensions/nautilus-canvas-note-item.c:
+ (nautilus_canvas_note_item_get_type),
+ (nautilus_canvas_note_item_class_init),
+ (nautilus_canvas_note_item_init),
+ (nautilus_canvas_note_item_destroy), (get_bounds),
+ (set_gc_foreground), (set_stipple), (set_outline_gc_width),
+ (nautilus_canvas_note_item_set_fill),
+ (nautilus_canvas_note_item_set_outline),
+ (update_item_bounding_box),
+ (nautilus_canvas_note_item_set_note_text),
+ (nautilus_canvas_note_item_set_arg), (get_color_arg),
+ (nautilus_canvas_note_item_get_arg),
+ (nautilus_canvas_note_item_realize),
+ (nautilus_canvas_note_item_unrealize),
+ (nautilus_canvas_note_item_translate),
+ (nautilus_canvas_note_item_bounds), (draw_item_aa_text),
+ (nautilus_canvas_note_item_render),
+ (nautilus_canvas_note_item_draw),
+ (nautilus_canvas_note_item_point),
+ (nautilus_canvas_note_item_update):
+ * libnautilus-extensions/nautilus-canvas-note-item.h:
+ * libnautilus-extensions/nautilus-file-utilities.c:
+ (nautilus_get_user_main_directory):
+ * libnautilus-extensions/nautilus-file.c:
+ (prepend_automatic_emblem_names):
+ * libnautilus-extensions/nautilus-file.h:
+ * libnautilus-extensions/nautilus-global-preferences.c:
+ (global_preferences_install_defaults),
+ (global_preferences_create_dialog):
+ * libnautilus-extensions/nautilus-global-preferences.h:
+ * libnautilus-extensions/nautilus-icon-canvas-item.c:
+ (nautilus_icon_canvas_item_initialize),
+ (nautilus_icon_canvas_item_destroy),
+ (nautilus_icon_canvas_item_get_icon_width),
+ (nautilus_icon_canvas_item_get_icon_height),
+ (nautilus_icon_canvas_item_set_arg), (do_control_destroy),
+ (nautilus_icon_canvas_item_get_arg),
+ (nautilus_icon_canvas_item_get_image), (recompute_bounding_box),
+ (nautilus_icon_canvas_item_update_bounds),
+ (draw_or_measure_label_text), (emblem_layout_next),
+ (nautilus_icon_canvas_item_draw), (draw_or_measure_label_text_aa),
+ (nautilus_icon_canvas_item_render), (create_annotation),
+ (remove_annotation), (create_annotation_timeout_callback),
+ (nautilus_icon_canvas_item_set_note_state),
+ (nautilus_icon_canvas_item_event), (hit_test),
+ (nautilus_icon_canvas_item_point),
+ (nautilus_icon_canvas_item_bounds),
+ (nautilus_icon_canvas_item_get_icon_rectangle),
+ (get_icon_canvas_rectangle),
+ (nautilus_icon_canvas_item_hit_test_rectangle),
+ (nautilus_icon_canvas_item_set_smooth_font),
+ (nautilus_icon_canvas_item_get_control),
+ (nautilus_icon_canvas_item_set_control):
+ * libnautilus-extensions/nautilus-icon-canvas-item.h:
+ * libnautilus-extensions/nautilus-icon-container.c: (destroy),
+ (nautilus_icon_container_initialize_class),
+ (nautilus_icon_container_initialize),
+ (nautilus_icon_container_update_icon),
+ (nautilus_icon_container_annotation_changed),
+ (nautilus_icon_container_get_note_text):
+ * libnautilus-extensions/nautilus-icon-container.h:
+ * libnautilus-extensions/nautilus-icon-dnd.c:
+ (nautilus_icon_dnd_begin_drag):
+ * libnautilus-extensions/nautilus-icon-private.h:
+ * libnautilus-extensions/nautilus-link.c:
+ (nautilus_link_local_get_component_info):
+ * libnautilus-extensions/nautilus-link.h:
+ * libnautilus-extensions/nautilus-metadata.h:
+ * nautilus-clean.sh:
+ * src/file-manager/Makefile.am:
+ * src/file-manager/fm-annotation-window.c:
+ (fm_annotation_window_initialize_class),
+ (fm_annotation_window_initialize), (real_destroy),
+ (get_pixbuf_for_annotation_window),
+ (update_annotation_window_icon), (create_image_widget_for_file),
+ (update_annotation_window_title), (create_options_table),
+ (annotation_clicked_callback), (create_annotation_window),
+ (fm_annotation_window_present):
+ * src/file-manager/fm-annotation-window.h:
+ * src/file-manager/fm-icon-view.c: (annotate_callback),
+ (fm_icon_view_merge_menus), (fm_icon_view_update_menus),
+ (get_icon_control_callback), (get_icon_text_callback),
+ (get_icon_annotation_callback), (create_icon_container):
+ * src/file-manager/nautilus-icon-view-ui.xml:
+ * src/nautilus-property-browser.c:
+ (nautilus_property_browser_update_contents):
+
2001-04-05 John Sullivan <sullivan@eazel.com>
Fixed bug 7986 ("Paste Files" disabled in trash:)
diff --git a/Makefile.am b/Makefile.am
index f859a775f..062862726 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -12,7 +12,6 @@ endif
SUBDIRS = \
data \
icons \
- intl \
cut-n-paste-code \
libnautilus \
libnautilus-adapter \
diff --git a/components/Makefile.am b/components/Makefile.am
index 46761130d..559ddec69 100644
--- a/components/Makefile.am
+++ b/components/Makefile.am
@@ -13,6 +13,7 @@ SUBDIRS = \
loser \
music \
notes \
+ rss-control \
sample \
text \
throbber \
diff --git a/components/rss-control/.cvsignore b/components/rss-control/.cvsignore
new file mode 100644
index 000000000..9eeb873c6
--- /dev/null
+++ b/components/rss-control/.cvsignore
@@ -0,0 +1,5 @@
+.deps
+.libs
+Makefile
+Makefile.in
+nautilus-rss-control
diff --git a/components/rss-control/Makefile.am b/components/rss-control/Makefile.am
new file mode 100644
index 000000000..db0531dd4
--- /dev/null
+++ b/components/rss-control/Makefile.am
@@ -0,0 +1,49 @@
+NULL =
+
+SUBDIRS =
+
+INCLUDES = \
+ -DPREFIX=\"$(prefix)\" \
+ -DG_LOG_DOMAIN=\"Nautilus-RSS-Control\" \
+ -DDATADIR=\""$(datadir)"\" \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ -I$(top_builddir)/libnautilus \
+ -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
+ $(EEL_INCLUDEDIR) \
+ $(LIBRSVG_INCLUDEDIR) \
+ $(GNOMEUI_CFLAGS) \
+ $(GCONF_CFLAGS) \
+ $(OAF_CFLAGS) \
+ $(BONOBO_CFLAGS) \
+ $(VFS_CFLAGS)
+
+oafdir = $(datadir)/oaf
+oaf_DATA = \
+ nautilus-rss-control.oafinfo
+
+
+bin_PROGRAMS = \
+ nautilus-rss-control
+
+nautilus_rss_control_SOURCES = \
+ nautilus-rss-control.c \
+ nautilus-rss-control.h \
+ main.c
+
+nautilus_rss_control_LDADD = \
+ $(top_builddir)/libnautilus/libnautilus.la \
+ $(top_builddir)/libnautilus-extensions/libnautilus-extensions.la \
+ $(EEL_LIBS) \
+ $(LIBRSVG_LIBS) \
+ $(BONOBO_LIBS) \
+ $(GNOMEUI_LIBS) \
+ $(GCONF_LIBS) \
+ $(VFS_LIBS) \
+ $(GNORBA_LIBS)
+
+OBJECT_DIRECTORY_LIBS = $(GNOME_LIBS) $(OAF_LIBS)
+
+EXTRA_DIST = \
+ $(oaf_DATA) \
+ $(NULL)
diff --git a/components/rss-control/main.c b/components/rss-control/main.c
new file mode 100644
index 000000000..ada73e46b
--- /dev/null
+++ b/components/rss-control/main.c
@@ -0,0 +1,120 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2000 Eazel, Inc
+ *
+ * This program 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; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Andy Hertzfeld
+ */
+
+/* main.c - main function and object activation function for the rss control component. */
+
+#include <config.h>
+#include "nautilus-rss-control.h"
+
+#include <bonobo.h>
+#include <gnome.h>
+#include <libgnomevfs/gnome-vfs.h>
+#include <eel/eel-debug.h>
+#include <liboaf/liboaf.h>
+
+static int object_count = 0;
+
+static void
+rss_control_object_destroyed(GtkObject *obj)
+{
+ object_count--;
+ if (object_count <= 0) {
+ gtk_main_quit ();
+ }
+}
+
+static BonoboObject *
+rss_control_make_object (BonoboGenericFactory *factory,
+ const char *iid,
+ void *closure)
+{
+ NautilusRSSControl *rss_control;
+ BonoboObject *bonobo_control;
+
+ if (strcmp (iid, "OAFIID:nautilus_rss_control:1230")) {
+ return NULL;
+ }
+
+ rss_control = NAUTILUS_RSS_CONTROL (gtk_object_new (NAUTILUS_TYPE_RSS_CONTROL, NULL));
+
+ object_count++;
+
+ bonobo_control = nautilus_rss_control_get_control (rss_control);
+
+ gtk_signal_connect (GTK_OBJECT (bonobo_control), "destroy", rss_control_object_destroyed, NULL);
+ return bonobo_control;
+}
+
+int
+main (int argc, char *argv[])
+{
+ BonoboGenericFactory *factory;
+ CORBA_ORB orb;
+ char *registration_id;
+
+ /* Make criticals and warnings stop in the debugger if
+ * NAUTILUS_DEBUG is set. Unfortunately, this has to be done
+ * explicitly for each domain.
+ */
+ if (g_getenv ("NAUTILUS_DEBUG") != NULL) {
+ eel_make_warnings_and_criticals_stop_in_debugger
+ (G_LOG_DOMAIN, g_log_domain_glib,
+ "Bonobo",
+ "Gdk",
+ "GnomeUI",
+ "GnomeVFS",
+ "GnomeVFS-CORBA",
+ "GnomeVFS-pthread",
+ "Gtk",
+ "Gdk-Pixbuf",
+ "Nautilus",
+ "Nautilus-Authenticate",
+ "Nautilus-Tree",
+ "ORBit",
+ NULL);
+ }
+
+ gnome_init_with_popt_table("nautilus-rss-control", VERSION,
+ argc, argv,
+ oaf_popt_options, 0, NULL);
+
+ orb = oaf_init (argc, argv);
+
+ bonobo_init (orb, CORBA_OBJECT_NIL, CORBA_OBJECT_NIL);
+
+ /* initialize gnome-vfs, etc */
+ g_thread_init (NULL);
+ gnome_vfs_init ();
+
+ registration_id = oaf_make_registration_id ("OAFIID:nautilus_rss_control_factory:1230", getenv ("DISPLAY"));
+ factory = bonobo_generic_factory_new_multi (registration_id,
+ rss_control_make_object,
+ NULL);
+ g_free (registration_id);
+
+
+ do {
+ bonobo_main ();
+ } while (object_count > 0);
+
+ return 0;
+}
diff --git a/components/rss-control/nautilus-rss-control.c b/components/rss-control/nautilus-rss-control.c
new file mode 100644
index 000000000..4ec56cffa
--- /dev/null
+++ b/components/rss-control/nautilus-rss-control.c
@@ -0,0 +1,872 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+/*
+ * Copyright (C) 2000 Eazel, Inc.
+ *
+ * This library 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; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library 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. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Andy Hertzfeld <andy@eazel.com>
+ *
+ */
+
+/* this is the implementation of the rss control, which fetches an rss file through a uri, and
+ * displays it in the widget.
+ */
+
+#include <config.h>
+#include <time.h>
+#include <gnome.h>
+#include <liboaf/liboaf.h>
+
+#include <bonobo.h>
+
+#include "nautilus-rss-control.h"
+#include <ghttp.h>
+
+#include <gnome-xml/parser.h>
+#include <gnome-xml/xmlmemory.h>
+
+#include <libgnomevfs/gnome-vfs.h>
+
+#include <libnautilus/nautilus-view.h>
+
+#include <libnautilus-extensions/nautilus-file-utilities.h>
+#include <eel/eel-gdk-extensions.h>
+#include <eel/eel-gdk-pixbuf-extensions.h>
+#include <eel/eel-glib-extensions.h>
+#include <eel/eel-gtk-extensions.h>
+#include <eel/eel-gtk-macros.h>
+#include <eel/eel-scalable-font.h>
+#include <eel/eel-string.h>
+#include <eel/eel-xml-extensions.h>
+#include <libnautilus-extensions/nautilus-font-factory.h>
+#include <eel/eel-vfs-extensions.h>
+
+/* private instance variables */
+struct _NautilusRSSControlDetails {
+ char* rss_uri;
+ BonoboObject *control;
+ EelScalableFont *font;
+
+ EelReadFileHandle *load_file_handle;
+ EelPixbufLoadHandle *load_image_handle;
+
+ int items_v_offset;
+ int prelight_index;
+
+ char* title;
+ char* main_uri;
+
+ guint timer_task;
+ time_t last_update;
+ uint update_interval;
+
+ GdkPixbuf *logo;
+ GdkPixbuf *bullet;
+ GList *items;
+};
+
+/* per item structure for rss items */
+typedef struct {
+ char *item_title;
+ char *item_url;
+
+} RSSItemData;
+
+#define RSS_ITEM_HEIGHT 15
+#define MINIMUM_DRAW_SIZE 8
+
+static void nautilus_rss_control_initialize_class (NautilusRSSControlClass *klass);
+static void nautilus_rss_control_initialize (NautilusRSSControl *view);
+static void nautilus_rss_control_destroy (GtkObject *object);
+
+static void nautilus_rss_control_draw (GtkWidget *widget, GdkRectangle *box);
+static int nautilus_rss_control_expose (GtkWidget *widget, GdkEventExpose *event);
+static gboolean nautilus_rss_control_button_press_event (GtkWidget *widget, GdkEventButton *event);
+static gboolean nautilus_rss_control_motion_event (GtkWidget *widget, GdkEventMotion *event);
+static gboolean nautilus_rss_control_leave_event (GtkWidget *widget, GdkEventCrossing *event);
+static void nautilus_rss_control_size_request (GtkWidget *widget, GtkRequisition *request);
+
+static void nautilus_rss_control_set_uri (NautilusRSSControl *rss_control, const char *uri);
+static int check_for_update (gpointer callback_data);
+
+
+EEL_DEFINE_CLASS_BOILERPLATE (NautilusRSSControl,
+ nautilus_rss_control,
+ GTK_TYPE_EVENT_BOX)
+
+
+static void
+nautilus_rss_control_initialize_class (NautilusRSSControlClass *klass)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = GTK_OBJECT_CLASS (klass);
+ widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->destroy = nautilus_rss_control_destroy;
+
+ widget_class->draw = nautilus_rss_control_draw;
+ widget_class->expose_event = nautilus_rss_control_expose;
+ widget_class->button_press_event = nautilus_rss_control_button_press_event;
+ widget_class->motion_notify_event = nautilus_rss_control_motion_event;
+ widget_class->leave_notify_event = nautilus_rss_control_leave_event;
+ widget_class->size_request = nautilus_rss_control_size_request;
+}
+
+/* routines to handle setting and getting the configuration properties of the Bonobo control */
+
+enum {
+ CONFIGURATION
+} MyArgs;
+
+
+static void
+get_bonobo_properties (BonoboPropertyBag *bag,
+ BonoboArg *arg,
+ guint arg_id,
+ CORBA_Environment *ev,
+ gpointer user_data)
+{
+ NautilusRSSControl *rss_control = NAUTILUS_RSS_CONTROL (user_data);
+
+ switch (arg_id) {
+
+ case CONFIGURATION:
+ {
+ BONOBO_ARG_SET_STRING (arg, rss_control->details->rss_uri);
+ break;
+ }
+
+ default:
+ g_warning ("Unhandled arg %d", arg_id);
+ break;
+ }
+}
+
+static void
+set_bonobo_properties (BonoboPropertyBag *bag,
+ const BonoboArg *arg,
+ guint arg_id,
+ CORBA_Environment *ev,
+ gpointer user_data)
+{
+ NautilusRSSControl *rss_control = NAUTILUS_RSS_CONTROL (user_data);
+
+ switch (arg_id) {
+
+ case CONFIGURATION:
+ {
+ char *uri;
+
+ uri = BONOBO_ARG_GET_STRING (arg);
+ nautilus_rss_control_set_uri (rss_control, uri);
+
+ break;
+ }
+
+ default:
+ g_warning ("Unhandled arg %d", arg_id);
+ break;
+ }
+}
+
+/* initialize ourselves by connecting to the location change signal and allocating our subviews */
+static void
+nautilus_rss_control_initialize (NautilusRSSControl *rss_control)
+{
+ char *bullet_path;
+ BonoboPropertyBag *property_bag;
+
+ rss_control->details = g_new0 (NautilusRSSControlDetails, 1);
+
+ /* set up the font */
+ rss_control->details->font = eel_scalable_font_get_default_font ();
+ rss_control->details->prelight_index = -1;
+
+ /* set up the update variables */
+ rss_control->details->last_update = 0;
+ rss_control->details->update_interval = 2 * 60; /* default to once every two minutes */
+
+ rss_control->details->timer_task
+ = gtk_timeout_add (4000, check_for_update, rss_control);
+
+ /* load the bullet used to display the items */
+ bullet_path = nautilus_pixmap_file ("bullet.png");
+ rss_control->details->bullet = gdk_pixbuf_new_from_file (bullet_path);
+ g_free (bullet_path);
+
+ /* receive mouse motion events */
+ gtk_widget_add_events (GTK_WIDGET (rss_control), GDK_POINTER_MOTION_MASK);
+
+ /* make the bonobo control */
+ rss_control->details->control = (BonoboObject*) bonobo_control_new (GTK_WIDGET (rss_control));
+
+ /* attach a property bag with the configure property */
+ property_bag = bonobo_property_bag_new (get_bonobo_properties, set_bonobo_properties, rss_control);
+ bonobo_control_set_properties (BONOBO_CONTROL(rss_control->details->control),property_bag);
+ bonobo_object_unref (BONOBO_OBJECT (property_bag));
+
+ bonobo_property_bag_add (property_bag, "configuration", CONFIGURATION, BONOBO_ARG_STRING, NULL,
+ "RSS Configuration", BONOBO_PROPERTY_WRITEABLE);
+
+ /* show the view itself */
+ gtk_widget_show (GTK_WIDGET (rss_control));
+}
+
+static void
+free_rss_data_item (RSSItemData *item)
+{
+ g_free (item->item_title);
+ g_free (item->item_url);
+ g_free (item);
+}
+
+static void
+nautilus_rss_control_clear_items (NautilusRSSControl *rss_control)
+{
+ if (rss_control->details->items != NULL) {
+ eel_g_list_free_deep_custom (rss_control->details->items, (GFunc) free_rss_data_item, NULL);
+ rss_control->details->items = NULL;
+ }
+}
+
+static void
+nautilus_rss_control_destroy (GtkObject *object)
+{
+ NautilusRSSControl *rss_control;
+
+ rss_control = NAUTILUS_RSS_CONTROL (object);
+ g_free (rss_control->details->rss_uri);
+ g_free (rss_control->details->title);
+ g_free (rss_control->details->main_uri);
+
+ if (rss_control->details->load_file_handle != NULL) {
+ eel_read_file_cancel (rss_control->details->load_file_handle);
+ }
+
+ if (rss_control->details->load_image_handle != NULL) {
+ eel_cancel_gdk_pixbuf_load (rss_control->details->load_image_handle);
+ }
+
+ if (rss_control->details->logo != NULL) {
+ gdk_pixbuf_unref (rss_control->details->logo);
+ }
+
+ if (rss_control->details->bullet != NULL) {
+ gdk_pixbuf_unref (rss_control->details->bullet);
+ }
+
+ if (rss_control->details->items != NULL) {
+ nautilus_rss_control_clear_items (rss_control);
+ }
+
+ if (rss_control->details->font) {
+ gtk_object_unref (GTK_OBJECT (rss_control->details->font));
+ }
+
+ if (rss_control->details->timer_task != 0) {
+ gtk_timeout_remove (rss_control->details->timer_task);
+ rss_control->details->timer_task = 0;
+ }
+
+ g_free (rss_control->details);
+
+ EEL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
+}
+
+
+/* get associated Bonobo control */
+BonoboObject *
+nautilus_rss_control_get_control (NautilusRSSControl *rss_control)
+{
+ return rss_control->details->control;
+}
+
+static void
+nautilus_rss_control_set_title (NautilusRSSControl *rss_control, const char *title)
+{
+ if (eel_strcmp (rss_control->details->title, title) == 0) {
+ return;
+ }
+
+ if (rss_control->details->title) {
+ g_free (rss_control->details->title);
+ }
+ if (title != NULL) {
+ rss_control->details->title = g_strdup (title);
+ } else {
+ rss_control->details->title = NULL;
+
+ }
+ gtk_widget_queue_draw (GTK_WIDGET (rss_control));
+}
+
+
+static void
+rss_logo_callback (GnomeVFSResult error, GdkPixbuf *pixbuf, gpointer callback_data)
+{
+ NautilusRSSControl *rss_control;
+
+ rss_control = NAUTILUS_RSS_CONTROL (callback_data);
+ rss_control->details->load_image_handle = NULL;
+
+ if (rss_control->details->logo) {
+ gdk_pixbuf_unref (rss_control->details->logo);
+ }
+
+ if (pixbuf != NULL) {
+ gdk_pixbuf_ref (pixbuf);
+ rss_control->details->logo = pixbuf;
+ gtk_widget_queue_draw (GTK_WIDGET (rss_control));
+ }
+}
+
+/* utility routine to extract items from a node, returning the count of items found */
+static int
+extract_items (NautilusRSSControl *rss_control, xmlNodePtr container_node)
+{
+ RSSItemData *item_parameters;
+ xmlNodePtr current_node, title_node, temp_node;
+ int item_count;
+ char *title, *temp_str;
+
+ current_node = container_node->childs;
+ item_count = 0;
+ while (current_node != NULL) {
+ if (eel_strcmp (current_node->name, "item") == 0) {
+ title_node = eel_xml_get_child_by_name (current_node, "title");
+ if (title_node) {
+ item_parameters = (RSSItemData*) g_new0 (RSSItemData, 1);
+
+ title = xmlNodeGetContent (title_node);
+ item_parameters->item_title = g_strdup (title);
+ xmlFree (title);
+ temp_node = eel_xml_get_child_by_name (current_node, "link");
+
+ if (temp_node) {
+ temp_str = xmlNodeGetContent (temp_node);
+ item_parameters->item_url = g_strdup (temp_str);
+ xmlFree (temp_str);
+ }
+
+ rss_control->details->items = g_list_append (rss_control->details->items, item_parameters);
+ item_count += 1;
+ }
+ }
+ current_node = current_node->next;
+ }
+ return item_count;
+}
+
+/* completion routine invoked when we've loaded the rss file uri. Parse the xml document, and
+ * then extract the various elements that we require */
+
+static void
+rss_read_done_callback (GnomeVFSResult result,
+ GnomeVFSFileSize file_size,
+ char *file_contents,
+ gpointer callback_data)
+{
+ xmlDocPtr rss_document;
+ xmlNodePtr image_node, channel_node;
+ xmlNodePtr current_node, temp_node, uri_node;
+ char *image_uri, *title, *temp_str;
+ int item_count;
+ NautilusRSSControl *rss_control;
+
+ char *buffer;
+
+ rss_control = NAUTILUS_RSS_CONTROL (callback_data);
+ rss_control->details->load_file_handle = NULL;
+
+ /* make sure the read was successful */
+ if (result != GNOME_VFS_OK) {
+ g_assert (file_contents == NULL);
+ return;
+ }
+
+ /* flag the update time */
+ time (&rss_control->details->last_update);
+
+ /* Parse the rss file with gnome-xml. The gnome-xml parser requires a zero-terminated array. */
+ buffer = g_realloc (file_contents, file_size + 1);
+ buffer[file_size] = '\0';
+ rss_document = xmlParseMemory (buffer, file_size);
+ g_free (buffer);
+
+ /* make sure there wasn't in error parsing the document */
+ if (rss_document == NULL) {
+ return;
+ }
+
+ /* extract the title and set it */
+ channel_node = eel_xml_get_child_by_name (xmlDocGetRootElement (rss_document), "channel");
+ if (channel_node != NULL) {
+ temp_node = eel_xml_get_child_by_name (channel_node, "title");
+ if (temp_node != NULL) {
+ title = xmlNodeGetContent (temp_node);
+ if (title != NULL) {
+ nautilus_rss_control_set_title (rss_control, title);
+ xmlFree (title);
+ }
+ }
+
+ temp_node = eel_xml_get_child_by_name (channel_node, "link");
+ if (temp_node != NULL) {
+ temp_str = xmlNodeGetContent (temp_node);
+ if (temp_str != NULL) {
+ g_free (rss_control->details->main_uri);
+ rss_control->details->main_uri = g_strdup (temp_str);
+ xmlFree (temp_str);
+ }
+ }
+
+ }
+
+ /* extract the image uri and, if found, load it asynchronously */
+ image_node = eel_xml_get_child_by_name (xmlDocGetRootElement (rss_document), "image");
+
+ /* if we can't find it at the top level, look inside the channel */
+ if (image_node == NULL && channel_node != NULL) {
+ image_node = eel_xml_get_child_by_name (channel_node, "image");
+ }
+
+ if (image_node != NULL) {
+ uri_node = eel_xml_get_child_by_name (image_node, "url");
+ if (uri_node != NULL) {
+ image_uri = xmlNodeGetContent (uri_node);
+ if (image_uri != NULL) {
+ rss_control->details->load_image_handle = eel_gdk_pixbuf_load_async (image_uri, rss_logo_callback, rss_control);
+ xmlFree (image_uri);
+ }
+ }
+ }
+
+ /* extract the items in a loop */
+ nautilus_rss_control_clear_items (rss_control);
+ current_node = rss_document->root;
+ item_count = extract_items (rss_control, current_node);
+
+ /* if we couldn't find any items at the main level, look inside the channel node */
+ if (item_count == 0 && channel_node != NULL) {
+ item_count = extract_items (rss_control, channel_node);
+ }
+
+ /* we're done, so free everything up */
+ xmlFreeDoc (rss_document);
+
+ /* schedule a redraw to reflect the new contents */
+ gtk_widget_queue_draw (GTK_WIDGET (rss_control));
+}
+
+/* load the rss file asynchronously */
+static void
+load_rss_file (NautilusRSSControl *rss_control)
+{
+ char *title;
+ /* load the uri asynchrounously, calling a completion routine when completed */
+ rss_control->details->load_file_handle = eel_read_entire_file_async (rss_control->details->rss_uri, rss_read_done_callback, rss_control);
+
+ /* put up a title that's displayed while we wait */
+ title = g_strdup_printf ("Loading %s", rss_control->details->rss_uri);
+ nautilus_rss_control_set_title (rss_control, title);
+ g_free (title);
+}
+
+/* set the uri and load it */
+static void
+nautilus_rss_control_set_uri (NautilusRSSControl *rss_control, const char *uri)
+{
+
+ if (eel_strcmp (rss_control->details->rss_uri, uri) == 0) {
+ return;
+ }
+
+ if (rss_control->details->rss_uri != NULL) {
+ g_free (rss_control->details->rss_uri);
+ rss_control->details->rss_uri = NULL;
+ }
+
+ if (uri != NULL) {
+ rss_control->details->rss_uri = g_strdup (uri);
+ load_rss_file (rss_control);
+ }
+}
+
+/* handle periodically updating the items if necessary */
+static int
+check_for_update (gpointer callback_data)
+{
+ NautilusRSSControl *rss_control;
+ guint current_time, next_time;
+
+ rss_control = NAUTILUS_RSS_CONTROL (callback_data);
+
+ current_time = time (NULL);
+ next_time = rss_control->details->last_update + rss_control->details->update_interval;
+
+ if (current_time > next_time) {
+ rss_control->details->load_file_handle = eel_read_entire_file_async (rss_control->details->rss_uri, rss_read_done_callback, rss_control);
+ rss_control->details->last_update = current_time;
+ }
+ return TRUE;
+}
+
+/* convenience routine to composite an image with the proper clipping */
+static void
+rss_control_pixbuf_composite (GdkPixbuf *source, GdkPixbuf *destination, int x_offset, int y_offset, int alpha)
+{
+ int source_width, source_height, dest_width, dest_height;
+ double float_x_offset, float_y_offset;
+
+ source_width = gdk_pixbuf_get_width (source);
+ source_height = gdk_pixbuf_get_height (source);
+ dest_width = gdk_pixbuf_get_width (destination);
+ dest_height = gdk_pixbuf_get_height (destination);
+
+ float_x_offset = x_offset;
+ float_y_offset = y_offset;
+
+ /* clip to the destination size */
+ if ((x_offset + source_width) > dest_width) {
+ source_width = dest_width - x_offset;
+ }
+ if ((y_offset + source_height) > dest_height) {
+ source_height = dest_height - y_offset;
+ }
+
+ gdk_pixbuf_composite (source, destination, x_offset, y_offset, source_width, source_height,
+ float_x_offset, float_y_offset, 1.0, 1.0, GDK_PIXBUF_ALPHA_BILEVEL, alpha);
+}
+
+
+
+/* draw the logo image */
+static int
+draw_rss_logo_image (NautilusRSSControl *rss_control, GdkPixbuf *pixbuf, int offset)
+{
+ GtkWidget *widget;
+ char time_str[16];
+ int logo_width, logo_height;
+ int v_offset, pixbuf_width;
+ EelDimensions time_dimensions;
+
+ widget = GTK_WIDGET (rss_control);
+ v_offset = offset;
+
+ if (rss_control->details->logo != NULL) {
+ logo_width = gdk_pixbuf_get_width (rss_control->details->logo);
+ logo_height = gdk_pixbuf_get_height (rss_control->details->logo);
+
+ rss_control_pixbuf_composite (rss_control->details->logo, pixbuf, 2, v_offset, 255);
+ v_offset += logo_height + 2;
+ }
+
+ /* also, read the update time in the upper right corner */
+ if (rss_control->details->last_update != 0) {
+ strftime (&time_str[0], 16, "%I:%M %p", localtime (&rss_control->details->last_update));
+
+ time_dimensions = eel_scalable_font_measure_text (rss_control->details->font, 9, time_str, strlen (time_str));
+
+ pixbuf_width = gdk_pixbuf_get_width (pixbuf);
+ eel_scalable_font_draw_text (rss_control->details->font, pixbuf,
+ pixbuf_width - time_dimensions.width - 4, offset,
+ NULL, 9, time_str, strlen (time_str),
+ EEL_RGB_COLOR_BLACK, EEL_OPACITY_FULLY_OPAQUE);
+ }
+ return v_offset;
+}
+
+/* draw the title */
+static int
+draw_rss_title (NautilusRSSControl *rss_control, GdkPixbuf *pixbuf, int v_offset)
+{
+ GtkWidget *widget;
+ EelDimensions title_dimensions;
+
+ if (rss_control->details->title == NULL || rss_control->details->font == NULL) {
+ return v_offset;
+ }
+
+ widget = GTK_WIDGET (rss_control);
+
+ /* first, measure the text */
+ title_dimensions = eel_scalable_font_measure_text (rss_control->details->font,
+ 18,
+ rss_control->details->title, strlen (rss_control->details->title));
+
+ /* draw the name into the pixbuf using anti-aliased text */
+ eel_scalable_font_draw_text (rss_control->details->font, pixbuf,
+ 4, v_offset,
+ NULL,
+ 18,
+ rss_control->details->title, strlen (rss_control->details->title),
+ EEL_RGB_COLOR_BLACK,
+ EEL_OPACITY_FULLY_OPAQUE);
+
+ return v_offset + title_dimensions.height;
+}
+
+/* utility for underlining an item - assumes the pixbuf has an alpha channel */
+static void
+draw_blue_line (GdkPixbuf *pixbuf, int x, int y, int width)
+{
+ guchar *pixels_ptr;
+ int row_stride, line_width, pixbuf_width, i;
+
+ line_width = width;
+ pixbuf_width = gdk_pixbuf_get_width (pixbuf);
+ if ((x + line_width) > pixbuf_width) {
+ line_width = pixbuf_width - x - 1;
+ }
+ row_stride = gdk_pixbuf_get_rowstride (pixbuf);
+ pixels_ptr = gdk_pixbuf_get_pixels (pixbuf);
+
+ pixels_ptr += (4 * x) + (row_stride * y);
+ for (i = 0; i < line_width; i++) {
+ *pixels_ptr++ = 0;
+ *pixels_ptr++ = 0;
+ *pixels_ptr++ = 159;
+ *pixels_ptr++ = 255;
+
+ }
+}
+
+/* draw the items */
+static int
+draw_rss_items (NautilusRSSControl *rss_control, GdkPixbuf *pixbuf, int v_offset)
+{
+ GList *current_item;
+ RSSItemData *item_data;
+ int bullet_width, bullet_height, font_size;
+ int item_index, bullet_alpha;
+ int maximum_height;
+ guint32 text_color;
+ EelDimensions text_dimensions;
+
+ maximum_height = GTK_WIDGET (rss_control)->allocation.height - 16;
+
+ if (rss_control->details->bullet) {
+ bullet_width = gdk_pixbuf_get_width (rss_control->details->bullet);
+ bullet_height = gdk_pixbuf_get_height (rss_control->details->bullet);
+ } else {
+ bullet_width = 0;
+ bullet_height = 0;
+ }
+
+ current_item = rss_control->details->items;
+ item_index = 0;
+
+ while (current_item != NULL) {
+ /* draw the text */
+
+ item_data = (RSSItemData*) current_item->data;
+ if (item_index == rss_control->details->prelight_index) {
+ text_color = EEL_RGB_COLOR_BLUE;
+ bullet_alpha = 255;
+ } else {
+ text_color = EEL_RGB_COLOR_BLACK;
+ bullet_alpha = 160;
+ }
+ font_size = 12;
+
+ text_dimensions = eel_scalable_font_measure_text (rss_control->details->font,
+ font_size,
+ item_data->item_title,
+ strlen (item_data->item_title));
+
+ eel_scalable_font_draw_text (rss_control->details->font, pixbuf,
+ 20, v_offset,
+ NULL,
+ font_size,
+ item_data->item_title, strlen (item_data->item_title),
+ text_color,
+ EEL_OPACITY_FULLY_OPAQUE);
+
+ /* draw a blue underline to make it look like a link */
+ draw_blue_line (pixbuf, 20, v_offset + 11, text_dimensions.width);
+
+ /* draw the bullet */
+ if (rss_control->details->bullet) {
+ rss_control_pixbuf_composite (rss_control->details->bullet, pixbuf, 2, v_offset - 2, bullet_alpha);
+ }
+
+ v_offset += RSS_ITEM_HEIGHT;
+ item_index += 1;
+ current_item = current_item->next;
+ if (v_offset > maximum_height) {
+ break;
+ }
+
+ }
+
+ return v_offset;
+}
+
+/* handle drawing the control */
+static void
+nautilus_rss_control_draw (GtkWidget *widget, GdkRectangle *box)
+{
+ NautilusRSSControl *control;
+ GdkPixbuf *temp_pixbuf;
+ ArtIRect pixbuf_rect;
+
+ int width, height, v_offset;
+
+ /* allocate a pixbuf to draw into */
+ width = widget->allocation.width;
+ height = widget->allocation.height;
+
+ /* don't draw when too small, like during size negotiation */
+ if (width < MINIMUM_DRAW_SIZE || height < MINIMUM_DRAW_SIZE) {
+ return;
+ }
+
+ temp_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height);
+ pixbuf_rect.x0 = 1;
+ pixbuf_rect.y0 = 1;
+ pixbuf_rect.x1 = width - 1;
+ pixbuf_rect.y1 = height - 1;
+
+ eel_gdk_pixbuf_fill_rectangle_with_color (temp_pixbuf, NULL, 0xFF000000);
+ eel_gdk_pixbuf_fill_rectangle_with_color (temp_pixbuf, &pixbuf_rect, 0xFFEFEFEF);
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (NAUTILUS_IS_RSS_CONTROL (widget));
+
+ control = NAUTILUS_RSS_CONTROL (widget);
+
+ v_offset = draw_rss_logo_image (control, temp_pixbuf, 2);
+ v_offset = draw_rss_title (control, temp_pixbuf, v_offset);
+ control->details->items_v_offset = v_offset;
+
+ v_offset += 6;
+ v_offset = draw_rss_items (control, temp_pixbuf, v_offset);
+
+ /* blit the pixbuf to the drawable, then release it */
+ gdk_pixbuf_render_to_drawable_alpha (temp_pixbuf,
+ widget->window,
+ 0, 0,
+ widget->allocation.x, widget->allocation.y,
+ width, height,
+ GDK_PIXBUF_ALPHA_BILEVEL, 128,
+ GDK_RGB_DITHER_MAX,
+ 0, 0);
+
+ gdk_pixbuf_unref (temp_pixbuf);
+}
+
+/* handle expose events */
+static int
+nautilus_rss_control_expose (GtkWidget *widget, GdkEventExpose *event)
+{
+ GdkRectangle box;
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (NAUTILUS_IS_RSS_CONTROL (widget), FALSE);
+
+ box.x = 0; box.y = 0;
+ box.width = widget->allocation.width;
+ box.height = widget->allocation.height;
+
+ nautilus_rss_control_draw (widget, &box);
+ return FALSE;
+}
+
+/* maintain the prelight state, redrawing if necessary */
+
+static void
+nautilus_rss_control_set_prelight_index (NautilusRSSControl *rss_control, int prelight_state)
+{
+ if (rss_control->details->prelight_index != prelight_state) {
+ rss_control->details->prelight_index = prelight_state;
+ gtk_widget_queue_draw (GTK_WIDGET (rss_control));
+ }
+}
+
+/* handle mouse motion events by maintaining the prelight state */
+static gboolean
+nautilus_rss_control_motion_event (GtkWidget *widget, GdkEventMotion *event)
+{
+ int x, y;
+ int which_item, item_count;
+ NautilusRSSControl *rss_control;
+
+ rss_control = NAUTILUS_RSS_CONTROL (widget);
+
+ gtk_widget_get_pointer (widget, &x, &y);
+ which_item = (y - (widget->allocation.y + rss_control->details->items_v_offset)) / RSS_ITEM_HEIGHT;
+ item_count = g_list_length (rss_control->details->items);
+
+ if (which_item < 0 || which_item >= item_count) {
+ which_item = -1;
+ }
+ nautilus_rss_control_set_prelight_index (rss_control, which_item);
+ return TRUE;
+}
+
+/* handle leave events by cancelling any prelighting */
+static gboolean
+nautilus_rss_control_leave_event (GtkWidget *widget, GdkEventCrossing *event)
+{
+ NautilusRSSControl *rss_control;
+
+ rss_control = NAUTILUS_RSS_CONTROL (widget);
+ nautilus_rss_control_set_prelight_index (rss_control, -1);
+
+ return TRUE;
+}
+
+/* handle size requests by requesting a fixed size */
+/* should size according to the number of items */
+static void
+nautilus_rss_control_size_request (GtkWidget *widget, GtkRequisition *request)
+{
+ request->width = 240;
+ request->height = 140;
+}
+
+
+/* handle button press events */
+static gboolean
+nautilus_rss_control_button_press_event (GtkWidget *widget, GdkEventButton *event)
+{
+ GList *selected_item;
+ NautilusRSSControl *rss_control;
+ RSSItemData *item_data;
+ char *command;
+ int result, which_item;
+
+ rss_control = NAUTILUS_RSS_CONTROL (widget);
+ if (event->y < (widget->allocation.y + rss_control->details->items_v_offset)) {
+ command = g_strdup_printf ("nautilus %s", rss_control->details->main_uri);
+ result = system (command);
+ g_free (command);
+ } else {
+ which_item = (event->y - (widget->allocation.y + rss_control->details->items_v_offset)) / RSS_ITEM_HEIGHT;
+ if (which_item < (int) g_list_length (rss_control->details->items)) {
+ selected_item = g_list_nth (rss_control->details->items, which_item);
+ item_data = (RSSItemData*) selected_item->data;
+
+ command = g_strdup_printf ("nautilus %s", item_data->item_url);
+ result = system (command);
+ g_free (command);
+ }
+ }
+
+ return FALSE;
+}
+
diff --git a/components/rss-control/nautilus-rss-control.h b/components/rss-control/nautilus-rss-control.h
new file mode 100644
index 000000000..e73c31bbc
--- /dev/null
+++ b/components/rss-control/nautilus-rss-control.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2000 Eazel, Inc
+ *
+ * This program 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; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Andy Hertzfeld
+ */
+
+/* header file for the rss control component */
+
+#ifndef NAUTILUS_RSS_CONTROL_H
+#define NAUTILUS_RSS_CONTROL_H
+
+#include <bonobo.h>
+#include <gtk/gtkeventbox.h>
+typedef struct _NautilusRSSControl NautilusRSSControl;
+typedef struct _NautilusRSSControlClass NautilusRSSControlClass;
+
+#define NAUTILUS_TYPE_RSS_CONTROL (nautilus_rss_control_get_type ())
+#define NAUTILUS_RSS_CONTROL(obj) (GTK_CHECK_CAST ((obj), NAUTILUS_TYPE_RSS_CONTROL, NautilusRSSControl))
+#define NAUTILUS_RSS_CONTROL_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), NAUTILUS_TYPE_RSS_CONTROL, NautilusRSSControlClass))
+#define NAUTILUS_IS_RSS_CONTROL(obj) (GTK_CHECK_TYPE ((obj), NAUTILUS_TYPE_RSS_CONTROL))
+#define NAUTILUS_IS_RSS_CONTROL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_RSS_CONTROL))
+
+typedef struct _NautilusRSSControlDetails NautilusRSSControlDetails;
+
+struct _NautilusRSSControl {
+ GtkEventBox parent;
+ NautilusRSSControlDetails *details;
+};
+
+struct _NautilusRSSControlClass {
+ GtkEventBoxClass parent_class;
+};
+
+/* GtkObject support */
+GtkType nautilus_rss_control_get_type (void);
+BonoboObject* nautilus_rss_control_get_control (NautilusRSSControl *rss_control);
+
+#endif /* NAUTILUS_RSS_CONTROL_H */
diff --git a/components/rss-control/nautilus-rss-control.oafinfo b/components/rss-control/nautilus-rss-control.oafinfo
new file mode 100644
index 000000000..9d21acf6a
--- /dev/null
+++ b/components/rss-control/nautilus-rss-control.oafinfo
@@ -0,0 +1,20 @@
+<oaf_info>
+
+<oaf_server iid="OAFIID:nautilus_rss_control_factory:1230" type="exe" location="nautilus-rss-control">
+<oaf_attribute name="repo_ids" type="stringv">
+<item value="IDL:Bonobo/GenericFactory:1.0"/>
+</oaf_attribute>
+<oaf_attribute name="name" type="string" value="rss control factory"/>
+<oaf_attribute name="description" type="string" value="rss control object factory"/>
+</oaf_server>
+
+<oaf_server iid="OAFIID:nautilus_rss_control:1230" type="factory" location="OAFIID:nautilus_rss_control_factory:1230">
+<oaf_attribute name="repo_ids" type="stringv">
+<item value="IDL:Bonobo/Control:1.0"/>
+<item value="IDL:Bonobo/Unknown:1.0"/>
+</oaf_attribute>
+<oaf_attribute name="name" type="string" value="rss control"/>
+<oaf_attribute name="description" type="string" value="nautilus rss control object"/>
+</oaf_server>
+
+</oaf_info>
diff --git a/components/services/nautilus-dependent-shared/shared-service-widgets.c b/components/services/nautilus-dependent-shared/shared-service-widgets.c
deleted file mode 100644
index c88adcbd9..000000000
--- a/components/services/nautilus-dependent-shared/shared-service-widgets.c
+++ /dev/null
@@ -1,212 +0,0 @@
-/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
-
-/*
- * Copyright (C) 2000 Eazel, Inc
- *
- * This program 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; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This program 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. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public
- * License along with this program; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Authors: Ramiro Estrugo <ramiro@eazel.com>
- * J Shane Culpepper <pepper@eazel.com>
- *
- */
-
-#include <config.h>
-
-#include "shared-service-widgets.h"
-
-#include <libnautilus-extensions/nautilus-background.h>
-#include <libnautilus-extensions/nautilus-gdk-pixbuf-extensions.h>
-#include <libnautilus-extensions/nautilus-gtk-extensions.h>
-#include <libnautilus-extensions/nautilus-gtk-macros.h>
-#include <libnautilus-extensions/nautilus-glib-extensions.h>
-#include <libnautilus-extensions/nautilus-global-preferences.h>
-#include <libnautilus-extensions/nautilus-file-utilities.h>
-#include <libnautilus-extensions/nautilus-string.h>
-#include <libnautilus-extensions/nautilus-font-factory.h>
-#include <libnautilus-extensions/nautilus-gdk-extensions.h>
-#include <libnautilus-extensions/nautilus-theme.h>
-
-#include <stdio.h>
-
-/* private shared helper routine to create an image widget from a pixbuf */
-static GtkWidget*
-create_image_widget_from_pixbuf (GdkPixbuf *icon_pixbuf,
- const char *tile_icon_name)
-{
- GtkWidget *image_widget;
-
- g_return_val_if_fail (icon_pixbuf || tile_icon_name, NULL);
-
- image_widget = nautilus_image_new ();
-
- if (icon_pixbuf != NULL) {
- nautilus_image_set_pixbuf (NAUTILUS_IMAGE (image_widget), icon_pixbuf);
- }
-
- if (tile_icon_name != NULL) {
- char *tile_icon_path;
-
- tile_icon_path = nautilus_pixmap_file (tile_icon_name);
-
- if (tile_icon_path != NULL) {
- GdkPixbuf *tile_icon_pixbuf;
- tile_icon_pixbuf = gdk_pixbuf_new_from_file (tile_icon_path);
- g_free (tile_icon_path);
-
- if (tile_icon_pixbuf != NULL) {
- nautilus_buffered_widget_set_tile_pixbuf (NAUTILUS_BUFFERED_WIDGET (image_widget), tile_icon_pixbuf);
- gdk_pixbuf_unref (tile_icon_pixbuf);
- }
- else {
- g_warning ("Could not find the requested tile_icon: %s", tile_icon_path);
- }
- }
- }
-
- return image_widget;
-}
-
-/* create and return an image widget using a themed nautilus icon name and a tiled background */
-GtkWidget*
-create_image_widget (const char *icon_name, const char *tile_icon_name)
-{
- GtkWidget *image_widget;
- GdkPixbuf *pixbuf;
-
- g_return_val_if_fail (icon_name || tile_icon_name, NULL);
-
- pixbuf = NULL;
- if (icon_name != NULL) {
- char *icon_path;
-
- icon_path = nautilus_theme_get_image_path (icon_name);
- if (icon_path != NULL) {
- pixbuf = gdk_pixbuf_new_from_file (icon_path);
- g_free (icon_path);
-
- if (pixbuf == NULL) {
- g_warning ("Could not find the requested icon: %s", icon_path);
- }
- }
- }
-
- /* create the image widget then release the pixbuf*/
- image_widget = create_image_widget_from_pixbuf (pixbuf, tile_icon_name);
- if (pixbuf != NULL) {
- gdk_pixbuf_unref (pixbuf);
- }
- return image_widget;
-}
-
-/* create and return an image widget from a uri and a tiled background.
- It also pins the image to the specified dimensions */
-
-/* FIXME bugzilla.eazel.com 5138
- * this calls gnome-vfs synchronously for an HTTP uri and thus can block
- * the UI indefinitely
- */
-GtkWidget*
-create_image_widget_from_uri (const char *uri, const char *tile_icon_name,
- int max_width, int max_height)
-{
- GtkWidget *image_widget;
- GdkPixbuf *pixbuf, *scaled_pixbuf;
-
- g_return_val_if_fail (uri || tile_icon_name, NULL);
-
- /* as an optimization, it can be a local file. If it doesn't start with http://,
- just pass it on to create_image_widget */
- if (!nautilus_istr_has_prefix (uri, "http://")) {
- return create_image_widget (uri, tile_icon_name);
- }
-
- /* load the image - synchronously, at least at first */
- pixbuf = nautilus_gdk_pixbuf_load (uri);
-
- /* pin the image to the specified dimensions if necessary */
- if (pixbuf && max_width > 0 && max_height > 0) {
- scaled_pixbuf = nautilus_gdk_pixbuf_scale_down_to_fit (pixbuf, max_width, max_height);
- gdk_pixbuf_unref (pixbuf);
- pixbuf = scaled_pixbuf;
- }
-
- /* create the image widget then release the pixbuf*/
- image_widget = create_image_widget_from_pixbuf (pixbuf, tile_icon_name);
- if (pixbuf != NULL) {
- gdk_pixbuf_unref (pixbuf);
- }
-
- return image_widget;
-}
-
-/* create a label widget with anti-aliased text and a tiled image background */
-GtkWidget*
-create_label_widget (const char *text,
- guint font_size,
- const char *tile_icon_name,
- guint xpad,
- guint ypad,
- gint horizontal_offset,
- gint vertical_offset)
-{
- GtkWidget *label;
-
- g_return_val_if_fail (text != NULL, NULL);
- g_return_val_if_fail (font_size > 0, NULL);
-
- label = nautilus_label_new (text);
-
- nautilus_label_set_font_from_components (NAUTILUS_LABEL (label), "helvetica", "bold", NULL, NULL);
- nautilus_label_set_font_size (NAUTILUS_LABEL (label), font_size);
- nautilus_label_set_text_color (NAUTILUS_LABEL (label), NAUTILUS_RGB_COLOR_WHITE);
-
- if (tile_icon_name != NULL) {
- char *tile_icon_path;
-
- tile_icon_path = nautilus_pixmap_file (tile_icon_name);
-
- if (tile_icon_path != NULL) {
- GdkPixbuf *tile_icon_pixbuf;
- tile_icon_pixbuf = gdk_pixbuf_new_from_file (tile_icon_path);
- g_free (tile_icon_path);
-
- if (tile_icon_pixbuf != NULL) {
- nautilus_buffered_widget_set_tile_pixbuf (NAUTILUS_BUFFERED_WIDGET (label), tile_icon_pixbuf);
- gdk_pixbuf_unref (tile_icon_pixbuf);
- }
- else {
- g_warning ("Could not find the requested tile_icon: %s", tile_icon_path);
- }
- }
- }
-
- gtk_misc_set_padding (GTK_MISC (label), xpad, ypad);
-
- nautilus_buffered_widget_set_vertical_offset (NAUTILUS_BUFFERED_WIDGET (label), vertical_offset);
- nautilus_buffered_widget_set_horizontal_offset (NAUTILUS_BUFFERED_WIDGET (label), horizontal_offset);
-
- return label;
-}
-
-/* utility routine to show an error message */
-void
-show_feedback (GtkWidget *widget,
- char *error_message)
-{
- nautilus_label_set_text (NAUTILUS_LABEL (widget), error_message);
- gtk_widget_show (widget);
-}
-
diff --git a/components/throbber/nautilus-throbber.c b/components/throbber/nautilus-throbber.c
index f86d7513e..a3b82cb95 100644
--- a/components/throbber/nautilus-throbber.c
+++ b/components/throbber/nautilus-throbber.c
@@ -104,7 +104,8 @@ nautilus_throbber_initialize_class (NautilusThrobberClass *throbber_class)
enum {
THROBBING,
- LOCATION
+ LOCATION,
+ CONFIGURATION
} MyArgs;
@@ -140,8 +141,12 @@ get_bonobo_properties (BonoboPropertyBag *bag,
BONOBO_ARG_SET_STRING (arg, "");
}
+ break;
}
+ case CONFIGURATION:
+ BONOBO_ARG_SET_STRING (arg, "");
+ break;
default:
g_warning ("Unhandled arg %d", arg_id);
break;
@@ -174,6 +179,13 @@ set_bonobo_properties (BonoboPropertyBag *bag,
break;
}
+ /* respond to configuration calls by starting the throbber */
+ case CONFIGURATION:
+ {
+ nautilus_throbber_start (throbber);
+ break;
+ }
+
default:
g_warning ("Unhandled arg %d", arg_id);
break;
@@ -300,6 +312,9 @@ nautilus_throbber_initialize (NautilusThrobber *throbber)
"Throbber active", 0);
bonobo_property_bag_add (throbber->details->property_bag, "location", LOCATION, BONOBO_ARG_STRING, NULL,
"associated URL", 0);
+
+ bonobo_property_bag_add (throbber->details->property_bag, "configuration", CONFIGURATION, BONOBO_ARG_STRING, NULL,
+ "Throbber image source", BONOBO_PROPERTY_WRITEABLE);
/* allocate the pixmap that holds the image */
nautilus_throbber_load_images (throbber);
diff --git a/components/vcard/.cvsignore b/components/vcard/.cvsignore
new file mode 100644
index 000000000..3c5d6aaee
--- /dev/null
+++ b/components/vcard/.cvsignore
@@ -0,0 +1,5 @@
+.deps
+.libs
+Makefile
+Makefile.in
+nautilus-vcard
diff --git a/components/vcard/Makefile.am b/components/vcard/Makefile.am
new file mode 100644
index 000000000..0b66e828f
--- /dev/null
+++ b/components/vcard/Makefile.am
@@ -0,0 +1,51 @@
+NULL =
+
+SUBDIRS =
+
+INCLUDES = \
+ -DPREFIX=\"$(prefix)\" \
+ -DG_LOG_DOMAIN=\"Nautilus-VCard\" \
+ -DDATADIR=\""$(datadir)"\" \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ -I$(top_builddir)/libnautilus \
+ -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
+ $(EEL_INCLUDEDIR) \
+ $(LIBRSVG_INCLUDEDIR) \
+ $(GNOMEUI_CFLAGS) \
+ $(GCONF_CFLAGS) \
+ $(OAF_CFLAGS) \
+ $(BONOBO_CFLAGS) \
+ $(VFS_CFLAGS)
+
+oafdir = $(datadir)/oaf
+oaf_DATA = \
+ nautilus-vcard.oaf
+
+
+bin_PROGRAMS = \
+ nautilus-vcard
+
+nautilus_vcard_SOURCES = \
+ vcard.c \
+ vcard.h \
+ nautilus-vcard.c \
+ nautilus-vcard.h \
+ main.c
+
+nautilus_vcard_LDADD = \
+ $(top_builddir)/libnautilus/libnautilus.la \
+ $(top_builddir)/libnautilus-extensions/libnautilus-extensions.la \
+ $(EEL_LIBS) \
+ $(LIBRSVG_LIBS) \
+ $(BONOBO_LIBS) \
+ $(GNOMEUI_LIBS) \
+ $(GCONF_LIBS) \
+ $(VFS_LIBS) \
+ $(GNORBA_LIBS)
+
+OBJECT_DIRECTORY_LIBS = $(GNOME_LIBS) $(OAF_LIBS)
+
+EXTRA_DIST = \
+ $(oaf_DATA) \
+ $(NULL)
diff --git a/components/vcard/main.c b/components/vcard/main.c
new file mode 100644
index 000000000..68f1e3334
--- /dev/null
+++ b/components/vcard/main.c
@@ -0,0 +1,120 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2000 Eazel, Inc
+ *
+ * This program 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; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Andy Hertzfeld
+ */
+
+/* main.c - main function and object activation function for the vcard component. */
+
+#include <config.h>
+#include "nautilus-vcard.h"
+
+#include <bonobo.h>
+#include <gnome.h>
+#include <libgnomevfs/gnome-vfs.h>
+#include <eel/eel-debug.h>
+#include <liboaf/liboaf.h>
+
+static int object_count = 0;
+
+static void
+vcard_object_destroyed(GtkObject *obj)
+{
+ object_count--;
+ if (object_count <= 0) {
+ gtk_main_quit ();
+ }
+}
+
+static BonoboObject *
+vcard_make_object (BonoboGenericFactory *factory,
+ const char *iid,
+ void *closure)
+{
+ NautilusVCard *vcard;
+ BonoboObject *bonobo_control;
+
+ if (strcmp (iid, "OAFIID:nautilus_vcard")) {
+ return NULL;
+ }
+
+ vcard = NAUTILUS_VCARD (gtk_object_new (NAUTILUS_TYPE_VCARD, NULL));
+
+ object_count++;
+
+ bonobo_control = nautilus_vcard_get_control (vcard);
+
+ gtk_signal_connect (GTK_OBJECT (bonobo_control), "destroy", vcard_object_destroyed, NULL);
+ return bonobo_control;
+}
+
+int
+main (int argc, char *argv[])
+{
+ BonoboGenericFactory *factory;
+ CORBA_ORB orb;
+ char *registration_id;
+
+ /* Make criticals and warnings stop in the debugger if
+ * NAUTILUS_DEBUG is set. Unfortunately, this has to be done
+ * explicitly for each domain.
+ */
+ if (g_getenv ("NAUTILUS_DEBUG") != NULL) {
+ eel_make_warnings_and_criticals_stop_in_debugger
+ (G_LOG_DOMAIN, g_log_domain_glib,
+ "Bonobo",
+ "Gdk",
+ "GnomeUI",
+ "GnomeVFS",
+ "GnomeVFS-CORBA",
+ "GnomeVFS-pthread",
+ "Gtk",
+ "Gdk-Pixbuf",
+ "Nautilus",
+ "Nautilus-Authenticate",
+ "Nautilus-Tree",
+ "ORBit",
+ NULL);
+ }
+
+ gnome_init_with_popt_table("nautilus-vcard", VERSION,
+ argc, argv,
+ oaf_popt_options, 0, NULL);
+
+ orb = oaf_init (argc, argv);
+
+ bonobo_init (orb, CORBA_OBJECT_NIL, CORBA_OBJECT_NIL);
+
+ /* initialize gnome-vfs, etc */
+ g_thread_init (NULL);
+ gnome_vfs_init ();
+
+ registration_id = oaf_make_registration_id ("OAFIID:nautilus_vcard_factory", getenv ("DISPLAY"));
+ factory = bonobo_generic_factory_new_multi (registration_id,
+ vcard_make_object,
+ NULL);
+ g_free (registration_id);
+
+
+ do {
+ bonobo_main ();
+ } while (object_count > 0);
+
+ return 0;
+}
diff --git a/components/vcard/nautilus-vcard.c b/components/vcard/nautilus-vcard.c
new file mode 100644
index 000000000..e22002c79
--- /dev/null
+++ b/components/vcard/nautilus-vcard.c
@@ -0,0 +1,577 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+
+/*
+ * Copyright (C) 2000 Eazel, Inc.
+ *
+ * This library 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; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library 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. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Andy Hertzfeld <andy@eazel.com>
+ *
+ */
+
+/* this is the implementation of the vcard component, which display a vcard graphically
+ */
+
+#include <config.h>
+#include <gnome.h>
+#include <liboaf/liboaf.h>
+
+#include <bonobo.h>
+
+#include "vcard.h"
+#include "nautilus-vcard.h"
+
+#include <gnome-xml/parser.h>
+#include <gnome-xml/xmlmemory.h>
+
+#include <libgnomevfs/gnome-vfs.h>
+
+#include <libnautilus/nautilus-view.h>
+
+#include <libnautilus-extensions/nautilus-file-utilities.h>
+#include <eel/eel-gdk-extensions.h>
+#include <eel/eel-gdk-pixbuf-extensions.h>
+#include <eel/eel-glib-extensions.h>
+#include <eel/eel-gtk-extensions.h>
+#include <eel/eel-gtk-macros.h>
+#include <eel/eel-scalable-font.h>
+#include <eel/eel-string.h>
+#include <eel/eel-xml-extensions.h>
+#include <eel/eel-vfs-extensions.h>
+#include <libnautilus-extensions/nautilus-font-factory.h>
+
+/* private instance variables */
+struct _NautilusVCardDetails {
+ char *vcard_uri;
+ char *vcard_data;
+
+ EelReadFileHandle *load_file_handle;
+ EelPixbufLoadHandle *load_image_handle;
+
+ BonoboObject *control;
+ EelScalableFont *font;
+
+ GdkPixbuf *logo;
+};
+
+static void nautilus_vcard_initialize_class (NautilusVCardClass *klass);
+static void nautilus_vcard_initialize (NautilusVCard *view);
+static void nautilus_vcard_destroy (GtkObject *object);
+
+static void nautilus_vcard_draw (GtkWidget *widget, GdkRectangle *box);
+static int nautilus_vcard_expose (GtkWidget *widget, GdkEventExpose *event);
+static gboolean nautilus_vcard_button_press_event (GtkWidget *widget, GdkEventButton *event);
+static gboolean nautilus_vcard_motion_event (GtkWidget *widget, GdkEventMotion *event);
+static gboolean nautilus_vcard_leave_event (GtkWidget *widget, GdkEventCrossing *event);
+static void nautilus_vcard_size_request (GtkWidget *widget, GtkRequisition *request);
+
+static void nautilus_vcard_set_uri (NautilusVCard *vcard, const char *uri);
+
+
+EEL_DEFINE_CLASS_BOILERPLATE (NautilusVCard,
+ nautilus_vcard,
+ GTK_TYPE_EVENT_BOX)
+
+#define MINIMUM_DRAW_SIZE 4
+
+static void
+nautilus_vcard_initialize_class (NautilusVCardClass *klass)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+
+ object_class = GTK_OBJECT_CLASS (klass);
+ widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->destroy = nautilus_vcard_destroy;
+
+ widget_class->draw = nautilus_vcard_draw;
+ widget_class->expose_event = nautilus_vcard_expose;
+ widget_class->button_press_event = nautilus_vcard_button_press_event;
+ widget_class->motion_notify_event = nautilus_vcard_motion_event;
+ widget_class->leave_notify_event = nautilus_vcard_leave_event;
+ widget_class->size_request = nautilus_vcard_size_request;
+}
+
+/* routines to handle setting and getting the configuration properties of the Bonobo control */
+
+enum {
+ CONFIGURATION
+} MyArgs;
+
+
+static void
+get_bonobo_properties (BonoboPropertyBag *bag,
+ BonoboArg *arg,
+ guint arg_id,
+ CORBA_Environment *ev,
+ gpointer user_data)
+{
+ NautilusVCard *vcard = NAUTILUS_VCARD (user_data);
+
+ switch (arg_id) {
+
+ case CONFIGURATION:
+ {
+ BONOBO_ARG_SET_STRING (arg, vcard->details->vcard_uri);
+ break;
+ }
+
+ default:
+ g_warning ("Unhandled arg %d", arg_id);
+ break;
+ }
+}
+
+static void
+set_bonobo_properties (BonoboPropertyBag *bag,
+ const BonoboArg *arg,
+ guint arg_id,
+ CORBA_Environment *ev,
+ gpointer user_data)
+{
+ NautilusVCard *vcard = NAUTILUS_VCARD (user_data);
+
+ switch (arg_id) {
+
+ case CONFIGURATION:
+ {
+ char *uri;
+
+ uri = BONOBO_ARG_GET_STRING (arg);
+ nautilus_vcard_set_uri (vcard, uri);
+
+ break;
+ }
+
+ default:
+ g_warning ("Unhandled arg %d", arg_id);
+ break;
+ }
+}
+
+/* initialize ourselves by connecting to the location change signal and allocating our subviews */
+static void
+nautilus_vcard_initialize (NautilusVCard *vcard)
+{
+ GtkWidget *frame;
+ BonoboPropertyBag *property_bag;
+
+ vcard->details = g_new0 (NautilusVCardDetails, 1);
+
+ /* set up the font */
+ vcard->details->font = eel_scalable_font_get_default_font ();
+
+ /* receive mouse motion events */
+ gtk_widget_add_events (GTK_WIDGET (vcard), GDK_POINTER_MOTION_MASK);
+
+ /* embed it into a frame */
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type(GTK_FRAME (frame), GTK_SHADOW_OUT);
+ gtk_widget_show (frame);
+ gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET (vcard));
+
+ /* make the bonobo control */
+ vcard->details->control = (BonoboObject*) bonobo_control_new (GTK_WIDGET (frame));
+
+ /* attach a property bag with the configure property */
+ property_bag = bonobo_property_bag_new (get_bonobo_properties, set_bonobo_properties, vcard);
+ bonobo_control_set_properties (BONOBO_CONTROL(vcard->details->control),property_bag);
+ bonobo_object_unref (BONOBO_OBJECT (property_bag));
+
+ bonobo_property_bag_add (property_bag, "configuration", CONFIGURATION, BONOBO_ARG_STRING, NULL,
+ "VCard Configuration", BONOBO_PROPERTY_WRITEABLE);
+
+ /* show the view itself */
+ gtk_widget_show (GTK_WIDGET (vcard));
+}
+
+
+static void
+nautilus_vcard_destroy (GtkObject *object)
+{
+ NautilusVCard *vcard;
+
+ vcard = NAUTILUS_VCARD (object);
+ g_free (vcard->details->vcard_uri);
+ g_free (vcard->details->vcard_data);
+
+ if (vcard->details->load_file_handle != NULL) {
+ eel_read_file_cancel (vcard->details->load_file_handle);
+ }
+
+ if (vcard->details->load_image_handle != NULL) {
+ eel_cancel_gdk_pixbuf_load (vcard->details->load_image_handle);
+ }
+
+
+ if (vcard->details->logo != NULL) {
+ gdk_pixbuf_unref (vcard->details->logo);
+ }
+
+ if (vcard->details->font) {
+ gtk_object_unref (GTK_OBJECT (vcard->details->font));
+ }
+
+ g_free (vcard->details);
+
+ /* Chain destroy */
+ EEL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
+}
+
+/* get associated Bonobo control */
+BonoboObject *
+nautilus_vcard_get_control (NautilusVCard *vcard)
+{
+ return vcard->details->control;
+}
+
+static void
+vcard_logo_callback (GnomeVFSResult error, GdkPixbuf *pixbuf, gpointer callback_data)
+{
+ NautilusVCard *vcard;
+ GdkPixbuf *scaled_pixbuf;
+
+ vcard = NAUTILUS_VCARD (callback_data);
+ vcard->details->load_image_handle = NULL;
+
+ if (vcard->details->logo) {
+ gdk_pixbuf_unref (vcard->details->logo);
+ }
+
+ if (pixbuf != NULL) {
+ gdk_pixbuf_ref (pixbuf);
+
+ scaled_pixbuf = eel_gdk_pixbuf_scale_down_to_fit (pixbuf, 128, 80);
+ gdk_pixbuf_unref (pixbuf);
+ vcard->details->logo = scaled_pixbuf;
+ gtk_widget_queue_draw (GTK_WIDGET (vcard));
+ }
+}
+
+/* completion routine invoked when we've loaded the vcard file uri. */
+
+static void
+vcard_read_done_callback (GnomeVFSResult result,
+ GnomeVFSFileSize file_size,
+ char *file_contents,
+ gpointer callback_data)
+{
+ char *logo_uri;
+ NautilusVCard *vcard;
+
+ vcard = NAUTILUS_VCARD (callback_data);
+ vcard->details->load_file_handle = NULL;
+
+ /* make sure the read was successful */
+ if (result != GNOME_VFS_OK) {
+ g_assert (file_contents == NULL);
+ return;
+ }
+
+ /* free old data if any */
+ if (vcard->details->vcard_data) {
+ g_free (vcard->details->vcard_data);
+ }
+
+ /* set up the vcard data */
+ vcard->details->vcard_data = g_realloc (file_contents, file_size + 1);
+ vcard->details->vcard_data[file_size] = '\0';
+
+ /* extract the image uri and, if found, load it asynchronously */
+ logo_uri = vcard_logo (vcard->details->vcard_data);
+ g_message ("logo uri is %s", logo_uri);
+ if (logo_uri != NULL) {
+ vcard->details->load_image_handle = eel_gdk_pixbuf_load_async (logo_uri, vcard_logo_callback, vcard);
+ g_free (logo_uri);
+ }
+
+ /* schedule a redraw to reflect the new contents */
+ gtk_widget_queue_draw (GTK_WIDGET (vcard));
+}
+
+/* load the vcard asynchronously */
+static void
+load_vcard (NautilusVCard *vcard)
+{
+ /* load the uri asynchrounously, calling a completion routine when completed */
+ vcard->details->load_file_handle = eel_read_entire_file_async (vcard->details->vcard_uri, vcard_read_done_callback, vcard);
+}
+
+/* set the uri and load it */
+static void
+nautilus_vcard_set_uri (NautilusVCard *vcard, const char *uri)
+{
+
+ if (eel_strcmp (vcard->details->vcard_uri, uri) == 0) {
+ return;
+ }
+
+ if (vcard->details->vcard_uri != NULL) {
+ g_free (vcard->details->vcard_uri);
+ vcard->details->vcard_uri = NULL;
+ }
+
+ if (uri != NULL) {
+ vcard->details->vcard_uri = g_strdup (uri);
+ load_vcard (vcard);
+ }
+}
+
+/* convenience routine to composite an image with the proper clipping */
+static void
+vcard_pixbuf_composite (GdkPixbuf *source, GdkPixbuf *destination, int x_offset, int y_offset, int alpha)
+{
+ int source_width, source_height, dest_width, dest_height;
+ double float_x_offset, float_y_offset;
+
+ source_width = gdk_pixbuf_get_width (source);
+ source_height = gdk_pixbuf_get_height (source);
+ dest_width = gdk_pixbuf_get_width (destination);
+ dest_height = gdk_pixbuf_get_height (destination);
+
+ float_x_offset = x_offset;
+ float_y_offset = y_offset;
+
+ /* clip to the destination size */
+ if ((x_offset + source_width) > dest_width) {
+ source_width = dest_width - x_offset;
+ }
+ if ((y_offset + source_height) > dest_height) {
+ source_height = dest_height - y_offset;
+ }
+
+ gdk_pixbuf_composite (source, destination, x_offset, y_offset, source_width, source_height,
+ float_x_offset, float_y_offset, 1.0, 1.0, GDK_PIXBUF_ALPHA_BILEVEL, alpha);
+}
+
+
+
+/* draw the logo image */
+static int
+draw_vcard_logo_image (NautilusVCard *vcard, GdkPixbuf *pixbuf, int offset)
+{
+ GtkWidget *widget;
+ int logo_width, logo_height;
+ int v_offset;
+
+ widget = GTK_WIDGET (vcard);
+ v_offset = offset;
+
+ if (vcard->details->logo != NULL) {
+ logo_width = gdk_pixbuf_get_width (vcard->details->logo);
+ logo_height = gdk_pixbuf_get_height (vcard->details->logo);
+
+ vcard_pixbuf_composite (vcard->details->logo, pixbuf, 2, v_offset, 255);
+ v_offset += logo_height + 2;
+ }
+
+ return v_offset;
+}
+
+/* draw the name and title */
+static int
+draw_vcard_name_and_title (NautilusVCard *vcard, GdkPixbuf *pixbuf, int v_offset)
+{
+ int name_len, title_len;
+ char *name, *title;
+ GtkWidget *widget;
+ EelDimensions name_dimensions;
+ EelDimensions title_dimensions;
+
+ if (vcard->details->font == NULL) {
+ return v_offset;
+ }
+
+ widget = GTK_WIDGET (vcard);
+
+ /* extract the name and title */
+ name = vcard_full_name (vcard->details->vcard_data);
+ title = vcard_title (vcard->details->vcard_data);
+
+ /* first, measure the name */
+ if (name != NULL) {
+ name_len = strlen (name);
+ name_dimensions = eel_scalable_font_measure_text (vcard->details->font,
+ 18,
+ name, name_len);
+
+ /* draw the name into the pixbuf using anti-aliased text */
+ eel_scalable_font_draw_text (vcard->details->font, pixbuf,
+ 4, v_offset,
+ NULL,
+ 18,
+ name, name_len,
+ EEL_RGB_COLOR_BLACK,
+ EEL_OPACITY_FULLY_OPAQUE);
+ v_offset += name_dimensions.height + 4;
+
+ g_free (name);
+
+ if (title != NULL) {
+ title_len = strlen (title);
+ title_dimensions = eel_scalable_font_measure_text (vcard->details->font,
+ 14,
+ title, title_len);
+
+ /* draw the name into the pixbuf using anti-aliased text */
+ eel_scalable_font_draw_text (vcard->details->font, pixbuf,
+ 4, v_offset,
+ NULL,
+ 14,
+ title, title_len,
+ EEL_RGB_COLOR_BLACK,
+ EEL_OPACITY_FULLY_OPAQUE);
+ v_offset += title_dimensions.height + 4;
+
+ g_free (title);
+ }
+ }
+ return v_offset;
+}
+
+/* draw the addresses and phone numbers associated with the vcard */
+static int
+draw_vcard_addresses (NautilusVCard *vcard, GdkPixbuf *pixbuf, int v_offset)
+{
+ return v_offset;
+}
+
+/* handle drawing the control */
+static void
+nautilus_vcard_draw (GtkWidget *widget, GdkRectangle *box)
+{
+ NautilusVCard *control;
+ GdkPixbuf *temp_pixbuf;
+ int width, height, v_offset;
+
+ /* allocate a pixbuf to draw into */
+ width = widget->allocation.width;
+ height = widget->allocation.height;
+
+
+ temp_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, width, height);
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (NAUTILUS_IS_VCARD (widget));
+
+ control = NAUTILUS_VCARD (widget);
+
+ /* draw the background */
+ eel_gdk_pixbuf_fill_rectangle_with_color (temp_pixbuf, NULL, 0xFFEFEFEF);
+
+ /* draw the logo, if any */
+ v_offset = draw_vcard_logo_image (control, temp_pixbuf, 2);
+
+ /* draw the name and title */
+ v_offset = draw_vcard_name_and_title (control, temp_pixbuf, v_offset);
+
+ v_offset += 6;
+ /* draw the addresses */
+ v_offset = draw_vcard_addresses (control, temp_pixbuf, v_offset);
+
+ /* blit the resultingpixbuf to the drawable, then release it */
+ gdk_pixbuf_render_to_drawable_alpha (temp_pixbuf,
+ widget->window,
+ 0, 0,
+ widget->allocation.x, widget->allocation.y,
+ width, height,
+ GDK_PIXBUF_ALPHA_BILEVEL, 128,
+ GDK_RGB_DITHER_MAX,
+ 0, 0);
+
+ gdk_pixbuf_unref (temp_pixbuf);
+}
+
+/* handle expose events */
+static int
+nautilus_vcard_expose (GtkWidget *widget, GdkEventExpose *event)
+{
+ GdkRectangle box;
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (NAUTILUS_IS_VCARD (widget), FALSE);
+
+ box.x = 0; box.y = 0;
+ box.width = widget->allocation.width;
+ box.height = widget->allocation.height;
+
+ nautilus_vcard_draw (widget, &box);
+ return FALSE;
+}
+
+
+/* handle mouse motion events by maintaining the prelight state */
+static gboolean
+nautilus_vcard_motion_event (GtkWidget *widget, GdkEventMotion *event)
+{
+ int x, y;
+ int which_item, item_count;
+ NautilusVCard *vcard;
+
+ vcard = NAUTILUS_VCARD (widget);
+
+ gtk_widget_get_pointer (widget, &x, &y);
+ which_item = 0;
+ item_count = 0;
+
+ if (which_item < 0 || which_item >= item_count) {
+ which_item = -1;
+ }
+ return TRUE;
+}
+
+/* handle size requests by requesting a fixed size */
+static void
+nautilus_vcard_size_request (GtkWidget *widget, GtkRequisition *request)
+{
+ request->width = 220;
+ request->height = 140;
+}
+
+/* handle leave events by cancelling any prelighting */
+static gboolean
+nautilus_vcard_leave_event (GtkWidget *widget, GdkEventCrossing *event)
+{
+ NautilusVCard *vcard;
+
+ vcard = NAUTILUS_VCARD (widget);
+ return TRUE;
+}
+
+/* handle button press events */
+static gboolean
+nautilus_vcard_button_press_event (GtkWidget *widget, GdkEventButton *event)
+{
+ GList *selected_item;
+ NautilusVCard *vcard;
+ char *command;
+ int result, which_item;
+
+ vcard = NAUTILUS_VCARD (widget);
+ if (event->y < widget->allocation.y) {
+ command = g_strdup_printf ("nautilus %s", vcard->details->vcard_uri);
+ result = system (command);
+ g_free (command);
+ } else {
+ which_item = (event->y - widget->allocation.y ) / 16;
+ if (which_item < 0) {
+ selected_item = 0;
+
+ }
+ }
+
+ return FALSE;
+}
+
diff --git a/components/vcard/nautilus-vcard.h b/components/vcard/nautilus-vcard.h
new file mode 100644
index 000000000..4a742dd26
--- /dev/null
+++ b/components/vcard/nautilus-vcard.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Copyright (C) 2000 Eazel, Inc
+ *
+ * This program 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; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Andy Hertzfeld
+ */
+
+/* header file for the vcard component */
+
+#ifndef NAUTILUS_VCARD_H
+#define NAUTILUS_VCARD_H
+
+#include <bonobo.h>
+#include <gtk/gtkeventbox.h>
+typedef struct _NautilusVCard NautilusVCard;
+typedef struct _NautilusVCardClass NautilusVCardClass;
+
+#define NAUTILUS_TYPE_VCARD (nautilus_vcard_get_type ())
+#define NAUTILUS_VCARD(obj) (GTK_CHECK_CAST ((obj), NAUTILUS_TYPE_VCARD, NautilusVCard))
+#define NAUTILUS_VCARD_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), NAUTILUS_TYPE_VCARD, NautilusVCardClass))
+#define NAUTILUS_IS_VCARD(obj) (GTK_CHECK_TYPE ((obj), NAUTILUS_TYPE_VCARD))
+#define NAUTILUS_IS_VCARD_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_VCARD))
+
+typedef struct _NautilusVCardDetails NautilusVCardDetails;
+
+struct _NautilusVCard {
+ GtkEventBox parent;
+ NautilusVCardDetails *details;
+};
+
+struct _NautilusVCardClass {
+ GtkEventBoxClass parent_class;
+};
+
+/* GtkObject support */
+GtkType nautilus_vcard_get_type (void);
+BonoboObject* nautilus_vcard_get_control (NautilusVCard *vcard);
+
+#endif /* NAUTILUS_VCARD_H */
diff --git a/components/vcard/nautilus-vcard.oaf b/components/vcard/nautilus-vcard.oaf
new file mode 100644
index 000000000..bb6edd1ea
--- /dev/null
+++ b/components/vcard/nautilus-vcard.oaf
@@ -0,0 +1,20 @@
+<oaf_info>
+
+<oaf_server iid="OAFIID:nautilus_vcard_factory" type="exe" location="nautilus-rss-control">
+<oaf_attribute name="repo_ids" type="stringv">
+<item value="IDL:Bonobo/GenericFactory:1.0"/>
+</oaf_attribute>
+<oaf_attribute name="name" type="string" value="vcard factory"/>
+<oaf_attribute name="description" type="string" value="vcard object factory"/>
+</oaf_server>
+
+<oaf_server iid="OAFIID:nautilus_vcard" type="factory" location="OAFIID:nautilus_vcard_factory">
+<oaf_attribute name="repo_ids" type="stringv">
+<item value="IDL:Bonobo/Control:1.0"/>
+<item value="IDL:Bonobo/Unknown:1.0"/>
+</oaf_attribute>
+<oaf_attribute name="name" type="string" value="vcard"/>
+<oaf_attribute name="description" type="string" value="nautilus vcard object"/>
+</oaf_server>
+
+</oaf_info>
diff --git a/components/vcard/vcard.c b/components/vcard/vcard.c
new file mode 100644
index 000000000..58000315e
--- /dev/null
+++ b/components/vcard/vcard.c
@@ -0,0 +1,276 @@
+/* ad hoc vcard parsing routines for prototyping - replace with something better soon
+ *
+ * Copyright (C) 1999 Andy Hertzfeld
+ *
+ * This program 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <gnome.h>
+#include <ctype.h>
+
+#include "vcard.h"
+
+char * vcard_full_name (gchar* vcard)
+ {
+ gchar full_name[2048];
+ gchar *temp_str = strstr(vcard, "FN:");
+ if (temp_str)
+ {
+ gint str_len;
+ gchar *end_pos = strchr(temp_str, '\n');
+ if (!end_pos)
+ end_pos = temp_str + strlen(temp_str);
+
+ str_len = end_pos - temp_str - 3;
+ memcpy(full_name, temp_str + 3, str_len);
+ full_name[str_len] = '\0';
+ return g_strdup (full_name);
+ }
+ else
+ return NULL;
+ }
+
+char * vcard_title (gchar* vcard)
+ {
+ gchar title[2048];
+ gchar *temp_str = strstr(vcard, "TITLE:");
+ if (temp_str)
+ {
+ gint str_len;
+ gchar *end_pos = strchr(temp_str, '\n');
+ if (!end_pos)
+ end_pos = temp_str + strlen(temp_str);
+
+ str_len = end_pos - temp_str - 6;
+ memcpy(title, temp_str + 6, str_len);
+ title[str_len] = '\0';
+ return g_strdup (title);
+ }
+ else
+ return NULL;
+ }
+
+char * vcard_logo (gchar* vcard)
+ {
+ gchar logo[2048];
+ gchar *temp_str = strstr(vcard, "LOGO;VALUE=uri:");
+ if (temp_str)
+ {
+ gint str_len;
+ gchar *end_pos = strchr(temp_str, '\n');
+ if (!end_pos)
+ end_pos = temp_str + strlen(temp_str);
+
+ str_len = end_pos - temp_str - 15;
+ memcpy(logo, temp_str + 15, str_len);
+ logo[str_len] = '\0';
+ return g_strdup (logo);
+ }
+ else
+ return NULL;
+ }
+
+char * vcard_postal (gchar* vcard)
+ {
+ gchar postal[2048];
+ gchar *temp_str = strstr(vcard, "ADR;");
+ if (temp_str)
+ {
+ gchar *skip_pos = strchr(temp_str, ':');
+ if (skip_pos)
+ {
+ gint str_len;
+ gchar *end_pos = strchr(skip_pos, '\n');
+ if (!end_pos)
+ end_pos = skip_pos + strlen(skip_pos);
+
+ str_len = end_pos - skip_pos - 1;
+ memcpy(postal, skip_pos + 1, str_len);
+ postal[str_len] = '\0';
+ return g_strdup (postal);
+ }
+ else
+ return NULL;
+ }
+ else
+ return NULL;
+ }
+
+char * vcard_streetaddress(gchar* vcard)
+ {
+ gchar streetaddress[2048];
+ gchar *temp_str = strstr(vcard, "ADR;");
+ if (temp_str)
+ {
+ gchar *skip_pos = strchr(temp_str, ':');
+ if (skip_pos)
+ {
+ gint str_len;
+ gchar *end_pos;
+ gint semi_count = 0;
+
+ /* to extract the street address, count up two semi-colons, then scan until the next one */
+
+ while (*skip_pos && (semi_count < 2))
+ {
+ if (*skip_pos++ == ';')
+ semi_count += 1;
+ }
+
+ end_pos = strchr(skip_pos, ';');
+ if (!end_pos)
+ end_pos = skip_pos + strlen(skip_pos);
+
+ str_len = end_pos - skip_pos;
+
+ memcpy(streetaddress, skip_pos, str_len);
+ streetaddress[str_len] = '\0';
+ return g_strdup (streetaddress);
+ }
+ else
+ return NULL;
+ }
+ else
+ return NULL;
+ }
+
+char * vcard_city_state_zip(gchar* vcard)
+ {
+ gchar city[2048];
+ gchar *temp_str = strstr(vcard, "ADR;");
+ if (temp_str)
+ {
+ gchar *skip_pos = strchr(temp_str, ':');
+ if (skip_pos)
+ {
+ gint str_len;
+ gchar *end_pos;
+ gint semi_count = 0;
+
+ /* to extract the city, state and zip, count up 3 semi-colons */
+
+ while (*skip_pos && (semi_count < 3))
+ {
+ if (*skip_pos++ == ';')
+ semi_count += 1;
+ }
+
+ end_pos = skip_pos + strlen(skip_pos);
+
+ str_len = end_pos - skip_pos;
+ memcpy(city, skip_pos, str_len);
+ city[str_len] = '\0';
+ return g_strdup (city);
+ }
+ else
+ return NULL;
+ }
+ else
+ return NULL;
+ }
+
+char * vcard_company(gchar* vcard)
+ {
+ gchar company[2048];
+ gchar *temp_str = strstr(vcard, "ORG:");
+ if (temp_str)
+ {
+ gint str_len;
+ gchar *end_pos = strchr(temp_str, '\n');
+ if (!end_pos)
+ end_pos = temp_str + strlen(temp_str);
+
+ str_len = end_pos - temp_str - 4;
+ memcpy(company, temp_str + 4, str_len);
+ company[str_len] = '\0';
+ return g_strdup (company);
+ }
+ else
+ return NULL;
+ }
+
+char * vcard_email(gchar* vcard)
+ {
+ return NULL;
+ }
+
+char * vcard_fax(gchar* vcard)
+ {
+ return NULL;
+ }
+
+char * vcard_web(gchar* vcard)
+ {
+ return NULL;
+ }
+
+/* this routine returns a cr-delimited list of all the phone, email and web addresses it can find */
+
+char * vcard_address_list(gchar* vcard)
+ {
+ gchar *next_line, *scan_ptr;
+ gchar temp_card[16384];
+
+ strcpy(temp_card, vcard);
+
+ /* iterate through the vcard text, extracting the fields that we're interested in and building the returned list */
+
+ next_line = strtok(temp_card, "\n");
+ while (next_line != NULL)
+ {
+ /* skip over leading blanks, and find the tag delimiter */
+
+ next_line = g_strchug(next_line);
+ scan_ptr = next_line;
+
+ while (isalpha (*scan_ptr))
+ scan_ptr++;
+
+ if ((*scan_ptr == ';') || (*scan_ptr == ':'))
+ {
+ gchar delimiter = *scan_ptr;
+ *scan_ptr = '\0';
+
+ /* see if it's one of the tags that we collect */
+
+ if (!strcmp(next_line, "TEL") || !strcmp(next_line, "EMAIL") || !strcmp(next_line, "URL"))
+ {
+ gchar temp_address[2048];
+
+ /* it matched, s prepare the output string and append it to the result */
+
+ if (delimiter == ':')
+ sprintf(temp_address, "%s||%s\n", next_line, scan_ptr + 1);
+ else
+ {
+ gchar *sub_type_ptr;
+
+ scan_ptr += 1;
+ sub_type_ptr = scan_ptr;
+ while ((*scan_ptr != ':') && (*scan_ptr != '\0'))
+ scan_ptr += 1;
+ *scan_ptr = '\0';
+ sprintf(temp_address, "%s|%s|%s\n", next_line, sub_type_ptr, scan_ptr + 1);
+ }
+ /*
+ strcat(address_list, temp_address);
+ */
+ }
+ }
+ next_line = strtok(NULL, "\n");
+ }
+
+ return NULL;
+ }
diff --git a/components/vcard/vcard.h b/components/vcard/vcard.h
new file mode 100644
index 000000000..0eeaff8b2
--- /dev/null
+++ b/components/vcard/vcard.h
@@ -0,0 +1,15 @@
+
+/* ad hoc vcard parsing routines - replace with something better sometime */
+
+char * vcard_full_name (gchar* vcard);
+char * vcard_title(gchar* vcard);
+char * vcard_postal(gchar* vcard);
+char * vcard_email(gchar* vcard);
+char * vcard_fax(gchar* vcard);
+char * vcard_web(gchar* vcard);
+char * vcard_logo(gchar* vcard);
+char * vcard_streetaddress(gchar* vcard);
+char * vcard_city_state_zip(gchar* vcard);
+char * vcard_company(gchar* vcard);
+char * vcard_address_list(gchar* vcard);
+
diff --git a/configure.in b/configure.in
index 1c7e7adcc..fbd03a01f 100644
--- a/configure.in
+++ b/configure.in
@@ -937,6 +937,7 @@ components/music/Makefile
components/notes/Makefile
components/sample/Makefile
components/mozilla/Makefile
+components/rss-control/Makefile
components/text/Makefile
components/text/services/Makefile
components/throbber/Makefile
@@ -944,6 +945,7 @@ components/loser/Makefile
components/loser/content/Makefile
components/loser/sidebar/Makefile
components/tree/Makefile
+components/vcard/Makefile
po/Makefile.in
intl/Makefile
test/Makefile
diff --git a/icons/Makefile.am b/icons/Makefile.am
index 8b35d7f43..c7075d8a2 100644
--- a/icons/Makefile.am
+++ b/icons/Makefile.am
@@ -22,6 +22,7 @@ icon_DATA =\
bubble-LR.png \
bubble-UL.png \
bubble-UR.png \
+ bullet.png \
chit_frame.png \
colors.png \
computer.png \
@@ -43,6 +44,7 @@ icon_DATA =\
emblem-important.svg \
emblem-new.svg \
emblem-noread.svg \
+ emblem-note.png \
emblem-nowrite.svg \
emblem-ohno.svg \
emblem-personal.svg \
diff --git a/icons/emblem-note.png b/icons/emblem-note.png
new file mode 100644
index 000000000..54681aea6
--- /dev/null
+++ b/icons/emblem-note.png
Binary files differ
diff --git a/libnautilus-extensions/Makefile.am b/libnautilus-extensions/Makefile.am
index c0094f54d..515fdce88 100644
--- a/libnautilus-extensions/Makefile.am
+++ b/libnautilus-extensions/Makefile.am
@@ -38,6 +38,7 @@ endif
libnautilus_extensions_la_LDFLAGS = \
$(dependency_static_libs) \
+ $(AMMONITE_LIBS) \
$(BONOBO_PRINT_LIBS) \
$(BONOBOX_LIBS) \
$(ESD_LIBS) \
@@ -63,9 +64,11 @@ nautilus_metafile_server_idl_sources = \
libnautilus_extensions_la_SOURCES = \
$(nautilus_metafile_server_idl_sources) \
+ nautilus-annotation.c \
nautilus-audio-player.c \
nautilus-bonobo-extensions.c \
nautilus-bookmark.c \
+ nautilus-canvas-note-item.c \
nautilus-customization-data.c \
nautilus-dateedit-extensions.c \
nautilus-default-file-icon.c \
@@ -130,9 +133,11 @@ libnautilus_extensions_la_SOURCES = \
# Everything is private for now
noinst_HEADERS = \
+ nautilus-annotation.h \
nautilus-audio-player.h \
nautilus-bonobo-extensions.h \
nautilus-bookmark.h \
+ nautilus-canvas-note-item.h \
nautilus-cdrom-extensions.h \
nautilus-customization-data.h \
nautilus-dateedit-extensions.h \
diff --git a/libnautilus-extensions/nautilus-annotation.c b/libnautilus-extensions/nautilus-annotation.c
new file mode 100644
index 000000000..07a2d8512
--- /dev/null
+++ b/libnautilus-extensions/nautilus-annotation.c
@@ -0,0 +1,1312 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+ nautilus-annotation.c: routines for getting and setting xml-based annotations associated
+ with the digest of a file.
+
+ Copyright (C) 2001 Eazel, Inc.
+
+ This program 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; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program 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. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Andy Hertzfeld <andy@eazel.com>
+*/
+
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to md5_init, call md5_update as
+ * needed on buffers full of bytes, and then call md5_Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+/* parts of this file are :
+ * Written March 1993 by Branko Lankester
+ * Modified June 1993 by Colin Plumb for altered md5.c.
+ * Modified October 1995 by Erik Troan for RPM
+ */
+
+#include <config.h>
+#include "nautilus-annotation.h"
+
+#include "nautilus-file-utilities.h"
+#include "nautilus-file.h"
+#include "nautilus-file-private.h"
+#include "nautilus-global-preferences.h"
+#include "nautilus-metadata.h"
+#include "nautilus-preferences.h"
+#include <eel/eel-string.h>
+#include <eel/eel-xml-extensions.h>
+#include <eel/eel-vfs-extensions.h>
+#include <ghttp.h>
+#include <gnome-xml/parser.h>
+#include <gnome-xml/xmlmemory.h>
+#include <libgnome/gnome-util.h>
+#include <libgnomevfs/gnome-vfs.h>
+#include <libtrilobite/libammonite.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* icon selection callback function. */
+typedef void (* NautilusCalculateDigestCallback) (NautilusFile *file, char *file_digest);
+typedef struct NautilusDigestFileHandle NautilusDigestFileHandle;
+
+typedef struct {
+ guint32 buf[4];
+ guint32 bits[2];
+ guchar in[64];
+ int doByteReverse;
+} MD5Context ;
+
+struct NautilusDigestFileHandle {
+ GnomeVFSAsyncHandle *handle;
+ NautilusCalculateDigestCallback callback;
+ NautilusFile *file;
+ char *buffer;
+ gboolean opened;
+ MD5Context digest_context;
+};
+
+#define READ_CHUNK_SIZE 65536
+#define MAX_DIGESTS_IN_PROGRESS 16
+#define SERVER_URI_TEMPLATE "http://dellbert.differnet.com/get_notes.cgi?ids=%s"
+#define SERVER_POST_URI "http://dellbert.differnet.com/set_notes.cgi"
+#define NOTES_LOOKUP_INTERVAL 3600
+
+static int open_count = 0;
+static int close_count = 0;
+static int digests_in_progress = 0;
+
+static GList* digest_request_queue = NULL;
+static GList* annotation_request_queue = NULL;
+
+static GHashTable *files_awaiting_annotation = NULL;
+
+static void md5_transform (guint32 buf[4], const guint32 in[16]);
+
+static int _ie = 0x44332211;
+static union _endian { int i; char b[4]; } *_endian = (union _endian *)&_ie;
+#define IS_BIG_ENDIAN() (_endian->b[0] == '\x44')
+#define IS_LITTLE_ENDIAN() (_endian->b[0] == '\x11')
+
+static void got_file_digest (NautilusFile *file, const char *file_digest);
+static void process_digest_requests (void);
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void
+_byte_reverse (guchar *buf, guint32 longs)
+{
+ guint32 t;
+ do {
+ t = (guint32) ((guint32) buf[3] << 8 | buf[2]) << 16 |
+ ((guint32) buf[1] << 8 | buf[0]);
+ *(guint32 *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+
+/**
+ * md5_init: Initialise an md5 context object
+ * @ctx: md5 context
+ *
+ * Initialise an md5 buffer.
+ *
+ **/
+static void
+md5_init (MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+
+ if (IS_BIG_ENDIAN())
+ ctx->doByteReverse = 1;
+ else
+ ctx->doByteReverse = 0;
+}
+
+
+
+/**
+ * md5_update: add a buffer to md5 hash computation
+ * @ctx: conetxt object used for md5 computaion
+ * @buf: buffer to add
+ * @len: buffer length
+ *
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes. Use this to progressively construct an md5 hash.
+ **/
+static void
+md5_update (MD5Context *ctx, const guchar *buf, guint32 len)
+{
+ guint32 t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((guint32) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ guchar *p = (guchar *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy (p, buf, len);
+ return;
+ }
+ memcpy (p, buf, t);
+ if (ctx->doByteReverse)
+ _byte_reverse (ctx->in, 16);
+ md5_transform (ctx->buf, (guint32 *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy (ctx->in, buf, 64);
+ if (ctx->doByteReverse)
+ _byte_reverse (ctx->in, 16);
+ md5_transform (ctx->buf, (guint32 *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy (ctx->in, buf, len);
+}
+
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+/**
+ * md5_final: copy the final md5 hash to a bufer
+ * @digest: 16 bytes buffer
+ * @ctx: context containing the calculated md5
+ *
+ * copy the final md5 hash to a bufer
+ **/
+static void
+md5_final (MD5Context *ctx, guchar digest[16])
+{
+ guint32 count;
+ guchar *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset (p, 0, count);
+ if (ctx->doByteReverse)
+ _byte_reverse (ctx->in, 16);
+ md5_transform (ctx->buf, (guint32 *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset (ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset (p, 0, count - 8);
+ }
+ if (ctx->doByteReverse)
+ _byte_reverse (ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((guint32 *) ctx->in)[14] = ctx->bits[0];
+ ((guint32 *) ctx->in)[15] = ctx->bits[1];
+
+ md5_transform (ctx->buf, (guint32 *) ctx->in);
+ if (ctx->doByteReverse)
+ _byte_reverse ((guchar *) ctx->buf, 4);
+ memcpy (digest, ctx->buf, 16);
+}
+
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. md5_Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void
+md5_transform (guint32 buf[4], const guint32 in[16])
+{
+ register guint32 a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP (F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP (F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP (F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP (F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP (F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP (F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP (F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP (F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP (F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP (F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP (F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP (F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP (F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP (F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP (F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP (F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP (F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP (F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP (F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP (F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP (F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP (F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP (F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP (F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP (F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP (F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP (F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP (F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP (F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP (F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP (F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP (F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP (F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP (F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP (F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP (F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP (F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP (F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP (F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP (F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP (F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP (F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP (F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP (F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP (F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP (F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP (F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP (F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP (F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP (F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP (F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP (F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP (F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP (F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP (F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP (F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP (F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP (F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP (F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP (F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP (F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP (F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP (F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP (F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+
+
+/* When close is complete, there's no more work to do. */
+static void
+digest_file_close_callback (GnomeVFSAsyncHandle *handle,
+ GnomeVFSResult result,
+ gpointer callback_data)
+{
+ close_count += 1;
+ g_message ("opened %d, closed %d", open_count, close_count);
+}
+
+/* Close the file and then tell the caller we succeeded, handing off
+ * the buffer to the caller.
+ */
+static void
+digest_file_completed (NautilusDigestFileHandle *digest_handle)
+{
+ guchar digest_result[16];
+ char digest_string [33];
+ char* hex_string = "0123456789abcdef";
+ int index, result_index;
+ int current_value;
+
+ if (digest_handle->opened) {
+
+ gnome_vfs_async_close (digest_handle->handle,
+ digest_file_close_callback,
+ NULL);
+ }
+
+ digests_in_progress -= 1;
+
+ /* Invoke the callback to continue processing the annotation */
+ md5_final (&digest_handle->digest_context, digest_result);
+
+ /* make a hex string for the digest result */
+ digest_string[32] = '\0';
+ for (index = 0; index < 32; index++) {
+ current_value = digest_result[index >> 1];
+ if (index & 1) {
+ result_index = current_value & 15;
+ } else {
+ result_index = (current_value >> 4) & 15;
+ }
+
+ digest_string[index] = hex_string[result_index];
+ }
+
+ (* digest_handle->callback) (digest_handle->file, &digest_string[0]);
+
+ nautilus_file_unref (digest_handle->file);
+ g_free (digest_handle->buffer);
+ g_free (digest_handle);
+
+ /* start new digest requests if necessary */
+ process_digest_requests ();
+
+}
+
+/* Tell the caller we failed. */
+static void
+digest_file_failed (NautilusDigestFileHandle *digest_handle, GnomeVFSResult result)
+{
+ if (digest_handle->opened) {
+ gnome_vfs_async_close (digest_handle->handle,
+ digest_file_close_callback,
+ NULL);
+ }
+ g_free (digest_handle->buffer);
+
+ digests_in_progress -= 1;
+
+ (* digest_handle->callback) (digest_handle->file, NULL);
+
+ nautilus_file_unref (digest_handle->file);
+ g_free (digest_handle);
+
+ /* start new digest requests if necessary */
+ process_digest_requests ();
+}
+
+/* Here is the callback from the file read routine, where we actually accumulate the checksum */
+static void
+calculate_checksum_callback (GnomeVFSAsyncHandle *handle,
+ GnomeVFSResult result,
+ gpointer buffer,
+ GnomeVFSFileSize bytes_requested,
+ GnomeVFSFileSize bytes_read,
+ gpointer callback_data)
+{
+ NautilusDigestFileHandle *digest_handle;
+
+ /* Do a few reality checks. */
+ g_assert (bytes_requested == READ_CHUNK_SIZE);
+
+ digest_handle = callback_data;
+ g_assert (digest_handle->handle == handle);
+ g_assert (bytes_read <= bytes_requested);
+
+ /* Check for a failure. */
+ if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_EOF) {
+ digest_file_failed (digest_handle, result);
+ return;
+ }
+
+ /* accumulate the recently read data into the checksum */
+ if (bytes_read > 0) {
+ md5_update (&digest_handle->digest_context, buffer, bytes_read);
+ }
+
+ /* Read more unless we are at the end of the file. */
+ if (bytes_read > 0 && result == GNOME_VFS_OK) {
+ gnome_vfs_async_read (digest_handle->handle,
+ digest_handle->buffer,
+ READ_CHUNK_SIZE,
+ calculate_checksum_callback,
+ digest_handle);
+ } else {
+ digest_file_completed (digest_handle);
+ }
+}
+
+/* Once the open is finished, read a first chunk. */
+static void
+read_file_open_callback (GnomeVFSAsyncHandle *handle,
+ GnomeVFSResult result,
+ gpointer callback_data)
+{
+ NautilusDigestFileHandle *digest_handle;
+ char *name;
+
+ digest_handle = callback_data;
+ g_assert (digest_handle->handle == handle);
+
+ /* Handle the failure case. */
+ if (result != GNOME_VFS_OK) {
+ name = nautilus_file_get_name (digest_handle->file);
+ g_message ("open failed, filename %s, error was %d", name, result);
+ g_free (name);
+ digest_file_failed (digest_handle, result);
+ return;
+ }
+
+ /* read in the first chunk of the file */
+ digest_handle->opened = TRUE;
+ open_count += 1;
+ gnome_vfs_async_read (digest_handle->handle,
+ digest_handle->buffer,
+ READ_CHUNK_SIZE,
+ calculate_checksum_callback,
+ digest_handle);
+}
+
+/* calculate the digest for the passed-in file asynchronously, invoking the passed in
+ * callback when the calculation has been completed.
+ */
+static NautilusDigestFileHandle*
+calculate_file_digest (NautilusFile *file, NautilusCalculateDigestCallback callback)
+{
+ NautilusDigestFileHandle *handle;
+ char *uri;
+
+
+ /* allocate a digest-handle structure to keep our state */
+
+ handle = g_new0 (NautilusDigestFileHandle, 1);
+ uri = nautilus_file_get_uri (file);
+
+ handle->callback = callback;
+ handle->opened = FALSE;
+ handle->file = file;
+ nautilus_file_ref (file);
+
+ /* allocate the buffer */
+ handle->buffer = g_malloc (READ_CHUNK_SIZE);
+
+ /* initialize the MD5 stuff */
+ md5_init (&handle->digest_context);
+
+ /* open the file */
+ gnome_vfs_async_open (&handle->handle,
+ uri,
+ GNOME_VFS_OPEN_READ,
+ read_file_open_callback,
+ handle);
+ g_free (uri);
+ return handle;
+}
+
+/* process the digest request queue, launching as many requests as we can handle */
+static void
+process_digest_requests (void)
+{
+ GList *current_entry;
+ NautilusFile *file;
+
+ while (digests_in_progress < MAX_DIGESTS_IN_PROGRESS && digest_request_queue != NULL)
+ {
+ /* pull entry off queue */
+ current_entry = digest_request_queue;
+ digest_request_queue = current_entry->next;
+
+ file = NAUTILUS_FILE (current_entry->data);
+
+ /* initiate request */
+ calculate_file_digest (file, (NautilusCalculateDigestCallback) got_file_digest);
+
+ /* dispose of queue entry */
+ nautilus_file_unref (file);
+
+ g_list_free_1 (current_entry);
+ digests_in_progress += 1;
+ }
+}
+
+/* queue the digest request, and start processing it if we haven't exceeded the limit of requests
+ * in progress
+ */
+static void
+queue_file_digest_request (NautilusFile *file)
+{
+ /* if annotation lookup is disabled, don't bother to do all this work */
+ if (!nautilus_preferences_get_boolean (NAUTILUS_PREFERENCES_LOOKUP_ANNOTATIONS)) {
+ return;
+ }
+ nautilus_file_ref (file);
+ digest_request_queue = g_list_append (digest_request_queue, file);
+ process_digest_requests ();
+}
+
+/* given a digest, retrieve an associated file object from the hash table */
+static NautilusFile *
+get_file_from_digest (const char *digest)
+{
+ if (files_awaiting_annotation == NULL) {
+ return NULL;
+ }
+
+ return g_hash_table_lookup (files_awaiting_annotation, digest);
+}
+
+/* given a digest value, return the path to it in the local cache */
+static char *
+get_annotation_path (const char *digest)
+{
+ char *user_directory, *annotation_directory;
+ char *annotation_path, *directory_uri;
+
+ user_directory = nautilus_get_user_directory ();
+ annotation_directory = nautilus_make_path (user_directory, "annotations");
+ annotation_path = nautilus_make_path (annotation_directory, digest);
+
+ /* create the annotation directory if it doesn't exist */
+ if (!g_file_exists (annotation_directory)) {
+ directory_uri = gnome_vfs_get_uri_from_local_path (annotation_directory);
+ gnome_vfs_make_directory (directory_uri,
+ GNOME_VFS_PERM_USER_ALL
+ | GNOME_VFS_PERM_GROUP_ALL
+ | GNOME_VFS_PERM_OTHER_READ);
+ g_free (directory_uri);
+ }
+
+ /* free up the intermediate strings and return the complete path */
+ g_free (user_directory);
+ g_free (annotation_directory);
+
+ return annotation_path;
+}
+
+/* look up the passed-in digest in the local annotation cache */
+static char *
+look_up_local_annotation (NautilusFile *file, const char *digest)
+{
+ GnomeVFSResult result;
+ int file_size;
+ char *uri, *path, *file_data, *buffer;
+
+ path = get_annotation_path (digest);
+ if (g_file_exists (path)) {
+ /* load the file and return it */
+ uri = gnome_vfs_get_uri_from_local_path (path);
+ result = eel_read_entire_file (uri, &file_size, &file_data);
+ g_free (uri);
+ g_free (path);
+ if (result == GNOME_VFS_OK) {
+ /* add a null at the end, so it's a valid string */
+ buffer = g_realloc (file_data, file_size + 1);
+ buffer[file_size] = '\0';
+ return buffer;
+ } else {
+ return NULL;
+ }
+ }
+ g_free (path);
+ return NULL;
+}
+
+static gboolean
+has_local_annotation (const char *digest)
+{
+ gboolean has_annotation;
+ char *path;
+
+ path = get_annotation_path (digest);
+ has_annotation = g_file_exists (path);
+
+ g_free (path);
+ return has_annotation;
+}
+
+/* utility routine to save the passed-in xml document as a local annotations file */
+static void
+save_local_annotations (xmlDocPtr document, const char *digest)
+{
+ char *path;
+
+ path = get_annotation_path (digest);
+ xmlSaveFile (path, document);
+
+ g_free (path);
+}
+
+/* utility routine to add the passed-in xml node to the file associated with the passed-in
+ * digest. If there isn't a file, create one
+ */
+static void
+add_annotations_to_file (xmlNodePtr node_ptr, const char *digest)
+{
+ char *digest_path;
+ xmlDocPtr document;
+
+ digest_path = get_annotation_path (digest);
+
+ /* save the subtree as a new document, by making a new document and adding the new node */
+ document = xmlNewDoc ("1.0");
+ xmlDocSetRootElement (document, node_ptr);
+
+ /* save the xml tree as a file in the cache area */
+ xmlSaveFile (digest_path, document);
+
+ xmlFreeDoc (document);
+ g_free (digest_path);
+}
+
+/* remember the file object by adding it to a hash table */
+static void
+remember_file (NautilusFile *file, const char *digest)
+{
+ nautilus_file_ref (file);
+
+ if (files_awaiting_annotation == NULL) {
+ files_awaiting_annotation = g_hash_table_new (g_str_hash, g_str_equal);
+ /* g_atexit (annotations_file_table_free); */
+ }
+
+ g_hash_table_insert (files_awaiting_annotation, g_strdup (digest), file);
+}
+
+/* forget a file when we're done with it by removing it from the table */
+static void
+forget_file (const char *digest)
+{
+ NautilusFile *file;
+ if (files_awaiting_annotation == NULL) {
+ return;
+ }
+
+ file = g_hash_table_lookup (files_awaiting_annotation, digest);
+ if (file != NULL) {
+ nautilus_file_unref (file);
+ g_hash_table_remove (files_awaiting_annotation, digest);
+ }
+}
+
+/* completion routine invoked when we've loaded the an annotation file from the service.
+ * We must parse it, and walk through it to save the annotations in the local cache.
+ */
+static void
+got_annotations_callback (GnomeVFSResult result,
+ GnomeVFSFileSize file_size,
+ char *file_contents,
+ gpointer callback_data)
+{
+ NautilusFile *file;
+ xmlDocPtr annotations;
+ xmlNodePtr next_annotation, item;
+ xmlNodePtr saved_annotation;
+ int annotation_count;
+ char *buffer, *digest, *info_str;
+ time_t date_stamp;
+
+ /* exit if there was an error */
+ if (result != GNOME_VFS_OK) {
+ g_assert (file_contents == NULL);
+ return;
+ }
+
+ /* inexplicably, the gnome-xml parser requires a zero-terminated array, so add the null at the end. */
+ buffer = g_realloc (file_contents, file_size + 1);
+ buffer[file_size] = '\0';
+ annotations = xmlParseMemory (buffer, file_size);
+ g_free (buffer);
+
+ /* iterate through the xml document, handling each annotation entry */
+ if (annotations != NULL) {
+ next_annotation = xmlDocGetRootElement (annotations)->childs;
+ while (next_annotation != NULL) {
+ if (eel_strcmp (next_annotation->name, "annotations") == 0) {
+ /* get the digest associated with the annotations */
+ digest = xmlGetProp (next_annotation, "digest");
+ if (digest != NULL) {
+ /* count the number of annotations contained in the node */
+ annotation_count = 0;
+ item = next_annotation->childs;
+ while (item != NULL) {
+ if (eel_strcmp (item->name, "annotation") == 0) {
+ annotation_count += 1;
+ }
+ item = item->next;
+ }
+
+ /* write the annotation out to our cache area, if necessary */
+ if (annotation_count > 0) {
+ saved_annotation = xmlCopyNode (next_annotation, TRUE);
+ add_annotations_to_file (saved_annotation, digest);
+ }
+
+ /* retrieve the file object, and update it's count and time stamp */
+
+ file = get_file_from_digest (digest);
+ time (&date_stamp);
+ info_str = g_strdup_printf ("%lu:%d", date_stamp, annotation_count);
+
+ nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_NOTES_INFO, NULL, info_str);
+ g_free (info_str);
+
+ /* issue the changed signal */
+ nautilus_file_emit_changed (file);
+
+ /* remove the file from the hash table and unref it */
+ forget_file (digest);
+ xmlFree (digest);
+ }
+ }
+ next_annotation = next_annotation->next;
+ }
+
+
+ /* free the xml document */
+ xmlFreeDoc (annotations);
+ }
+}
+
+/* format the request, and send it to the server */
+/* the first cut implementation simply sends the digests as a cgi parameter,
+ * but soon we'll want use SOAP or XML-RPC
+ */
+static void
+fetch_annotations_from_server (void)
+{
+ GString *temp_string;
+ GList *current_entry, *save_entry;
+ char *uri;
+
+ /* check to see if there are enough requests, or a long enough delay since the last one */
+
+ current_entry = annotation_request_queue;
+ save_entry = current_entry;
+ annotation_request_queue = NULL;
+
+ /* simple cgi-based request format passed the digests as part of the uri, so
+ * gather the variable parts
+ */
+ temp_string = g_string_new ("");
+ while (current_entry != NULL) {
+ g_string_append (temp_string, (char*) current_entry->data);
+ if (current_entry->next != NULL) {
+ g_string_append (temp_string, ",");
+ }
+ current_entry = current_entry->next;
+ }
+
+
+ uri = g_strdup_printf (SERVER_URI_TEMPLATE, temp_string->str);
+ g_string_free (temp_string, TRUE);
+ eel_g_list_free_deep (save_entry);
+
+ /* read the result from the server asynchronously */
+ eel_read_entire_file_async (uri, got_annotations_callback, NULL);
+ g_free (uri);
+}
+
+
+/* ask the server for an annotation asynchronously */
+static void
+get_annotation_from_server (NautilusFile *file, const char *file_digest)
+{
+ /* see if there's a request for this one already pending - if so, we can return */
+ if (get_file_from_digest (file_digest) != NULL) {
+ return;
+ }
+
+ /* only do this if lookups are enabled */
+ /* if annotation lookup is disabled, don't bother to do all this work */
+ if (!nautilus_preferences_get_boolean (NAUTILUS_PREFERENCES_LOOKUP_ANNOTATIONS)) {
+ return;
+ }
+
+ /* add the request to the queue, and kick it off it there's enough of them */
+ annotation_request_queue = g_list_prepend (annotation_request_queue, g_strdup (file_digest));
+
+ remember_file (file, file_digest);
+ fetch_annotations_from_server ();
+}
+
+/* callback that's invokes when we've finished calculating the file's digest. Remember
+ * it in the metadata, and look up the associated annotation
+ */
+static void
+got_file_digest (NautilusFile *file, const char *file_digest)
+{
+
+ if (file_digest == NULL) {
+ return;
+ }
+
+ /* save the digest in the file metadata */
+ nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_FILE_DIGEST, NULL, file_digest);
+
+ /* lookup the annotations associated with the file. If there is one, flag the change and we're done */
+ if (has_local_annotation (file_digest)) {
+ nautilus_file_emit_changed (file);
+ return;
+ }
+
+ /* there isn't a local annotation, so ask the server for one */
+ get_annotation_from_server (file, file_digest);
+ return;
+}
+
+/* utility routine that takes a passed-in notes-info string and returns true if the
+ * encoded date is old enough to require a new look-up
+ */
+static gboolean
+annotation_is_stale (const char *notes_info)
+{
+ time_t info_date, date_stamp;
+
+ if (notes_info == NULL) {
+ return TRUE;
+ }
+
+ info_date = strtoul (notes_info, NULL, 10);
+ time (&date_stamp);
+
+ /* eventually, the lookup interval should be a preference, not a constant */
+ return (date_stamp - info_date) > NOTES_LOOKUP_INTERVAL;
+}
+
+/* return the annotation associated with a file. If we haven't inspected this file yet,
+ * return NULL but queue a request for an annotation lookup, which will be processed
+ * asynchronously and issue a "file_changed" signal if any is found.
+ */
+char *nautilus_annotation_get_annotation (NautilusFile *file)
+{
+ char *digest;
+ char *annotations;
+ char *digest_info;
+
+ /* if it's a directory, return NULL, at least until we figure out how to handle directory
+ * annotations
+ */
+ if (nautilus_file_is_directory (file)) {
+ return NULL;
+ }
+
+ /* see if there's a digest available in metadata */
+ digest = nautilus_file_get_metadata (file, NAUTILUS_METADATA_KEY_FILE_DIGEST, NULL);
+
+ /* there isn't a digest, so start a request for one going, and return NULL */
+ if (digest == NULL) {
+ queue_file_digest_request (file);
+ return NULL;
+ }
+
+ /* if we haven't update the info in a while, initiate a lookup */
+ digest_info = nautilus_file_get_metadata (file, NAUTILUS_METADATA_KEY_NOTES_INFO, NULL);
+ if (annotation_is_stale (digest_info)) {
+ get_annotation_from_server (file, digest);
+ }
+ g_free (digest_info);
+
+ /* there's a digest, so we if we have the annotations for the file cached locally */
+ annotations = look_up_local_annotation (file, digest);
+ if (annotations != NULL) {
+ g_free (digest);
+ return annotations;
+ }
+
+ g_free (digest);
+ return NULL;
+}
+
+/* utility routine to map raw annotation text into text to be displayed
+ * for now this is pretty naive and only handles free-form text, just returning
+ * the first suitable annotation it can find.
+ */
+char *
+nautilus_annotation_get_display_text (const char *note_text)
+{
+ char *display_text, *temp_text;
+ xmlChar *xml_text;
+ xmlDocPtr annotations;
+ xmlNodePtr next_annotation;
+
+ /* if its an xml file, parse it to extract the display text */
+ if (eel_istr_has_prefix (note_text, "<?xml")) {
+ display_text = NULL;
+ annotations = xmlParseMemory ((char*) note_text, strlen (note_text));
+ if (annotations != NULL) {
+ next_annotation = xmlDocGetRootElement (annotations)->childs;
+ while (next_annotation != NULL) {
+ if (eel_strcmp (next_annotation->name, "annotation") == 0) {
+ xml_text = xmlNodeGetContent (next_annotation);
+ temp_text = (char*) xml_text;
+ while (*temp_text && *temp_text < ' ') temp_text++;
+ display_text = g_strdup (temp_text);
+ xmlFree (xml_text);
+ break;
+ }
+ next_annotation = next_annotation->next;
+ }
+ xmlFreeDoc (annotations);
+ }
+ } else {
+ display_text = g_strdup (note_text);
+ }
+ return display_text;
+}
+
+/* convenience routine to return the display text of an annotation associated
+ * with a file
+ */
+char *
+nautilus_annotation_get_annotation_for_display (NautilusFile *file)
+{
+ char *raw_text, *display_text;
+
+ raw_text = nautilus_annotation_get_annotation (file);
+ if (raw_text != NULL) {
+ display_text = nautilus_annotation_get_display_text (raw_text);
+ g_free (raw_text);
+ return display_text;
+ }
+ return NULL;
+}
+
+/* return the number of annotations associated with the passed in file. If we don't know,
+ * return 0, but queue a request like above
+ */
+int nautilus_annotation_has_annotation (NautilusFile *file)
+{
+ char *digest_info, *digits, *temp_str;
+ int count = 0;
+
+ if (!nautilus_preferences_get_boolean (NAUTILUS_PREFERENCES_DISPLAY_ANNOTATIONS)) {
+ return 0;
+ }
+
+ digest_info = nautilus_file_get_metadata (file, NAUTILUS_METADATA_KEY_NOTES_INFO, NULL);
+
+ if (digest_info != NULL) {
+ digits = strrchr (digest_info, ':');
+ count = atoi (digits + 1);
+ g_free (digest_info);
+
+ /* if the date is stale, launch a new lookup */
+ if (annotation_is_stale (digest_info)) {
+ temp_str = nautilus_annotation_get_annotation (file);
+ g_free (temp_str);
+ }
+ return count;
+ } else {
+ /* initiate fetching the annotations from the server */
+ temp_str = nautilus_annotation_get_annotation (file);
+ g_free (temp_str);
+ }
+ g_free (digest_info);
+ return 0;
+}
+
+/* utility routine for making an HTTP POST request with ghttp */
+static gboolean
+http_post_simple (const char *uri, const char *name, const char *value) {
+ ghttp_request* request;
+ char *ename=NULL, *evalue=NULL, *body=NULL;
+
+ request = ghttp_request_new ();
+ if (!request) {
+ return FALSE;
+ }
+
+ if (ghttp_set_uri (request, (char *)uri) != 0 ||
+ ghttp_set_type (request, ghttp_type_post) != 0) {
+ ghttp_close (request);
+ return FALSE;
+ }
+ ghttp_set_header (request, http_hdr_Connection, "close");
+ ghttp_set_header (request, http_hdr_User_Agent, "Nautilus Annotation");
+ ghttp_set_header (request, http_hdr_Content_Type, "application/x-www-form-urlencoded");
+
+ evalue = gnome_vfs_escape_string (value);
+ if (name) {
+ ename = gnome_vfs_escape_string (name);
+ body = g_strconcat (ename, "=", evalue, NULL);
+ g_free (ename);
+ g_free (evalue);
+ } else {
+ body = evalue;
+ }
+
+ if (ghttp_set_body (request, body, strlen(body)) != 0 ||
+ ghttp_prepare (request) != 0) {
+ ghttp_close (request);
+ return FALSE;
+ }
+
+ if (ghttp_process (request) != ghttp_done) {
+ ghttp_close (request);
+ return FALSE;
+ }
+
+ ghttp_close (request);
+ return TRUE;
+}
+
+/* utility to count the number of annotations in the passed-in xml document */
+static int
+count_annotations (xmlDocPtr xml_document)
+{
+ xmlNodePtr next_annotation;
+ int annotation_count;
+
+ annotation_count = 0;
+ next_annotation = xmlDocGetRootElement (xml_document)->childs;
+
+ while (next_annotation != NULL) {
+ if (eel_strcmp (next_annotation->name, "annotation") == 0) {
+ annotation_count += 1;
+ }
+ next_annotation = next_annotation->next;
+ }
+ return annotation_count;
+}
+
+/*
+ * get_ammonite_get_default_user_username
+ *
+ * Returns username of the currently logged-in default Eazel Service User
+ * or NULL if there isn't one
+ */
+
+
+/* send the local annotations associated with the passed-in digest to the server */
+static void
+nautilus_annotation_send_to_server (const char *digest,
+ const char *annotation_type,
+ const char *annotation_text,
+ const char *date_str)
+{
+ char *user_id;
+ xmlChar *request_text;
+ xmlDocPtr xml_document;
+ xmlNodePtr root_node, annotation_node;
+ int request_size;
+
+ /* get the user name */
+ user_id = ammonite_get_default_user_username ();
+
+ /* if the user wasn't logged in, prompt for it (coming soon, for now, just use anonymous */
+ if (user_id == NULL) {
+ user_id = g_strdup ("anonymous");
+ }
+
+ /* create an xml document to hold the annotation posting */
+ xml_document = xmlNewDoc ("1.0");
+
+ /* create the header node, with the digest attribute */
+ root_node = xmlNewDocNode (xml_document, NULL, "annotations", NULL);
+ xmlDocSetRootElement (xml_document, root_node);
+ xmlSetProp (root_node, "digest", digest);
+ xmlSetProp (root_node, "user", user_id);
+
+ /* set up the annotation payload */
+ annotation_node = xmlNewChild (root_node, NULL, "annotation", NULL);
+ xmlSetProp (annotation_node, "type", annotation_type);
+ xmlSetProp (annotation_node, "date", date_str);
+
+ xmlNodeSetContent (annotation_node, annotation_text);
+
+ /* post the annotation request to the server using ghttp */
+ xmlDocDumpMemory (xml_document, &request_text, &request_size);
+
+ if (!http_post_simple (SERVER_POST_URI, "note", request_text)) {
+ g_message ("post request failed");
+ }
+
+ /* clean up and we're done */
+ xmlFree (request_text);
+ g_free (user_id);
+ xmlFreeDoc (xml_document);
+
+}
+
+/* add an annotation to a file */
+void
+nautilus_annotation_add_annotation (NautilusFile *file,
+ const char *annotation_type,
+ const char *annotation_text,
+ const char *access)
+{
+ char *digest;
+ char *annotations;
+ char *info_str, *date_str;
+ char *annotation_path;
+ int annotation_count;
+ time_t date_stamp;
+ xmlDocPtr xml_document;
+ xmlNodePtr root_node, node;
+ gboolean has_annotation, has_new_annotation;
+
+ /* we can't handle directories yet, so just return. */
+ if (nautilus_file_is_directory (file)) {
+ return;
+ }
+
+ has_new_annotation = annotation_text != NULL && strlen (annotation_text) > 0;
+
+ /* fetch the local annotation, if one exists */
+ digest = nautilus_file_get_metadata (file, NAUTILUS_METADATA_KEY_FILE_DIGEST, NULL);
+
+ /* there isn't a digest, so start a request for one going, and return */
+ /* this shouldn't happen in practice, since the annotation window will have
+ * already created a digest
+ */
+ if (digest == NULL) {
+ queue_file_digest_request (file);
+ return;
+ }
+
+ /* there's a digest, so we if we have the annotations for the file cached locally */
+ annotations = look_up_local_annotation (file, digest);
+ has_annotation = annotations != NULL && strlen (annotations) > 0;
+
+ /* handle the case of no annotation, by removing it if necessary */
+ if (!has_new_annotation) {
+ if (has_annotation) {
+ /* delete the annotation */
+ annotation_path = get_annotation_path (digest);
+ unlink (annotation_path);
+
+ /* set the info to indicate no notes available */
+ time (&date_stamp);
+ info_str = g_strdup_printf ("%lu:%d", date_stamp, 0);
+ nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_NOTES_INFO, NULL, info_str);
+ g_free (info_str);
+
+ nautilus_file_emit_changed (file);
+
+ g_free (annotation_path);
+ }
+
+ g_free (digest);
+ g_free (annotations);
+ return;
+ }
+
+ /* there is a new annotation, but no current annotation exists, so create the
+ * initial xml document from scratch
+ */
+ if (!has_annotation) {
+ xml_document = xmlNewDoc ("1.0");
+ /* create the header node, with the digest attribute */
+ root_node = xmlNewDocNode (xml_document, NULL, "annotations", NULL);
+ xmlDocSetRootElement (xml_document, root_node);
+ xmlSetProp (root_node, "digest", digest);
+ } else {
+ /* open the existing annotation and load it */
+ xml_document = xmlParseMemory (annotations, strlen (annotations));
+ root_node = xmlDocGetRootElement (xml_document);
+ }
+
+ time (&date_stamp);
+ date_str = g_strdup_printf ("%lu", date_stamp);
+
+ /* add the new entry. For now, we only support one entry per file, so we replace the old
+ * one, if it exists, but this will change soon as we support multiple notes per file
+ */
+ if (root_node->childs == NULL) {
+ node = xmlNewChild (root_node, NULL, "annotation", NULL);
+ xmlSetProp (node, "type", annotation_type);
+
+ date_str = g_strdup_printf ("%lu", date_stamp);
+ xmlSetProp (node, "date", date_str);
+ } else {
+ node = root_node->childs;
+ }
+
+ xmlNodeSetContent (node, annotation_text);
+
+ /* save the modified xml document back to the local repository */
+ save_local_annotations (xml_document, digest);
+
+ /* update the metadata date and count */
+ annotation_count = count_annotations (xml_document);
+ info_str = g_strdup_printf ("%lu:%d", date_stamp, annotation_count);
+
+ nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_NOTES_INFO, NULL, info_str);
+ g_free (info_str);
+
+ /* issue file changed symbol to update the emblem */
+ nautilus_file_emit_changed (file);
+
+ /* if the access is global, send it to the server */
+ if (eel_strcmp (access, "global") == 0) {
+ nautilus_annotation_send_to_server (digest, annotation_type, annotation_text, date_str);
+ }
+
+ /* clean up and we're done */
+ xmlFreeDoc (xml_document);
+ g_free (date_str);
+ g_free (digest);
+ g_free (annotations);
+}
+
+/* remove an annotation from a file */
+void nautilus_annotation_remove_annotation (NautilusFile *file, int which_annotation)
+{
+}
+
diff --git a/libnautilus-extensions/nautilus-annotation.h b/libnautilus-extensions/nautilus-annotation.h
new file mode 100644
index 000000000..712b276f8
--- /dev/null
+++ b/libnautilus-extensions/nautilus-annotation.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+ nautilus-annotation.h: routines for getting and setting xml-based annotations associated
+ with the digest of a file.
+
+ Copyright (C) 2000 Eazel, Inc.
+
+ This program 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; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program 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. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Authors: Andy Hertzfeld <andy@eazel.com>
+*/
+
+#ifndef NAUTILUS_ANNOTATION_H
+#define NAUTILUS_ANNOTATION_H
+
+#include <glib.h>
+#include <libnautilus-extensions/nautilus-file.h>
+#include <libnautilus-extensions/nautilus-metadata.h>
+
+char * nautilus_annotation_get_annotation (NautilusFile *file);
+char * nautilus_annotation_get_display_text (const char* annotation_text);
+char * nautilus_annotation_get_annotation_for_display (NautilusFile *file);
+
+int nautilus_annotation_has_annotation (NautilusFile *file);
+
+void nautilus_annotation_add_annotation (NautilusFile *file,
+ const char *annotation_type,
+ const char *annotation_text,
+ const char *access);
+void nautilus_annotation_remove_annotation (NautilusFile *file, int which_annotation);
+
+#endif /* NAUTILUS_ANNOTATION_H */
diff --git a/libnautilus-extensions/nautilus-canvas-note-item.c b/libnautilus-extensions/nautilus-canvas-note-item.c
new file mode 100644
index 000000000..6f3607090
--- /dev/null
+++ b/libnautilus-extensions/nautilus-canvas-note-item.c
@@ -0,0 +1,973 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+ nautilus-canvas-note-item.c: annotation canvas item for nautilus implementation
+
+ Copyright (C) 2001 Eazel, Inc.
+
+ This program 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; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program 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. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ based on gnome_canvas_rect_item by Federico Mena Quintero
+
+ Author: Andy Hertzfeld <andy@eazel.com>
+*/
+
+#include <config.h>
+#include <math.h>
+
+#include <libgnomeui/gnome-canvas.h>
+#include <libgnomeui/gnome-canvas-util.h>
+#include <libgnomeui/gnome-icon-text.h>
+
+#include <gnome-xml/parser.h>
+#include <gnome-xml/xmlmemory.h>
+
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_vpath.h>
+#include <libart_lgpl/art_rgb_svp.h>
+
+#include "nautilus-annotation.h"
+#include "nautilus-canvas-note-item.h"
+#include "nautilus-font-factory.h"
+#include <eel/eel-gdk-extensions.h>
+#include <eel/eel-gdk-pixbuf-extensions.h>
+#include <eel/eel-gnome-extensions.h>
+#include <eel/eel-scalable-font.h>
+#include <eel/eel-smooth-text-layout.h>
+#include <eel/eel-string.h>
+
+enum {
+ ARG_0,
+ ARG_X1,
+ ARG_Y1,
+ ARG_X2,
+ ARG_Y2,
+ ARG_FILL_COLOR,
+ ARG_FILL_COLOR_GDK,
+ ARG_FILL_COLOR_RGBA,
+ ARG_NOTE_TEXT,
+ ARG_OUTLINE_COLOR,
+ ARG_OUTLINE_COLOR_GDK,
+ ARG_OUTLINE_COLOR_RGBA,
+ ARG_FILL_STIPPLE,
+ ARG_OUTLINE_STIPPLE,
+ ARG_WIDTH_PIXELS,
+ ARG_WIDTH_UNITS
+};
+
+#define ANNOTATION_MAX_WIDTH 240
+#define DEFAULT_FONT_SIZE 12
+#define LINE_BREAK_CHARACTERS " -_,;.?/&"
+
+#define ARROW_HEIGHT 16
+#define MIN_ARROW_HALF_WIDTH 4
+#define MAX_ARROW_HALF_WIDTH 12
+
+static void nautilus_canvas_note_item_class_init (NautilusCanvasNoteItemClass *class);
+static void nautilus_canvas_note_item_init (NautilusCanvasNoteItem *note_item);
+static void nautilus_canvas_note_item_destroy (GtkObject *object);
+static void nautilus_canvas_note_item_set_arg (GtkObject *object,
+ GtkArg *arg,
+ guint arg_id);
+static void nautilus_canvas_note_item_get_arg (GtkObject *object,
+ GtkArg *arg,
+ guint arg_id);
+
+static void nautilus_canvas_note_item_realize (GnomeCanvasItem *item);
+static void nautilus_canvas_note_item_unrealize (GnomeCanvasItem *item);
+static void nautilus_canvas_note_item_translate (GnomeCanvasItem *item, double dx, double dy);
+static void nautilus_canvas_note_item_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+
+static void nautilus_canvas_note_item_draw (GnomeCanvasItem *item, GdkDrawable *drawable, int x, int y, int width, int height);
+static void nautilus_canvas_note_item_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+static void nautilus_canvas_note_item_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+static double nautilus_canvas_note_item_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item);
+
+static GnomeCanvasItemClass *note_item_parent_class;
+
+
+GtkType
+nautilus_canvas_note_item_get_type (void)
+{
+ static GtkType note_item_type = 0;
+
+ if (!note_item_type) {
+ GtkTypeInfo note_item_info = {
+ "NautilusCanvasNoteItem",
+ sizeof (NautilusCanvasNoteItem),
+ sizeof (NautilusCanvasNoteItemClass),
+ (GtkClassInitFunc) nautilus_canvas_note_item_class_init,
+ (GtkObjectInitFunc) nautilus_canvas_note_item_init,
+ NULL, /* reserved_1 */
+ NULL, /* reserved_2 */
+ (GtkClassInitFunc) NULL
+ };
+
+ note_item_type = gtk_type_unique (gnome_canvas_item_get_type (), &note_item_info);
+ }
+
+ return note_item_type;
+}
+
+static void
+nautilus_canvas_note_item_class_init (NautilusCanvasNoteItemClass *class)
+{
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ object_class = (GtkObjectClass *) class;
+ item_class = (GnomeCanvasItemClass *) class;
+
+ note_item_parent_class = gtk_type_class (gnome_canvas_item_get_type ());
+
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::x1", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_X1);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::y1", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_Y1);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::x2", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_X2);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::y2", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_Y2);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::fill_color", GTK_TYPE_STRING, GTK_ARG_WRITABLE, ARG_FILL_COLOR);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::fill_color_gdk", GTK_TYPE_GDK_COLOR, GTK_ARG_READWRITE, ARG_FILL_COLOR_GDK);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::fill_color_rgba", GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_FILL_COLOR_RGBA);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::note_text", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_NOTE_TEXT);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::outline_color", GTK_TYPE_STRING, GTK_ARG_WRITABLE, ARG_OUTLINE_COLOR);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::outline_color_gdk", GTK_TYPE_GDK_COLOR, GTK_ARG_READWRITE, ARG_OUTLINE_COLOR_GDK);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::outline_color_rgba", GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_OUTLINE_COLOR_RGBA);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::fill_stipple", GTK_TYPE_GDK_WINDOW, GTK_ARG_READWRITE, ARG_FILL_STIPPLE);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::outline_stipple", GTK_TYPE_GDK_WINDOW, GTK_ARG_READWRITE, ARG_OUTLINE_STIPPLE);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::width_pixels", GTK_TYPE_UINT, GTK_ARG_WRITABLE, ARG_WIDTH_PIXELS);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::width_units", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_WIDTH_UNITS);
+
+ object_class->destroy = nautilus_canvas_note_item_destroy;
+ object_class->set_arg = nautilus_canvas_note_item_set_arg;
+ object_class->get_arg = nautilus_canvas_note_item_get_arg;
+
+ item_class->realize = nautilus_canvas_note_item_realize;
+ item_class->unrealize = nautilus_canvas_note_item_unrealize;
+ item_class->translate = nautilus_canvas_note_item_translate;
+ item_class->bounds = nautilus_canvas_note_item_bounds;
+
+ item_class->draw = nautilus_canvas_note_item_draw;
+ item_class->point = nautilus_canvas_note_item_point;
+ item_class->update = nautilus_canvas_note_item_update;
+ item_class->render = nautilus_canvas_note_item_render;
+}
+
+static void
+nautilus_canvas_note_item_init (NautilusCanvasNoteItem *note_item)
+{
+ note_item->width = 0.0;
+ note_item->fill_svp = NULL;
+ note_item->outline_svp = NULL;
+}
+
+static void
+nautilus_canvas_note_item_destroy (GtkObject *object)
+{
+ NautilusCanvasNoteItem *note_item;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (NAUTILUS_IS_CANVAS_NOTE_ITEM (object));
+
+ note_item = NAUTILUS_CANVAS_NOTE_ITEM (object);
+
+ if (note_item->fill_stipple)
+ gdk_bitmap_unref (note_item->fill_stipple);
+
+ if (note_item->outline_stipple)
+ gdk_bitmap_unref (note_item->outline_stipple);
+
+ if (note_item->fill_svp)
+ art_svp_free (note_item->fill_svp);
+
+ if (note_item->outline_svp)
+ art_svp_free (note_item->outline_svp);
+
+ if (note_item->note_text)
+ g_free (note_item->note_text);
+
+ if (GTK_OBJECT_CLASS (note_item_parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (note_item_parent_class)->destroy) (object);
+}
+
+static void get_bounds (NautilusCanvasNoteItem *note_item, double *px1, double *py1, double *px2, double *py2)
+{
+ GnomeCanvasItem *item;
+ double x1, y1, x2, y2;
+ int cx1, cy1, cx2, cy2;
+ double hwidth;
+
+ item = GNOME_CANVAS_ITEM (note_item);
+
+ if (note_item->width_pixels)
+ hwidth = (note_item->width / item->canvas->pixels_per_unit) / 2.0;
+ else
+ hwidth = note_item->width / 2.0;
+
+ x1 = note_item->x1;
+ y1 = note_item->y1;
+ x2 = note_item->x2;
+ y2 = note_item->y2;
+
+ gnome_canvas_item_i2w (item, &x1, &y1);
+ gnome_canvas_item_i2w (item, &x2, &y2);
+ gnome_canvas_w2c (item->canvas, x1 - hwidth, y1 - hwidth, &cx1, &cy1);
+ gnome_canvas_w2c (item->canvas, x2 + hwidth, y2 + hwidth, &cx2, &cy2);
+ *px1 = cx1;
+ *py1 = cy1;
+ *px2 = cx2;
+ *py2 = cy2;
+
+ /* Some safety fudging */
+
+ *px1 -= 2;
+ *py1 -= 2;
+ *px2 += 2;
+ *py2 += 2;
+}
+
+/* Convenience function to set a GC's foreground color to the specified pixel value */
+static void
+set_gc_foreground (GdkGC *gc, gulong pixel)
+{
+ GdkColor c;
+
+ if (!gc)
+ return;
+
+ c.pixel = pixel;
+ gdk_gc_set_foreground (gc, &c);
+}
+
+/* Sets the stipple pattern for the specified gc */
+static void
+set_stipple (GdkGC *gc, GdkBitmap **internal_stipple, GdkBitmap *stipple, int reconfigure)
+{
+ if (*internal_stipple && !reconfigure)
+ gdk_bitmap_unref (*internal_stipple);
+
+ *internal_stipple = stipple;
+ if (stipple && !reconfigure)
+ gdk_bitmap_ref (stipple);
+
+ if (gc) {
+ if (stipple) {
+ gdk_gc_set_stipple (gc, stipple);
+ gdk_gc_set_fill (gc, GDK_STIPPLED);
+ } else
+ gdk_gc_set_fill (gc, GDK_SOLID);
+ }
+}
+
+/* Recalculate the outline width of the rectangle/ellipse and set it in its GC */
+static void
+set_outline_gc_width (NautilusCanvasNoteItem *note_item)
+{
+ int width;
+
+ if (!note_item->outline_gc)
+ return;
+
+ if (note_item->width_pixels)
+ width = (int) note_item->width;
+ else
+ width = (int) (note_item->width * note_item->item.canvas->pixels_per_unit + 0.5);
+
+ gdk_gc_set_line_attributes (note_item->outline_gc, width,
+ GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
+}
+
+/* utility to update the canvas item bounding box from the note item's private bounding box */
+static void
+update_item_bounding_box (NautilusCanvasNoteItem *note_item)
+{
+ GnomeCanvasItem *item;
+ item = GNOME_CANVAS_ITEM (note_item);
+
+ item->x1 = note_item->x1;
+ item->y1 = note_item->y1;
+ item->x2 = note_item->x2 + 1;
+ item->y2 = note_item->y2 + 1;
+}
+
+static void
+nautilus_canvas_note_item_set_note_text (NautilusCanvasNoteItem *note_item, const char *new_text)
+{
+ char *display_text;
+ int text_width, height, width;
+ int font_height;
+ GnomeCanvasItem *item;
+ EelScalableFont *scalable_font;
+ GdkFont *font;
+ EelDimensions dimensions;
+ EelSmoothTextLayout *smooth_text_layout;
+
+ item = GNOME_CANVAS_ITEM (note_item);
+
+ if (note_item->note_text) {
+ g_free (note_item->note_text);
+ }
+
+ height = 0; width = 0; /* to avoid compiler complaint */
+ note_item->note_text = g_strdup (new_text);
+
+ /* set the width and height based on the display text */
+ /* this will get more sophisticated as we get fancier */
+ display_text = nautilus_annotation_get_display_text (new_text);
+
+ if (item->canvas->aa) {
+ scalable_font = eel_scalable_font_get_default_font ();
+
+
+ smooth_text_layout = eel_smooth_text_layout_new (
+ display_text, strlen(display_text),
+ scalable_font, DEFAULT_FONT_SIZE, TRUE);
+
+ dimensions = eel_smooth_text_layout_get_dimensions (smooth_text_layout);
+ text_width = dimensions.width + 8;
+ height = dimensions.height + 4;
+ height += ARROW_HEIGHT;
+ gtk_object_unref (GTK_OBJECT (scalable_font));
+ gtk_object_destroy (GTK_OBJECT (smooth_text_layout));
+
+ } else {
+ font = nautilus_font_factory_get_font_from_preferences (DEFAULT_FONT_SIZE);
+ text_width = 8 + gdk_text_measure (font, display_text, strlen (display_text));
+ font_height = gdk_text_height (font, display_text, strlen (display_text));
+ height = font_height * (1 + (text_width / ANNOTATION_MAX_WIDTH));
+ gdk_font_unref (font);
+ }
+
+ width = (text_width < ANNOTATION_MAX_WIDTH) ? text_width : ANNOTATION_MAX_WIDTH;
+
+ /* add some vertical slop for descenders and incorporate scale factor */
+ note_item->x2 = floor (note_item->x1 + (width / item->canvas->pixels_per_unit) + .5);
+ note_item->y2 = floor (note_item->y1 + 4.0 + (height / item->canvas->pixels_per_unit) + .5);
+
+
+ update_item_bounding_box (note_item);
+
+ g_free (display_text);
+}
+
+static void
+nautilus_canvas_note_item_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
+{
+ GnomeCanvasItem *item;
+ NautilusCanvasNoteItem *note_item;
+ GdkColor color = { 0, 0, 0, 0, };
+ GdkColor *pcolor;
+ int have_pixel;
+
+ item = GNOME_CANVAS_ITEM (object);
+ note_item = NAUTILUS_CANVAS_NOTE_ITEM (object);
+ have_pixel = FALSE;
+
+ switch (arg_id) {
+ case ARG_X1:
+ note_item->x1 = GTK_VALUE_DOUBLE (*arg);
+ update_item_bounding_box (note_item);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case ARG_Y1:
+ note_item->y1 = GTK_VALUE_DOUBLE (*arg);
+ update_item_bounding_box (note_item);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case ARG_X2:
+ note_item->x2 = GTK_VALUE_DOUBLE (*arg);
+ update_item_bounding_box (note_item);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case ARG_Y2:
+ note_item->y2 = GTK_VALUE_DOUBLE (*arg);
+ update_item_bounding_box (note_item);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case ARG_FILL_COLOR:
+ case ARG_FILL_COLOR_GDK:
+ case ARG_FILL_COLOR_RGBA:
+ switch (arg_id) {
+ case ARG_FILL_COLOR:
+ gdk_color_parse (GTK_VALUE_STRING (*arg), &color);
+
+ note_item->fill_color = ((color.red & 0xff00) << 16 |
+ (color.green & 0xff00) << 8 |
+ (color.blue & 0xff00) |
+ 0xff);
+ break;
+
+ case ARG_FILL_COLOR_GDK:
+ pcolor = GTK_VALUE_BOXED (*arg);
+ if (pcolor) {
+ color = *pcolor;
+ gdk_color_context_query_color (item->canvas->cc, &color);
+ have_pixel = TRUE;
+ }
+
+ note_item->fill_color = ((color.red & 0xff00) << 16 |
+ (color.green & 0xff00) << 8 |
+ (color.blue & 0xff00) |
+ 0xff);
+ break;
+
+ case ARG_FILL_COLOR_RGBA:
+ note_item->fill_color = GTK_VALUE_UINT (*arg);
+ break;
+ }
+
+ if (have_pixel)
+ note_item->fill_pixel = color.pixel;
+ else
+ note_item->fill_pixel = gnome_canvas_get_color_pixel (item->canvas, note_item->fill_color);
+
+ if (!item->canvas->aa)
+ set_gc_foreground (note_item->fill_gc, note_item->fill_pixel);
+
+ gnome_canvas_item_request_redraw_svp (item, note_item->fill_svp);
+ break;
+
+ case ARG_OUTLINE_COLOR:
+ case ARG_OUTLINE_COLOR_GDK:
+ case ARG_OUTLINE_COLOR_RGBA:
+ switch (arg_id) {
+ case ARG_OUTLINE_COLOR:
+ gdk_color_parse (GTK_VALUE_STRING (*arg), &color);
+
+ note_item->outline_color = ((color.red & 0xff00) << 16 |
+ (color.green & 0xff00) << 8 |
+ (color.blue & 0xff00) |
+ 0xff);
+ break;
+
+ case ARG_OUTLINE_COLOR_GDK:
+ pcolor = GTK_VALUE_BOXED (*arg);
+ if (pcolor) {
+ color = *pcolor;
+ gdk_color_context_query_color (item->canvas->cc, &color);
+ have_pixel = TRUE;
+ }
+
+ note_item->outline_color = ((color.red & 0xff00) << 16 |
+ (color.green & 0xff00) << 8 |
+ (color.blue & 0xff00) |
+ 0xff);
+ break;
+
+ case ARG_OUTLINE_COLOR_RGBA:
+ note_item->outline_color = GTK_VALUE_UINT (*arg);
+ break;
+ }
+
+ if (have_pixel)
+ note_item->outline_pixel = color.pixel;
+ else
+ note_item->outline_pixel = gnome_canvas_get_color_pixel (item->canvas,
+ note_item->outline_color);
+
+ if (!item->canvas->aa)
+ set_gc_foreground (note_item->outline_gc, note_item->outline_pixel);
+
+ gnome_canvas_item_request_redraw_svp (item, note_item->outline_svp);
+ break;
+
+ case ARG_NOTE_TEXT:
+ nautilus_canvas_note_item_set_note_text (note_item, GTK_VALUE_STRING (*arg));
+ break;
+
+ case ARG_FILL_STIPPLE:
+ if (!item->canvas->aa)
+ set_stipple (note_item->fill_gc, &note_item->fill_stipple, GTK_VALUE_BOXED (*arg), FALSE);
+
+ break;
+
+ case ARG_OUTLINE_STIPPLE:
+ if (!item->canvas->aa)
+ set_stipple (note_item->outline_gc, &note_item->outline_stipple, GTK_VALUE_BOXED (*arg), FALSE);
+ break;
+
+ case ARG_WIDTH_PIXELS:
+ note_item->width = GTK_VALUE_UINT (*arg);
+ note_item->width_pixels = TRUE;
+ if (!item->canvas->aa)
+ set_outline_gc_width (note_item);
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case ARG_WIDTH_UNITS:
+ note_item->width = fabs (GTK_VALUE_DOUBLE (*arg));
+ note_item->width_pixels = FALSE;
+ if (!item->canvas->aa)
+ set_outline_gc_width (note_item);
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* Allocates a GdkColor structure filled with the specified pixel, and puts it into the specified
+ * arg for returning it in the get_arg method.
+ */
+static void
+get_color_arg (NautilusCanvasNoteItem *note_item, gulong pixel, GtkArg *arg)
+{
+ GdkColor *color;
+
+ color = g_new (GdkColor, 1);
+ color->pixel = pixel;
+ gdk_color_context_query_color (GNOME_CANVAS_ITEM (note_item)->canvas->cc, color);
+ GTK_VALUE_BOXED (*arg) = color;
+}
+
+static void
+nautilus_canvas_note_item_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
+{
+ NautilusCanvasNoteItem *note_item;
+ GnomeCanvasItem *item;
+
+ item = GNOME_CANVAS_ITEM (object);
+ note_item = NAUTILUS_CANVAS_NOTE_ITEM (object);
+
+ switch (arg_id) {
+ case ARG_X1:
+ GTK_VALUE_DOUBLE (*arg) = note_item->x1;
+ break;
+
+ case ARG_Y1:
+ GTK_VALUE_DOUBLE (*arg) = note_item->y1;
+ break;
+
+ case ARG_X2:
+ GTK_VALUE_DOUBLE (*arg) = note_item->x2;
+ break;
+
+ case ARG_Y2:
+ GTK_VALUE_DOUBLE (*arg) = note_item->y2;
+ break;
+
+ case ARG_FILL_COLOR_GDK:
+ get_color_arg (note_item, note_item->fill_pixel, arg);
+ break;
+
+ case ARG_OUTLINE_COLOR_GDK:
+ get_color_arg (note_item, note_item->outline_pixel, arg);
+ break;
+
+ case ARG_FILL_COLOR_RGBA:
+ GTK_VALUE_UINT (*arg) = note_item->fill_color;
+ break;
+
+ case ARG_OUTLINE_COLOR_RGBA:
+ GTK_VALUE_UINT (*arg) = note_item->outline_color;
+ break;
+
+ case ARG_FILL_STIPPLE:
+ GTK_VALUE_BOXED (*arg) = note_item->fill_stipple;
+ break;
+
+ case ARG_OUTLINE_STIPPLE:
+ GTK_VALUE_BOXED (*arg) = note_item->outline_stipple;
+ break;
+
+ case ARG_NOTE_TEXT:
+ GTK_VALUE_STRING (*arg) = note_item->note_text;
+ break;
+ default:
+ arg->type = GTK_TYPE_INVALID;
+ break;
+ }
+}
+
+static void
+nautilus_canvas_note_item_realize (GnomeCanvasItem *item)
+{
+ NautilusCanvasNoteItem *note_item;
+
+ note_item = NAUTILUS_CANVAS_NOTE_ITEM (item);
+
+ if (note_item_parent_class->realize)
+ (* note_item_parent_class->realize) (item);
+
+ if (!item->canvas->aa) {
+ note_item->fill_gc = gdk_gc_new (item->canvas->layout.bin_window);
+ note_item->outline_gc = gdk_gc_new (item->canvas->layout.bin_window);
+ }
+}
+
+static void
+nautilus_canvas_note_item_unrealize (GnomeCanvasItem *item)
+{
+ NautilusCanvasNoteItem *note_item;
+
+ note_item = NAUTILUS_CANVAS_NOTE_ITEM (item);
+
+ if (!item->canvas->aa) {
+ gdk_gc_unref (note_item->fill_gc);
+ note_item->fill_gc = NULL;
+ gdk_gc_unref (note_item->outline_gc);
+ note_item->outline_gc = NULL;
+ }
+
+ if (note_item_parent_class->unrealize)
+ (* note_item_parent_class->unrealize) (item);
+}
+
+static void
+nautilus_canvas_note_item_translate (GnomeCanvasItem *item, double dx, double dy)
+{
+ NautilusCanvasNoteItem *note_item;
+
+ note_item = NAUTILUS_CANVAS_NOTE_ITEM (item);
+
+ note_item->x1 += dx;
+ note_item->y1 += dy;
+ note_item->x2 += dx;
+ note_item->y2 += dy;
+
+ update_item_bounding_box (note_item);
+
+ if (item->canvas->aa) {
+ gnome_canvas_item_request_update (item);
+ }
+}
+
+static void
+nautilus_canvas_note_item_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ NautilusCanvasNoteItem *note_item;
+ double hwidth;
+
+ note_item = NAUTILUS_CANVAS_NOTE_ITEM (item);
+
+ if (note_item->width_pixels)
+ hwidth = (note_item->width / item->canvas->pixels_per_unit) / 2.0;
+ else
+ hwidth = note_item->width / 2.0;
+
+ *x1 = note_item->x1 - hwidth;
+ *y1 = note_item->y1 - hwidth;
+ *x2 = note_item->x2 + hwidth;
+ *y2 = note_item->y2 + hwidth;
+}
+
+/* utility routine to map raw annotation text into text to be displayed */
+/* for now this is pretty naive and only handles free-form text, just returning
+ * the first suitable annotation it can find
+ */
+
+/* utility routine to draw a text string into the passed-in item */
+static void
+draw_item_aa_text (GnomeCanvasBuf *buf, GnomeCanvasItem *item, const char *note_text)
+{
+ EelScalableFont *font;
+ GdkPixbuf *text_pixbuf;
+ ArtIRect item_bounds, dest_bounds;
+ int width, height;
+ EelSmoothTextLayout *smooth_text_layout;
+
+ font = eel_scalable_font_get_default_font ();
+
+ eel_gnome_canvas_item_get_canvas_bounds (item, &item_bounds);
+ width = item_bounds.x1 - item_bounds.x0;
+ height = item_bounds.y1 - item_bounds.y0;
+
+ text_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+ TRUE,
+ 8,
+ width,
+ height);
+ eel_gdk_pixbuf_fill_rectangle_with_color (text_pixbuf, NULL,
+ EEL_RGBA_COLOR_PACK (0, 0, 0, 0));
+
+ smooth_text_layout = eel_smooth_text_layout_new (
+ note_text, strlen(note_text),
+ font, DEFAULT_FONT_SIZE, TRUE);
+
+ eel_smooth_text_layout_set_line_wrap_width (smooth_text_layout, width - 4);
+
+ dest_bounds.x0 = 0;
+ dest_bounds.y0 = ARROW_HEIGHT;
+ dest_bounds.x1 = width;
+ dest_bounds.y1 = height;
+
+ eel_smooth_text_layout_draw_to_pixbuf
+ (smooth_text_layout, text_pixbuf,
+ 0, 0, &dest_bounds, GTK_JUSTIFY_LEFT,
+ FALSE, EEL_RGBA_COLOR_OPAQUE_BLACK,
+ EEL_OPACITY_FULLY_OPAQUE);
+
+ gtk_object_destroy (GTK_OBJECT (smooth_text_layout));
+
+ eel_gnome_canvas_draw_pixbuf (buf, text_pixbuf, item_bounds.x0 + 4, item_bounds.y0 + 2);
+
+ gdk_pixbuf_unref (text_pixbuf);
+ gtk_object_unref (GTK_OBJECT (font));
+}
+
+static void
+nautilus_canvas_note_item_render (GnomeCanvasItem *item,
+ GnomeCanvasBuf *buf)
+{
+ NautilusCanvasNoteItem *note_item;
+ char *display_text;
+
+ note_item = NAUTILUS_CANVAS_NOTE_ITEM (item);
+
+ if (note_item->fill_svp != NULL) {
+ gnome_canvas_render_svp (buf, note_item->fill_svp, note_item->fill_color);
+ }
+
+ /* draw the annotation text, if necessary */
+ if (note_item->note_text) {
+ display_text = nautilus_annotation_get_display_text (note_item->note_text);
+ if (display_text && strlen (display_text)) {
+ draw_item_aa_text (buf, item, display_text);
+ }
+ g_free (display_text);
+ }
+
+ if (note_item->outline_svp != NULL) {
+ gnome_canvas_render_svp (buf, note_item->outline_svp, note_item->outline_color);
+ }
+}
+
+static void
+nautilus_canvas_note_item_draw (GnomeCanvasItem *item, GdkDrawable *drawable, int x, int y, int width, int height)
+{
+ NautilusCanvasNoteItem *note_item;
+ GdkFont *font;
+ char* display_text;
+ double i2w[6], w2c[6], i2c[6];
+ int x1, y1, x2, y2;
+ ArtPoint i1, i2;
+ ArtPoint c1, c2;
+ GnomeIconTextInfo *text_info;
+
+ note_item = NAUTILUS_CANVAS_NOTE_ITEM (item);
+
+ /* Get canvas pixel coordinates */
+ gnome_canvas_item_i2w_affine (item, i2w);
+ gnome_canvas_w2c_affine (item->canvas, w2c);
+ art_affine_multiply (i2c, i2w, w2c);
+
+ i1.x = note_item->x1;
+ i1.y = note_item->y1;
+ i2.x = note_item->x2;
+ i2.y = note_item->y2;
+ art_affine_point (&c1, &i1, i2c);
+ art_affine_point (&c2, &i2, i2c);
+ x1 = c1.x;
+ y1 = c1.y;
+ x2 = c2.x;
+ y2 = c2.y;
+
+ if (note_item->fill_stipple)
+ gnome_canvas_set_stipple_origin (item->canvas, note_item->fill_gc);
+
+ gdk_draw_rectangle (drawable,
+ note_item->fill_gc,
+ TRUE,
+ x1 - x,
+ y1 - y,
+ x2 - x1 + 1,
+ y2 - y1 + 1);
+
+ /* draw the annotation text */
+ if (note_item->note_text) {
+ font = nautilus_font_factory_get_font_from_preferences (DEFAULT_FONT_SIZE);
+ display_text = nautilus_annotation_get_display_text (note_item->note_text);
+
+ text_info = gnome_icon_layout_text
+ (font, display_text,
+ LINE_BREAK_CHARACTERS,
+ x2 - x1 - 2, TRUE);
+
+ gnome_icon_paint_text (text_info, drawable, note_item->outline_gc,
+ x1 - x + 4, y1 - y + 4, GTK_JUSTIFY_LEFT);
+ gnome_icon_text_info_free (text_info);
+
+ gdk_font_unref (font);
+ g_free (display_text);
+ }
+
+ if (note_item->outline_stipple)
+ gnome_canvas_set_stipple_origin (item->canvas, note_item->outline_gc);
+
+ gdk_draw_rectangle (drawable,
+ note_item->outline_gc,
+ FALSE,
+ x1 - x,
+ y1 - y,
+ x2 - x1,
+ y2 - y1);
+}
+
+static double
+nautilus_canvas_note_item_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
+{
+ NautilusCanvasNoteItem *note_item;
+ double x1, y1, x2, y2;
+ double hwidth;
+ double dx, dy;
+
+ note_item = NAUTILUS_CANVAS_NOTE_ITEM (item);
+
+ *actual_item = item;
+
+ /* Find the bounds for the rectangle plus its outline width */
+
+ x1 = note_item->x1;
+ y1 = note_item->y1;
+ x2 = note_item->x2;
+ y2 = note_item->y2;
+
+ if (note_item->width_pixels)
+ hwidth = (note_item->width / item->canvas->pixels_per_unit) / 2.0;
+ else
+ hwidth = note_item->width / 2.0;
+
+ x1 -= hwidth;
+ y1 -= hwidth;
+ x2 += hwidth;
+ y2 += hwidth;
+
+ /* Is point inside rectangle (which can be hollow if it has no fill set)? */
+
+ if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
+ return 0.0;
+ }
+
+ /* Point is outside rectangle */
+
+ if (x < x1)
+ dx = x1 - x;
+ else if (x > x2)
+ dx = x - x2;
+ else
+ dx = 0.0;
+
+ if (y < y1)
+ dy = y1 - y;
+ else if (y > y2)
+ dy = y - y2;
+ else
+ dy = 0.0;
+
+ return sqrt (dx * dx + dy * dy);
+}
+
+static void
+nautilus_canvas_note_item_update (GnomeCanvasItem *item, double affine[6], ArtSVP *clip_path, gint flags)
+{
+ NautilusCanvasNoteItem *note_item;
+ ArtVpath vpath[9];
+ ArtVpath *vpath2;
+ ArtSVP *stroke_svp;
+ double x0, y0, x1, y1;
+ double round_off_amount;
+ double midpoint;
+ double arrow_half_width;
+ note_item = NAUTILUS_CANVAS_NOTE_ITEM (item);
+
+ if (note_item_parent_class->update)
+ (* note_item_parent_class->update) (item, affine, clip_path, flags);
+
+ if (item->canvas->aa) {
+ x0 = note_item->x1;
+ y0 = note_item->y1 + ARROW_HEIGHT;
+ x1 = note_item->x2;
+ y1 = note_item->y2;
+
+ round_off_amount = item->canvas->pixels_per_unit / 2;
+
+ gnome_canvas_item_reset_bounds (item);
+ midpoint = (x1 + x0) / 2;
+ arrow_half_width = (x1 - x0) / 16;
+ arrow_half_width = CLAMP (arrow_half_width, MIN_ARROW_HALF_WIDTH, MAX_ARROW_HALF_WIDTH);
+
+ vpath[0].code = ART_MOVETO;
+ vpath[0].x = x0 - round_off_amount;
+ vpath[0].y = y0 - round_off_amount;
+
+ vpath[1].code = ART_LINETO;
+ vpath[1].x = x0 - round_off_amount;
+ vpath[1].y = y1 + round_off_amount;
+
+ vpath[2].code = ART_LINETO;
+ vpath[2].x = x1 + round_off_amount;
+ vpath[2].y = y1 + round_off_amount;
+
+ vpath[3].code = ART_LINETO;
+ vpath[3].x = x1 + round_off_amount;
+ vpath[3].y = y0 - round_off_amount;
+
+ vpath[4].code = ART_LINETO;
+ vpath[4].x = midpoint + arrow_half_width + round_off_amount;
+ vpath[4].y = y0 - round_off_amount;
+
+ vpath[5].code = ART_LINETO;
+ vpath[5].x = midpoint + round_off_amount;
+ vpath[5].y = note_item->y1 - round_off_amount;
+
+ vpath[6].code = ART_LINETO;
+ vpath[6].x = midpoint - arrow_half_width - round_off_amount;
+ vpath[6].y = y0 - round_off_amount;
+
+ vpath[7].code = ART_LINETO;
+ vpath[7].x = x0 - round_off_amount;
+ vpath[7].y = y0 - round_off_amount;
+
+ vpath[8].code = ART_END;
+ vpath[8].x = 0;
+ vpath[8].y = 0;
+
+ vpath2 = art_vpath_affine_transform (vpath, affine);
+
+ gnome_canvas_item_update_svp_clip (item, &note_item->fill_svp, art_svp_from_vpath (vpath2), clip_path);
+
+ stroke_svp = art_svp_vpath_stroke (vpath2,
+ ART_PATH_STROKE_JOIN_MITER,
+ ART_PATH_STROKE_CAP_BUTT,
+ (note_item->width_pixels) ? note_item->width : (note_item->width * item->canvas->pixels_per_unit),
+ 4,
+ 25);
+
+ gnome_canvas_item_update_svp_clip (item, &note_item->outline_svp, stroke_svp, clip_path);
+ art_free (vpath2);
+
+ eel_gnome_canvas_item_request_redraw
+ (GNOME_CANVAS_ITEM (item));
+
+ } else {
+ /* xlib rendering - just update the bbox */
+
+ set_gc_foreground (note_item->fill_gc, note_item->fill_pixel);
+ set_gc_foreground (note_item->outline_gc, note_item->outline_pixel);
+ set_stipple (note_item->fill_gc, &note_item->fill_stipple, note_item->fill_stipple, TRUE);
+ set_stipple (note_item->outline_gc, &note_item->outline_stipple, note_item->outline_stipple, TRUE);
+ set_outline_gc_width (note_item);
+
+ get_bounds (note_item, &x0, &y0, &x1, &y1);
+ gnome_canvas_update_bbox (item, x0, y0, x1, y1);
+ }
+}
+
diff --git a/libnautilus-extensions/nautilus-canvas-note-item.h b/libnautilus-extensions/nautilus-canvas-note-item.h
new file mode 100644
index 000000000..3c763c3df
--- /dev/null
+++ b/libnautilus-extensions/nautilus-canvas-note-item.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+ nautilus-canvas-note-item.h: annotation canvas item for Nautilus
+
+ Copyright (C) 2001 Eazel, Inc.
+
+ This program 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; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program 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. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ based on gnome_canvas_rect_item by Federico Mena Quintero
+
+ Author: Andy Hertzfeld <andy@eazel.com>
+*/
+
+#ifndef NAUTILUS_CANVAS_NOTE_ITEM_H
+#define NAUTILUS_CANVAS_NOTE_ITEM_H
+
+#include <libgnome/gnome-defs.h>
+#include <libgnomeui/gnome-canvas.h>
+
+#include <libart_lgpl/art_svp.h>
+
+BEGIN_GNOME_DECLS
+
+#define NAUTILUS_TYPE_CANVAS_NOTE_ITEM (nautilus_canvas_note_item_get_type ())
+#define NAUTILUS_CANVAS_NOTE_ITEM(obj) (GTK_CHECK_CAST ((obj), NAUTILUS_TYPE_CANVAS_NOTE_ITEM, NautilusCanvasNoteItem))
+#define NAUTILUS_CANVAS_NOTE_ITEM_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), NAUTILUS_CANVAS_NOTE_ITEM, NautilusCanvasNoteItemClass))
+#define NAUTILUS_IS_CANVAS_NOTE_ITEM(obj) (GTK_CHECK_TYPE ((obj), NAUTILUS_TYPE_CANVAS_NOTE_ITEM))
+#define NAUTILUS_IS_CANVAS_NOTE_ITEM_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_CANVAS_NOTE_ITEM))
+
+
+typedef struct _NautilusCanvasNoteItem NautilusCanvasNoteItem;
+typedef struct _NautilusCanvasNoteItemClass NautilusCanvasNoteItemClass;
+
+struct _NautilusCanvasNoteItem {
+ GnomeCanvasItem item;
+
+ double x1; /* canvas coordinates of bounding box */
+ double y1;
+ double x2;
+ double y2;
+
+ double width; /* Outline width */
+
+ guint fill_color; /* Fill color, RGBA */
+ guint outline_color; /* Outline color, RGBA */
+
+ gulong fill_pixel; /* Fill color */
+ gulong outline_pixel; /* Outline color */
+
+ GdkBitmap *fill_stipple; /* Stipple for fill */
+ GdkBitmap *outline_stipple; /* Stipple for outline */
+
+ GdkGC *fill_gc; /* GC for filling */
+ GdkGC *outline_gc; /* GC for outline */
+
+ /* text message */
+ char *note_text; /* text for annotation */
+
+ /* Antialiased specific stuff follows */
+ ArtSVP *fill_svp; /* The SVP for the filled shape */
+ ArtSVP *outline_svp; /* The SVP for the outline shape */
+
+ /* Configuration flags */
+ unsigned int width_pixels : 1; /* Is outline width specified in pixels or units? */
+};
+
+struct _NautilusCanvasNoteItemClass {
+ GnomeCanvasItemClass parent_class;
+};
+
+/* Standard Gtk function */
+GtkType nautilus_canvas_note_item_get_type (void);
+
+
+END_GNOME_DECLS
+
+#endif
diff --git a/libnautilus-extensions/nautilus-file-utilities.c b/libnautilus-extensions/nautilus-file-utilities.c
index d015879cd..5f6c5c990 100644
--- a/libnautilus-extensions/nautilus-file-utilities.c
+++ b/libnautilus-extensions/nautilus-file-utilities.c
@@ -224,7 +224,7 @@ nautilus_get_user_main_directory (void)
/* If this fails to create the directory, nautilus_application_startup will
* notice and refuse to launch.
*/
-
+
/* install the default link sets */
nautilus_link_set_install (user_main_directory, "apps");
nautilus_link_set_install (user_main_directory, "home");
diff --git a/libnautilus-extensions/nautilus-file.c b/libnautilus-extensions/nautilus-file.c
index 8bd2b7c23..e251032dc 100644
--- a/libnautilus-extensions/nautilus-file.c
+++ b/libnautilus-extensions/nautilus-file.c
@@ -25,6 +25,7 @@
#include <config.h>
#include "nautilus-file.h"
+#include "nautilus-annotation.h"
#include "nautilus-directory-metafile.h"
#include "nautilus-directory-notify.h"
#include "nautilus-directory-private.h"
@@ -1500,6 +1501,10 @@ prepend_automatic_emblem_names (NautilusFile *file,
{
/* Prepend in reverse order. */
+ if (nautilus_annotation_has_annotation (file) > 0) {
+ names = g_list_prepend
+ (names, g_strdup (NAUTILUS_FILE_EMBLEM_ANNOTATION));
+ }
if (nautilus_file_is_in_trash (file)) {
names = g_list_prepend
(names, g_strdup (NAUTILUS_FILE_EMBLEM_NAME_TRASH));
diff --git a/libnautilus-extensions/nautilus-file.h b/libnautilus-extensions/nautilus-file.h
index 8a4892969..820cae932 100644
--- a/libnautilus-extensions/nautilus-file.h
+++ b/libnautilus-extensions/nautilus-file.h
@@ -71,6 +71,7 @@ typedef enum {
#define NAUTILUS_FILE_EMBLEM_NAME_CANT_READ "noread"
#define NAUTILUS_FILE_EMBLEM_NAME_CANT_WRITE "nowrite"
#define NAUTILUS_FILE_EMBLEM_NAME_TRASH "trash"
+#define NAUTILUS_FILE_EMBLEM_ANNOTATION "note"
typedef void (*NautilusFileCallback) (NautilusFile *file,
gpointer callback_data);
diff --git a/libnautilus-extensions/nautilus-global-preferences.c b/libnautilus-extensions/nautilus-global-preferences.c
index c9d59c17a..5603d7dee 100644
--- a/libnautilus-extensions/nautilus-global-preferences.c
+++ b/libnautilus-extensions/nautilus-global-preferences.c
@@ -368,6 +368,18 @@ static const PreferenceDefault preference_defaults[] = {
{ NAUTILUS_USER_LEVEL_NOVICE, GINT_TO_POINTER (TRUE) },
{ USER_LEVEL_NONE }
},
+ { NAUTILUS_PREFERENCES_LOOKUP_ANNOTATIONS,
+ PREFERENCE_BOOLEAN,
+ NAUTILUS_USER_LEVEL_INTERMEDIATE,
+ { NAUTILUS_USER_LEVEL_NOVICE, GINT_TO_POINTER (FALSE) },
+ { USER_LEVEL_NONE }
+ },
+ { NAUTILUS_PREFERENCES_DISPLAY_ANNOTATIONS,
+ PREFERENCE_BOOLEAN,
+ NAUTILUS_USER_LEVEL_INTERMEDIATE,
+ { NAUTILUS_USER_LEVEL_NOVICE, GINT_TO_POINTER (FALSE) },
+ { USER_LEVEL_NONE }
+ },
{ NAUTILUS_PREFERENCES_PREVIEW_SOUND,
PREFERENCE_INTEGER,
NAUTILUS_USER_LEVEL_INTERMEDIATE,
@@ -654,6 +666,14 @@ global_preferences_install_defaults (void)
preference_defaults[i].visible_user_level);
}
+ nautilus_preferences_default_set_boolean (NAUTILUS_PREFERENCES_LOOKUP_ANNOTATIONS,
+ NAUTILUS_USER_LEVEL_NOVICE,
+ FALSE);
+
+ nautilus_preferences_default_set_boolean (NAUTILUS_PREFERENCES_DISPLAY_ANNOTATIONS,
+ NAUTILUS_USER_LEVEL_NOVICE,
+ FALSE);
+
/* Add the gnome-vfs path to the list of monitored directories - for proxy settings */
nautilus_preferences_monitor_directory (SYSTEM_GNOME_VFS_PATH);
@@ -1041,6 +1061,24 @@ static PreferenceDialogItem tradeoffs_items[] = {
{ NULL }
};
+static PreferenceDialogItem annotation_items[] = {
+ { N_("Lookup File Annotations"),
+ NAUTILUS_PREFERENCES_LOOKUP_ANNOTATIONS,
+ N_("Use the service to lookup file annotations"),
+ NAUTILUS_PREFERENCE_ITEM_BOOLEAN,
+ NULL,
+ 0
+ },
+ { N_("Display File Annotations"),
+ NAUTILUS_PREFERENCES_DISPLAY_ANNOTATIONS,
+ N_("Display emblems for file annotations"),
+ NAUTILUS_PREFERENCE_ITEM_BOOLEAN,
+ NULL,
+ 0
+ },
+ { NULL, NULL, NULL, 0, NULL, 0 }
+};
+
static GtkWidget *
global_preferences_create_dialog (void)
{
@@ -1105,6 +1143,11 @@ global_preferences_create_dialog (void)
_("Speed Tradeoffs"),
tradeoffs_items);
+ /* Annotations */
+ global_preferences_populate_pane (preference_box,
+ _("Annotations"),
+ annotation_items);
+
/* Update the dialog so that the right items show up based on the current user level */
nautilus_preferences_dialog_update (NAUTILUS_PREFERENCES_DIALOG (prefs_dialog));
diff --git a/libnautilus-extensions/nautilus-global-preferences.h b/libnautilus-extensions/nautilus-global-preferences.h
index d52cd42fe..fdaac1e2b 100644
--- a/libnautilus-extensions/nautilus-global-preferences.h
+++ b/libnautilus-extensions/nautilus-global-preferences.h
@@ -110,6 +110,10 @@ enum
NAUTILUS_DEFAULT_FOLDER_VIEWER_LIST_VIEW
};
+/* enabling annotations */
+#define NAUTILUS_PREFERENCES_LOOKUP_ANNOTATIONS "preferences/lookup_annotations"
+#define NAUTILUS_PREFERENCES_DISPLAY_ANNOTATIONS "preferences/display_annotations"
+
/* Icon View */
#define NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_SORT_IN_REVERSE_ORDER "icon-view/default_sort_in_reverse_order"
#define NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_SORT_ORDER "icon-view/default_sort_order"
diff --git a/libnautilus-extensions/nautilus-icon-canvas-item.c b/libnautilus-extensions/nautilus-icon-canvas-item.c
index 2e9c11bb6..58b729a95 100644
--- a/libnautilus-extensions/nautilus-icon-canvas-item.c
+++ b/libnautilus-extensions/nautilus-icon-canvas-item.c
@@ -3,7 +3,6 @@
/* Nautilus - Icon canvas item class for icon container.
*
* Copyright (C) 2000 Eazel, Inc
- *
* Author: Andy Hertzfeld <andy@eazel.com>
*
* This library is free software; you can redistribute it and/or
@@ -29,8 +28,10 @@
#include <string.h>
#include <stdio.h>
#include <gtk/gtksignal.h>
+#include <gtk/gtkmain.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <libgnome/gnome-i18n.h>
+#include <libgnomeui/gnome-canvas-rect-ellipse.h>
#include <libgnomeui/gnome-canvas-util.h>
#include <libgnomeui/gnome-icon-text.h>
#include <libart_lgpl/art_rgb.h>
@@ -38,6 +39,7 @@
#include <libart_lgpl/art_rgb_rgba_affine.h>
#include <libart_lgpl/art_svp_vpath.h>
#include "nautilus-icon-private.h"
+#include "nautilus-canvas-note-item.h"
#include <eel/eel-string.h>
#include <eel/eel-art-extensions.h>
#include <eel/eel-glib-extensions.h>
@@ -54,9 +56,6 @@
#include <eel/eel-smooth-text-layout.h>
#include <eel/eel-smooth-text-layout-cache.h>
-/* Comment this out if the new smooth fonts code give you problems
- * This isnt meant to be permanent. Its just a precaution.
- */
#define EMBLEM_SPACING 2
/* gap between bottom of icon and start of text box */
@@ -80,6 +79,17 @@ struct NautilusIconCanvasItemDetails {
GdkFont *font;
NautilusEmblemAttachPoints *attach_points;
+ /* stuff for controls; if this gets too big, we'll put it in a separate struct */
+ GtkWidget *control; /* optional Bonobo control*/
+ guint control_destroy_id;
+
+ /* stuff for annotations - since these are infrequently used, we probably should
+ * combine them into a structure so we only use a pointer for every item eventually
+ */
+ GnomeCanvasItem *annotation;
+ int annotation_time_out;
+ int note_state;
+
/* Size of the text at current font. */
int text_width;
int text_height;
@@ -93,7 +103,7 @@ struct NautilusIconCanvasItemDetails {
guint is_highlighted_for_drop : 1;
guint show_stretch_handles : 1;
guint is_prelit : 1;
-
+ guint in_control_destroy : 1;
gboolean is_renaming;
/* Font stuff whilst in smooth mode */
@@ -112,7 +122,7 @@ enum {
ARG_EDITABLE_TEXT,
ARG_ADDITIONAL_TEXT,
ARG_FONT,
- ARG_HIGHLIGHTED_FOR_SELECTION,
+ ARG_HIGHLIGHTED_FOR_SELECTION,
ARG_HIGHLIGHTED_AS_KEYBOARD_FOCUS,
ARG_HIGHLIGHTED_FOR_DROP,
ARG_MODIFIER,
@@ -210,9 +220,13 @@ static void get_icon_canvas_rectangle (NautilusIconCanvasIt
static void emblem_layout_reset (EmblemLayout *layout,
NautilusIconCanvasItem *icon_item,
const ArtIRect *icon_rect);
-static gboolean emblem_layout_next (EmblemLayout *layout,
+static gboolean emblem_layout_next (EmblemLayout *layout,
GdkPixbuf **emblem_pixbuf,
ArtIRect *emblem_rect);
+static void get_emblem_rectangle (NautilusIconCanvasItem *icon_item,
+ int which_emblem,
+ ArtIRect *rect);
+
static void draw_pixbuf (GdkPixbuf *pixbuf,
GdkDrawable *drawable,
int x,
@@ -222,7 +236,6 @@ static gboolean hit_test_stretch_handle (NautilusIconCanvasIt
static gboolean icon_canvas_item_is_smooth (const NautilusIconCanvasItem *icon_item);
-
EEL_DEFINE_CLASS_BOILERPLATE (NautilusIconCanvasItem, nautilus_icon_canvas_item, GNOME_TYPE_CANVAS_ITEM)
static EelSmoothTextLayoutCache *layout_cache;
@@ -308,7 +321,8 @@ nautilus_icon_canvas_item_initialize (NautilusIconCanvasItem *icon_item)
/* set up the default font and size */
icon_item->details->smooth_font_size = 12;
- icon_item->details->smooth_font = eel_scalable_font_get_default_font ();
+ icon_item->details->smooth_font = eel_scalable_font_get_default_font ();
+ icon_item->details->annotation_time_out = -1;
}
/* Destroy handler for the icon canvas item. */
@@ -339,6 +353,11 @@ nautilus_icon_canvas_item_destroy (GtkObject *object)
gdk_font_unref (details->font);
}
+ if (details->control && !details->in_control_destroy) {
+ gtk_signal_disconnect (GTK_OBJECT (details->control), details->control_destroy_id);
+ gtk_widget_destroy (details->control);
+ }
+
gtk_object_unref (GTK_OBJECT (icon_item->details->smooth_font));
icon_item->details->smooth_font = NULL;
@@ -370,6 +389,43 @@ nautilus_icon_canvas_item_invalidate_label_size (NautilusIconCanvasItem *item)
item->details->text_height = -1;
}
+/* abstraction layer for icon width and height, to separate it from pixbuf with and height */
+static int
+nautilus_icon_canvas_item_get_icon_width (NautilusIconCanvasItem *item)
+{
+ GtkRequisition size_requisition;
+ double scale_factor = GNOME_CANVAS_ITEM (item)->canvas->pixels_per_unit;
+
+ if (item->details->control != NULL) {
+ gtk_widget_size_request (item->details->control, &size_requisition);
+ return size_requisition.width * scale_factor;
+ }
+
+ if (item->details->pixbuf == NULL) {
+ return NAUTILUS_ICON_SIZE_STANDARD;
+ }
+
+ return gdk_pixbuf_get_width (item->details->pixbuf);
+}
+
+static int
+nautilus_icon_canvas_item_get_icon_height (NautilusIconCanvasItem *item)
+{
+ GtkRequisition size_requisition;
+ double scale_factor = GNOME_CANVAS_ITEM (item)->canvas->pixels_per_unit;
+
+ if (item->details->control != NULL) {
+ gtk_widget_size_request (item->details->control, &size_requisition);
+ return size_requisition.height * scale_factor;
+ }
+ if (item->details->pixbuf == NULL) {
+ return NAUTILUS_ICON_SIZE_STANDARD;
+ }
+
+ return gdk_pixbuf_get_height (item->details->pixbuf);
+}
+
+
/* Set_arg handler for the icon item. */
static void
nautilus_icon_canvas_item_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
@@ -452,8 +508,8 @@ nautilus_icon_canvas_item_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
case ARG_SMOOTH_FONT_SIZE:
nautilus_icon_canvas_item_set_smooth_font_size (NAUTILUS_ICON_CANVAS_ITEM (object),
GTK_VALUE_INT (*arg));
- break;
-
+ break;
+
default:
g_warning ("nautilus_icons_view_item_item_set_arg on unknown argument");
return;
@@ -462,12 +518,27 @@ nautilus_icon_canvas_item_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (object));
}
+/* handler for the control's destroy signal */
+static void
+do_control_destroy (GtkObject *object, gpointer data)
+{
+ NautilusIconCanvasItemDetails *details;
+
+ details = NAUTILUS_ICON_CANVAS_ITEM (data)->details;
+
+ details->in_control_destroy = TRUE;
+
+ gtk_object_destroy (GTK_OBJECT (data));
+}
+
/* Get_arg handler for the icon item */
static void
nautilus_icon_canvas_item_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
{
NautilusIconCanvasItemDetails *details;
+ GnomeCanvasItem *item;
+ item = GNOME_CANVAS_ITEM (object);
details = NAUTILUS_ICON_CANVAS_ITEM (object)->details;
switch (arg_id) {
@@ -515,12 +586,27 @@ GdkPixbuf *
nautilus_icon_canvas_item_get_image (NautilusIconCanvasItem *item)
{
NautilusIconCanvasItemDetails *details;
-
+ int width, height;
+ GdkPixbuf *pixbuf;
+
g_return_val_if_fail (NAUTILUS_IS_ICON_CANVAS_ITEM (item), NULL);
details = item->details;
- return details->pixbuf;
+ if (details->control) {
+ width = details->control->allocation.width;
+ height = details->control->allocation.height;
+ pixbuf = eel_gdk_pixbuf_get_from_window_safe (details->control->window,
+ details->control->allocation.x,
+ details->control->allocation.y,
+ details->control->allocation.width,
+ details->control->allocation.height);
+ } else {
+ pixbuf = details->pixbuf;
+ gdk_pixbuf_ref (pixbuf);
+ }
+
+ return pixbuf;
}
void
@@ -622,6 +708,12 @@ recompute_bounding_box (NautilusIconCanvasItem *icon_item)
item->y1 = top_left.y;
item->x2 = bottom_right.x;
item->y2 = bottom_right.y;
+
+ if (icon_item->details->control)
+ gtk_layout_move (GTK_LAYOUT (item->canvas), icon_item->details->control,
+ item->x1 + item->canvas->zoom_xofs,
+ item->y1 + item->canvas->zoom_yofs);
+
}
static void
@@ -636,13 +728,16 @@ compute_text_rectangle (NautilusIconCanvasItem *item,
text_rect->y1 = text_rect->y0 + item->details->text_height;
}
+
void
nautilus_icon_canvas_item_update_bounds (NautilusIconCanvasItem *item)
{
ArtIRect before, after, emblem_rect;
EmblemLayout emblem_layout;
GdkPixbuf *emblem_pixbuf;
-
+ GtkRequisition size_requisition;
+ int item_width, item_height;
+
/* Compute new bounds. */
eel_gnome_canvas_item_get_current_canvas_bounds
(GNOME_CANVAS_ITEM (item), &before);
@@ -669,6 +764,16 @@ nautilus_icon_canvas_item_update_bounds (NautilusIconCanvasItem *item)
art_irect_union (&item->details->emblem_rect, &item->details->emblem_rect, &emblem_rect);
}
+ /* if there is an embedded control, make a size request and size accordingly */
+ if (item->details->control) {
+ /* size the control appropriately */
+ gtk_widget_size_request (item->details->control, &size_requisition);
+ item_width = size_requisition.width * GNOME_CANVAS_ITEM (item)->canvas->pixels_per_unit;
+ item_height = size_requisition.height * GNOME_CANVAS_ITEM (item)->canvas->pixels_per_unit;
+
+ gtk_widget_set_usize (item->details->control, item_width, item_height);
+ }
+
/* Send out the bounds_changed signal and queue a redraw. */
eel_gnome_canvas_request_redraw_rectangle
(GNOME_CANVAS_ITEM (item)->canvas, &before);
@@ -809,7 +914,7 @@ draw_or_measure_label_text (NautilusIconCanvasItem *item,
canvas_item = GNOME_CANVAS_ITEM (item);
if (drawable != NULL) {
- icon_width = details->pixbuf == NULL ? 0 : gdk_pixbuf_get_width (details->pixbuf);
+ icon_width = details->pixbuf == NULL ? 0 : nautilus_icon_canvas_item_get_icon_width (item);
gc = gdk_gc_new (canvas_item->canvas->layout.bin_window);
gdk_gc_get_values (gc, &save_gc);
}
@@ -1125,6 +1230,7 @@ emblem_layout_next (EmblemLayout *layout,
/* Advance to the next emblem. */
layout->emblem = layout->emblem->next;
+ layout->index += 1;
attach_points = layout->icon_item->details->attach_points;
if (attach_points != NULL) {
@@ -1135,8 +1241,6 @@ emblem_layout_next (EmblemLayout *layout,
x = layout->icon_rect.x0 + attach_points->points[layout->index].x;
y = layout->icon_rect.y0 + attach_points->points[layout->index].y;
- layout->index += 1;
-
/* Return the rectangle and pixbuf. */
*emblem_pixbuf = pixbuf;
emblem_rect->x0 = x - width / 2;
@@ -1211,11 +1315,17 @@ emblem_layout_next (EmblemLayout *layout,
/* Return the rectangle and pixbuf. */
*emblem_pixbuf = pixbuf;
- emblem_rect->x0 = x - width / 2;
- emblem_rect->y0 = y - height / 2;
+ if (layout->icon_item->details->control) {
+ emblem_rect->x0 = x;
+ emblem_rect->y0 = y;
+ } else {
+ emblem_rect->x0 = x - width / 2;
+ emblem_rect->y0 = y - height / 2;
+ }
+
emblem_rect->x1 = emblem_rect->x0 + width;
emblem_rect->y1 = emblem_rect->y0 + height;
-
+
return TRUE;
}
@@ -1330,10 +1440,6 @@ nautilus_icon_canvas_item_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
icon_item = NAUTILUS_ICON_CANVAS_ITEM (item);
details = icon_item->details;
- /* Draw the pixbuf. */
- if (details->pixbuf == NULL) {
- return;
- }
/* Compute icon rectangle in drawable coordinates. */
icon_rect = icon_item->details->canvas_rect;
@@ -1341,12 +1447,29 @@ nautilus_icon_canvas_item_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
icon_rect.y0 -= y;
icon_rect.x1 -= x;
icon_rect.y1 -= y;
+ /* draw the icon or widget */
+ if (icon_item->details->control) {
+ gtk_widget_queue_draw (icon_item->details->control);
+ } else {
+ if (details->pixbuf != NULL) {
+
+ /* Compute icon rectangle in drawable coordinates. */
+ get_icon_canvas_rectangle (icon_item, &icon_rect);
+ icon_rect.x0 -= x;
+ icon_rect.y0 -= y;
+ icon_rect.x1 -= x;
+ icon_rect.y1 -= y;
+
+ /* if the pre-lit or selection flag is set, make a pre-lit or darkened pixbuf and draw that instead */
+ temp_pixbuf = map_pixbuf (icon_item);
+ draw_pixbuf (temp_pixbuf, drawable, icon_rect.x0, icon_rect.y0);
+
+ if (temp_pixbuf != details->pixbuf) {
+ gdk_pixbuf_unref (temp_pixbuf);
+ }
+
+ }
- /* if the pre-lit or selection flag is set, make a pre-lit or darkened pixbuf and draw that instead */
- temp_pixbuf = map_pixbuf (icon_item);
- draw_pixbuf (temp_pixbuf, drawable, icon_rect.x0, icon_rect.y0);
- if (temp_pixbuf != details->pixbuf) {
- gdk_pixbuf_unref (temp_pixbuf);
}
/* Draw the emblem pixbufs. */
@@ -1431,7 +1554,7 @@ draw_or_measure_label_text_aa (NautilusIconCanvasItem *item,
if (destination_pixbuf == NULL ) {
icon_width = 0;
} else {
- icon_width = details->pixbuf == NULL ? 0 : gdk_pixbuf_get_width (details->pixbuf);
+ icon_width = details->pixbuf == NULL ? 0 : nautilus_icon_canvas_item_get_icon_width (item);
}
max_text_width = floor (nautilus_icon_canvas_item_get_max_text_width (item));
@@ -1706,15 +1829,21 @@ nautilus_icon_canvas_item_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
gnome_canvas_buf_ensure_buf (buf);
buf->is_bg = FALSE;
}
-
- /* draw the icon */
- eel_gnome_canvas_draw_pixbuf (buf, temp_pixbuf, icon_rect.x0, icon_rect.y0);
+
+ /* draw the icon or widget */
+ if (icon_item->details->control) {
+ gtk_widget_queue_draw (icon_item->details->control);
+ } else {
+ eel_gnome_canvas_draw_pixbuf (buf, temp_pixbuf, icon_rect.x0, icon_rect.y0);
+ }
if (temp_pixbuf != icon_item->details->pixbuf) {
gdk_pixbuf_unref (temp_pixbuf);
}
-
- /* draw the emblems */
+
+ /* draw the emblems */
+ get_icon_canvas_rectangle (icon_item, &icon_rect);
+
emblem_layout_reset (&emblem_layout, icon_item, &icon_rect);
while (emblem_layout_next (&emblem_layout, &emblem_pixbuf, &emblem_rect)) {
eel_gnome_canvas_draw_pixbuf (buf, emblem_pixbuf, emblem_rect.x0, emblem_rect.y0);
@@ -1728,6 +1857,124 @@ nautilus_icon_canvas_item_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
draw_label_text_aa (icon_item, buf, icon_rect.x0, icon_rect.y1, x_delta);
}
+/* create an annotation for the emblem designated by the passed-in index */
+static void
+create_annotation (NautilusIconCanvasItem *icon_item, int emblem_index)
+{
+ uint fill_color, outline_color;
+ double top, left;
+ double right, bottom;
+ ArtDRect icon_rect;
+ ArtIRect emblem_rect;
+ int emblem_x, emblem_y;
+ double world_emblem_x, world_emblem_y;
+ int annotation_width;
+ char *note_text;
+ GnomeCanvas *canvas;
+ GnomeCanvasItem *item;
+
+ /* compute the position for the top left of the annotation */
+ nautilus_icon_canvas_item_get_icon_rectangle (icon_item, &icon_rect);
+ left = icon_rect.x0 + 8.0;
+ top = icon_rect.y0 + 8.0;
+
+ fill_color = 0xFFFF75E0;
+ outline_color = 0x000000FF;
+
+ canvas = GNOME_CANVAS_ITEM (icon_item)->canvas;
+ item = GNOME_CANVAS_ITEM (icon_item);
+
+ note_text = nautilus_icon_container_get_note_text (NAUTILUS_ICON_CONTAINER (canvas), icon_item->user_data, emblem_index);
+
+ icon_item->details->annotation = gnome_canvas_item_new
+ (gnome_canvas_root (canvas),
+ nautilus_canvas_note_item_get_type (),
+ "x1", left,
+ "y1", top,
+ "fill_color_rgba", fill_color,
+ "outline_color_rgba", outline_color,
+ "note_text", note_text,
+ "width_pixels", 1,
+ NULL);
+
+ g_free (note_text);
+
+ /* reposition the item now that it's had a chance to be properly sized */
+ if (canvas->aa) {
+ get_emblem_rectangle (icon_item, emblem_index, &emblem_rect);
+ annotation_width = icon_item->details->annotation->x2 - icon_item->details->annotation->x1;
+
+ emblem_x = (emblem_rect.x0 + emblem_rect.x1) / 2;
+ emblem_y = (emblem_rect.y0 + emblem_rect.y1) / 2;
+ gnome_canvas_c2w (canvas, emblem_x, emblem_y, &world_emblem_x, &world_emblem_y);
+
+ left = world_emblem_x - (annotation_width / 2.0 );
+ top = world_emblem_y;
+ right = left + annotation_width;
+ bottom = top + icon_item->details->annotation->y2 - icon_item->details->annotation->y1;
+
+ gnome_canvas_item_set (icon_item->details->annotation, "x1", left, "y1", top, "x2", right, "y2", bottom, NULL);
+ }
+
+ gnome_canvas_item_raise_to_top (icon_item->details->annotation);
+}
+
+/* remove any annotation that's showing */
+static void
+remove_annotation (NautilusIconCanvasItem *icon_item)
+{
+ if (icon_item->details->annotation != NULL) {
+ gtk_object_destroy (GTK_OBJECT (icon_item->details->annotation));
+ icon_item->details->annotation = NULL;
+ icon_item->details->note_state = 0;
+ }
+}
+
+/* handle the timeout firing by creating the annotation */
+static int
+create_annotation_timeout_callback (gpointer callback_data)
+{
+ NautilusIconCanvasItem *icon_item;
+
+ icon_item = NAUTILUS_ICON_CANVAS_ITEM (callback_data);
+ create_annotation (icon_item, icon_item->details->note_state);
+
+ icon_item->details->annotation_time_out = -1;
+ return 0;
+}
+
+/* manage showing and hiding annotations, based on mouse-over the passed-in emblem */
+static void
+nautilus_icon_canvas_item_set_note_state (NautilusIconCanvasItem *icon_item, int new_state)
+{
+ /* nothing to do if nothing changed */
+ if (new_state == icon_item->details->note_state) {
+ return;
+ }
+
+ /* if there already is a timeout in progress and we're showing one, just wait for it to fire */
+ if (new_state > 0 && icon_item->details->annotation_time_out >= 0) {
+ return;
+ }
+
+ /* get rid of the old annotation, if there was one */
+ if (icon_item->details->annotation) {
+ remove_annotation (icon_item);
+ }
+
+ if (icon_item->details->annotation_time_out >= 0) {
+ gtk_timeout_remove (icon_item->details->annotation_time_out);
+ icon_item->details->annotation_time_out = -1;
+ }
+
+ icon_item->details->note_state = new_state;
+
+ /* add a timeout to create the new annotation */
+ if (new_state > 0) {
+ icon_item->details->annotation_time_out = gtk_timeout_add (750, create_annotation_timeout_callback, icon_item);
+ }
+}
+
/* handle events */
@@ -1735,7 +1982,12 @@ static int
nautilus_icon_canvas_item_event (GnomeCanvasItem *item, GdkEvent *event)
{
NautilusIconCanvasItem *icon_item;
-
+ GdkEventMotion *motion_event;
+ ArtIRect hit_rect;
+ ArtDRect world_rect;
+ HitType hit_type;
+ int hit_index, emblem_state;
+
icon_item = NAUTILUS_ICON_CANVAS_ITEM (item);
switch (event->type) {
@@ -1782,10 +2034,29 @@ nautilus_icon_canvas_item_event (GnomeCanvasItem *item, GdkEvent *event)
icon_item->details->is_prelit = FALSE;
icon_item->details->is_active = 0;
icon_item->details->is_highlighted_for_drop = FALSE;
+
+ nautilus_icon_canvas_item_set_note_state (icon_item, 0);
gnome_canvas_item_request_update (item);
}
return TRUE;
+
+ case GDK_MOTION_NOTIFY:
+ motion_event = (GdkEventMotion*) event;
+
+ world_rect.x0 = motion_event->x;
+ world_rect.y0 = motion_event->y;
+ world_rect.x1 = world_rect.x0 + 1.0;
+ world_rect.y1 = world_rect.y0 + 1.0;
+
+ eel_gnome_canvas_world_to_canvas_rectangle
+ (GNOME_CANVAS_ITEM (item)->canvas, &world_rect, &hit_rect);
+ /* hit-test so we can handle tooltips for emblems */
+ nautilus_icon_canvas_item_hit_test_full (icon_item, &hit_rect, &hit_type, &hit_index);
+ emblem_state = hit_type == EMBLEM_HIT ? hit_index : 0;
+ nautilus_icon_canvas_item_set_note_state (icon_item, emblem_state);
+ return TRUE;
+
default:
/* Don't eat up other events; icon container might use them. */
return FALSE;
@@ -1838,10 +2109,14 @@ hit_test_pixbuf (GdkPixbuf *pixbuf, const ArtIRect *pixbuf_location, const ArtIR
return FALSE;
}
-static gboolean
-hit_test (NautilusIconCanvasItem *icon_item, const ArtIRect *canvas_rect)
+gboolean
+nautilus_icon_canvas_item_hit_test_full (NautilusIconCanvasItem *icon_item,
+ const ArtIRect *canvas_rect,
+ HitType *hit_type,
+ int *hit_index)
{
NautilusIconCanvasItemDetails *details;
+ ArtIRect icon_rect;
ArtIRect emblem_rect;
EmblemLayout emblem_layout;
GdkPixbuf *emblem_pixbuf;
@@ -1855,12 +2130,40 @@ hit_test (NautilusIconCanvasItem *icon_item, const ArtIRect *canvas_rect)
return FALSE;
}
+ /* default to -1, which means nothing was hit */
+ if (hit_index != NULL) {
+ *hit_index = -1;
+ }
+
/* Check for hits in the stretch handles. */
if (hit_test_stretch_handle (icon_item, canvas_rect)) {
+ if (hit_type != NULL) {
+ *hit_type = STRETCH_HANDLE_HIT;
+ }
return TRUE;
}
/* Check for hit in the icon. If we're highlighted for dropping, anywhere in the rect is OK */
+ get_icon_canvas_rectangle (icon_item, &icon_rect);
+
+ /* Check for hit in the emblem pixbufs first, since they appear on top of the icon. */
+ emblem_layout_reset (&emblem_layout, icon_item, &icon_item->details->canvas_rect);
+ while (emblem_layout_next (&emblem_layout, &emblem_pixbuf, &emblem_rect)) {
+ if (hit_test_pixbuf (emblem_pixbuf, &emblem_rect, canvas_rect)) {
+ if (hit_type != NULL) {
+ *hit_type = EMBLEM_HIT;
+ }
+ if (hit_index != NULL) {
+ *hit_index = emblem_layout.index;
+ }
+ return TRUE;
+ }
+ }
+
+ if (hit_type != NULL) {
+ *hit_type = ICON_HIT;
+ }
+
if (icon_item->details->is_highlighted_for_drop) {
if (eel_art_irect_hits_irect (&icon_item->details->canvas_rect, canvas_rect)) {
return TRUE;
@@ -1874,17 +2177,17 @@ hit_test (NautilusIconCanvasItem *icon_item, const ArtIRect *canvas_rect)
/* Check for hit in the text. */
if (eel_art_irect_hits_irect (&details->text_rect, canvas_rect)
&& !icon_item->details->is_renaming) {
+ if (hit_type != NULL) {
+ *hit_type = LABEL_HIT;
+ }
return TRUE;
}
- /* Check for hit in the emblem pixbufs. */
- emblem_layout_reset (&emblem_layout, icon_item, &icon_item->details->canvas_rect);
- while (emblem_layout_next (&emblem_layout, &emblem_pixbuf, &emblem_rect)) {
- if (hit_test_pixbuf (emblem_pixbuf, &emblem_rect, canvas_rect)) {
- return TRUE;
- }
+ /* there wasn't a hit, so indicate that */
+ if (hit_type != NULL) {
+ *hit_type = NO_HIT;
}
-
+
return FALSE;
}
@@ -1900,7 +2203,7 @@ nautilus_icon_canvas_item_point (GnomeCanvasItem *item, double x, double y, int
canvas_rect.y0 = cy;
canvas_rect.x1 = cx + 1;
canvas_rect.y1 = cy + 1;
- if (hit_test (NAUTILUS_ICON_CANVAS_ITEM (item), &canvas_rect)) {
+ if (nautilus_icon_canvas_item_hit_test_rectangle (NAUTILUS_ICON_CANVAS_ITEM (item), &canvas_rect)) {
return 0.0;
} else {
/* This value means not hit.
@@ -1939,8 +2242,8 @@ nautilus_icon_canvas_item_bounds (GnomeCanvasItem *item,
icon_rect.x1 = 0;
icon_rect.y1 = 0;
} else {
- icon_rect.x1 = gdk_pixbuf_get_width (details->pixbuf);
- icon_rect.y1 = gdk_pixbuf_get_height (details->pixbuf);
+ icon_rect.x1 = nautilus_icon_canvas_item_get_icon_width (icon_item);
+ icon_rect.y1 = nautilus_icon_canvas_item_get_icon_height (icon_item);
}
/* Compute text rectangle. */
@@ -1986,10 +2289,32 @@ nautilus_icon_canvas_item_get_icon_rectangle (NautilusIconCanvasItem *item,
pixbuf = item->details->pixbuf;
pixels_per_unit = GNOME_CANVAS_ITEM (item)->canvas->pixels_per_unit;
- rect->x1 = rect->x0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_width (pixbuf)) / pixels_per_unit;
- rect->y1 = rect->y0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_height (pixbuf)) / pixels_per_unit;
+ rect->x1 = rect->x0 + (pixbuf == NULL ? 0 : nautilus_icon_canvas_item_get_icon_width (item)) / pixels_per_unit;
+ rect->y1 = rect->y0 + (pixbuf == NULL ? 0 : nautilus_icon_canvas_item_get_icon_height (item)) / pixels_per_unit;
}
+static void
+get_emblem_rectangle (NautilusIconCanvasItem *icon_item,
+ int which_emblem,
+ ArtIRect *rect)
+{
+ EmblemLayout emblem_layout;
+ GdkPixbuf *pixbuf;
+ int emblem_index;
+
+ emblem_layout_reset (&emblem_layout, icon_item, &icon_item->details->canvas_rect);
+ emblem_index = 0;
+
+ rect->x0 = 0;
+ rect->y0 = 0;
+ rect->x1 = 0;
+ rect->y1 = 0;
+
+ while (emblem_index < which_emblem && emblem_layout_next (&emblem_layout, &pixbuf, rect)) {
+ emblem_index += 1;
+ }
+}
+
/* Get the rectangle of the icon only, in canvas coordinates. */
static void
get_icon_canvas_rectangle (NautilusIconCanvasItem *item,
@@ -2013,8 +2338,8 @@ get_icon_canvas_rectangle (NautilusIconCanvasItem *item,
pixbuf = item->details->pixbuf;
- rect->x1 = rect->x0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_width (pixbuf));
- rect->y1 = rect->y0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_height (pixbuf));
+ rect->x1 = rect->x0 + (pixbuf == NULL ? 0 : nautilus_icon_canvas_item_get_icon_width (item));
+ rect->y1 = rect->y0 + (pixbuf == NULL ? 0 : nautilus_icon_canvas_item_get_icon_height (item));
}
void
@@ -2110,10 +2435,11 @@ nautilus_icon_canvas_item_hit_test_stretch_handles (NautilusIconCanvasItem *item
gboolean
nautilus_icon_canvas_item_hit_test_rectangle (NautilusIconCanvasItem *item, const ArtIRect *canvas_rect)
{
+
g_return_val_if_fail (NAUTILUS_IS_ICON_CANVAS_ITEM (item), FALSE);
g_return_val_if_fail (canvas_rect != NULL, FALSE);
- return hit_test (item, canvas_rect);
+ return nautilus_icon_canvas_item_hit_test_full (item, canvas_rect, NULL, NULL);
}
const char *
@@ -2171,6 +2497,41 @@ nautilus_icon_canvas_item_set_smooth_font (NautilusIconCanvasItem *icon_item,
}
}
+GtkWidget *
+nautilus_icon_canvas_item_get_control (NautilusIconCanvasItem *icon_item)
+{
+ return icon_item->details->control;
+}
+
+void
+nautilus_icon_canvas_item_set_control (NautilusIconCanvasItem *icon_item, GtkWidget *control)
+{
+ GnomeCanvasItem *item;
+
+ if (icon_item->details->control == control) {
+ return;
+ }
+
+ item = GNOME_CANVAS_ITEM (icon_item);
+ if (icon_item->details->control) {
+ gtk_signal_disconnect (GTK_OBJECT (icon_item->details->control), icon_item->details->control_destroy_id);
+ gtk_container_remove (GTK_CONTAINER (item->canvas), icon_item->details->control);
+ icon_item->details->control = NULL;
+ }
+
+ if (control) {
+ icon_item->details->control = control;
+ icon_item->details->control_destroy_id = gtk_signal_connect (GTK_OBJECT (control),
+ "destroy",
+ (GtkSignalFunc) do_control_destroy,
+ item);
+ gtk_widget_show (control);
+ gtk_layout_put (GTK_LAYOUT (item->canvas), control,
+ item->x1 + item->canvas->zoom_xofs,
+ item->y1 + item->canvas->zoom_yofs);
+ }
+}
+
void
nautilus_icon_canvas_item_set_smooth_font_size (NautilusIconCanvasItem *icon_item,
int font_size)
diff --git a/libnautilus-extensions/nautilus-icon-canvas-item.h b/libnautilus-extensions/nautilus-icon-canvas-item.h
index f7be68e70..2f9f90a34 100644
--- a/libnautilus-extensions/nautilus-icon-canvas-item.h
+++ b/libnautilus-extensions/nautilus-icon-canvas-item.h
@@ -44,6 +44,14 @@ BEGIN_GNOME_DECLS
#define NAUTILUS_IS_ICON_CANVAS_ITEM_CLASS(klass) \
(GTK_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_ICON_CANVAS_ITEM))
+typedef enum {
+ NO_HIT,
+ ICON_HIT,
+ LABEL_HIT,
+ STRETCH_HANDLE_HIT,
+ EMBLEM_HIT
+} HitType;
+
typedef struct NautilusIconCanvasItem NautilusIconCanvasItem;
typedef struct NautilusIconCanvasItemClass NautilusIconCanvasItemClass;
typedef struct NautilusIconCanvasItemDetails NautilusIconCanvasItemDetails;
@@ -80,10 +88,19 @@ const char *nautilus_icon_canvas_item_get_editable_text (NautilusIconCanv
void nautilus_icon_canvas_item_set_renaming (NautilusIconCanvasItem *icon_item,
gboolean state);
+GtkWidget * nautilus_icon_canvas_item_get_control (NautilusIconCanvasItem *icon_item);
+void nautilus_icon_canvas_item_set_control (NautilusIconCanvasItem *icon_item,
+ GtkWidget *control);
+
/* geometry and hit testing */
gboolean nautilus_icon_canvas_item_hit_test_rectangle (NautilusIconCanvasItem *item,
const ArtIRect *canvas_rect);
+gboolean nautilus_icon_canvas_item_hit_test_full (NautilusIconCanvasItem *icon_item,
+ const ArtIRect *canvas_rect,
+ HitType *hit_type,
+ int *hit_index);
+
gboolean nautilus_icon_canvas_item_hit_test_stretch_handles (NautilusIconCanvasItem *item,
const ArtPoint *world_point);
void nautilus_icon_canvas_item_invalidate_label_size (NautilusIconCanvasItem *item);
diff --git a/libnautilus-extensions/nautilus-icon-container.c b/libnautilus-extensions/nautilus-icon-container.c
index 9daecc782..e41f03522 100644
--- a/libnautilus-extensions/nautilus-icon-container.c
+++ b/libnautilus-extensions/nautilus-icon-container.c
@@ -119,10 +119,12 @@ enum {
*/
};
-static void activate_selected_items (NautilusIconContainer *container);
+static void activate_selected_items (NautilusIconContainer *container,
+ int select_location);
static void nautilus_icon_container_initialize_class (NautilusIconContainerClass *class);
static void nautilus_icon_container_initialize (NautilusIconContainer *container);
static void nautilus_icon_container_theme_changed (gpointer user_data);
+static void nautilus_icon_container_annotation_changed (gpointer user_data);
static void compute_stretch (StretchState *start,
StretchState *current);
@@ -166,10 +168,12 @@ enum {
CONTEXT_CLICK_SELECTION,
MIDDLE_CLICK,
GET_CONTAINER_URI,
+ GET_ICON_CONTROL,
GET_ICON_IMAGES,
GET_ICON_TEXT,
GET_ICON_URI,
GET_ICON_DROP_TARGET_URI,
+ GET_ICON_ANNOTATION,
GET_STORED_ICON_POSITION,
ICON_POSITION_CHANGED,
ICON_TEXT_CHANGED,
@@ -2286,7 +2290,6 @@ select_previous_or_next_name (NautilusIconContainer *container,
}
/* GtkObject methods. */
-
static void
destroy (GtkObject *object)
{
@@ -2333,6 +2336,10 @@ destroy (GtkObject *object)
gtk_object_destroy (GTK_OBJECT (container->details->rename_widget));
}
+ /* remove the annotation preference callbacks */
+ nautilus_preferences_remove_callback (NAUTILUS_PREFERENCES_LOOKUP_ANNOTATIONS, nautilus_icon_container_annotation_changed, container);
+ nautilus_preferences_remove_callback (NAUTILUS_PREFERENCES_DISPLAY_ANNOTATIONS, nautilus_icon_container_annotation_changed, container);
+
/* FIXME: The code to extract colors from the theme should be in FMDirectoryView, not here.
* The NautilusIconContainer class should simply provide calls to set the colors.
*/
@@ -2530,13 +2537,42 @@ button_press_event (GtkWidget *widget,
return return_value;
}
+/* utility routine to determine which portion of an icon was clicked, and return the
+ * emblem index if an emblem was clicked on
+ */
+static int
+hit_test_item (NautilusIconCanvasItem *icon_item, GdkEventButton *event)
+{
+ ArtDRect world_rect;
+ ArtIRect canvas_rect;
+ HitType hit_type;
+ int emblem_index;
+
+ gnome_canvas_window_to_world (GNOME_CANVAS_ITEM (icon_item)->canvas, event->x, event->y,
+ &world_rect.x0, &world_rect.y0);
+ world_rect.x1 = world_rect.x0 + 1.0;
+ world_rect.y1 = world_rect.y0 + 1.0;
+
+ eel_gnome_canvas_world_to_canvas_rectangle
+ (GNOME_CANVAS_ITEM (icon_item)->canvas, &world_rect, &canvas_rect);
+
+ if (nautilus_icon_canvas_item_hit_test_full (icon_item, &canvas_rect, &hit_type, &emblem_index)) {
+ if (hit_type == EMBLEM_HIT) {
+ return emblem_index;
+ }
+ }
+ return 0;
+}
+
static void
nautilus_icon_container_did_not_drag (NautilusIconContainer *container,
GdkEventButton *event)
{
+ int click_location;
NautilusIconContainerDetails *details;
+
details = container->details;
-
+
if (!button_event_modifies_selection (event) && !details->drag_icon->is_selected) {
gboolean selection_changed;
@@ -2565,7 +2601,8 @@ nautilus_icon_container_did_not_drag (NautilusIconContainer *container,
* NautilusList goes the other way because its "links" seem
* much more link-like.
*/
- activate_selected_items (container);
+ click_location = hit_test_item (details->drag_icon->item, event);
+ activate_selected_items (container, click_location);
}
}
}
@@ -2997,7 +3034,7 @@ key_press_event (GtkWidget *widget,
break;
case GDK_Return:
case GDK_KP_Enter:
- activate_selected_items (container);
+ activate_selected_items (container, 0);
handled = TRUE;
break;
case GDK_Escape:
@@ -3070,9 +3107,10 @@ nautilus_icon_container_initialize_class (NautilusIconContainerClass *class)
object_class->type,
GTK_SIGNAL_OFFSET (NautilusIconContainerClass,
activate),
- gtk_marshal_NONE__POINTER,
- GTK_TYPE_NONE, 1,
- GTK_TYPE_POINTER);
+ gtk_marshal_NONE__POINTER_INT,
+ GTK_TYPE_NONE, 2,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_INT);
signals[CONTEXT_CLICK_SELECTION]
= gtk_signal_new ("context_click_selection",
GTK_RUN_LAST,
@@ -3147,6 +3185,16 @@ nautilus_icon_container_initialize_class (NautilusIconContainerClass *class)
gtk_marshal_NONE__POINTER,
GTK_TYPE_NONE, 1,
GTK_TYPE_POINTER);
+ signals[GET_ICON_CONTROL]
+ = gtk_signal_new ("get_icon_control",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (NautilusIconContainerClass,
+ get_icon_control),
+ gtk_marshal_NONE__POINTER_POINTER,
+ GTK_TYPE_NONE, 2,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_POINTER);
signals[GET_ICON_IMAGES]
= gtk_signal_new ("get_icon_images",
GTK_RUN_LAST,
@@ -3187,6 +3235,16 @@ nautilus_icon_container_initialize_class (NautilusIconContainerClass *class)
eel_gtk_marshal_STRING__POINTER,
GTK_TYPE_STRING, 1,
GTK_TYPE_POINTER);
+ signals[GET_ICON_ANNOTATION]
+ = gtk_signal_new ("get_icon_annotation",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (NautilusIconContainerClass,
+ get_icon_annotation),
+ eel_gtk_marshal_STRING__POINTER_INT,
+ GTK_TYPE_STRING, 2,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_INT);
signals[COMPARE_ICONS]
= gtk_signal_new ("compare_icons",
GTK_RUN_LAST,
@@ -3384,6 +3442,10 @@ nautilus_icon_container_initialize (NautilusIconContainer *container)
gtk_signal_connect (GTK_OBJECT (container), "focus-out-event", handle_focus_out_event, NULL);
+ /* add callbacks to notify us when the annotation state changes */
+ nautilus_preferences_add_callback (NAUTILUS_PREFERENCES_LOOKUP_ANNOTATIONS, nautilus_icon_container_annotation_changed, container);
+ nautilus_preferences_add_callback (NAUTILUS_PREFERENCES_DISPLAY_ANNOTATIONS, nautilus_icon_container_annotation_changed, container);
+
/* FIXME: The code to extract colors from the theme should be in FMDirectoryView, not here.
* The NautilusIconContainer class should simply provide calls to set the colors.
*/
@@ -3519,7 +3581,7 @@ handle_icon_button_press (NautilusIconContainer *container,
details->drag_button = 0;
details->drag_icon = NULL;
- activate_selected_items (container);
+ activate_selected_items (container, 0);
}
return TRUE;
@@ -3674,7 +3736,7 @@ icon_destroy (NautilusIconContainer *container,
/* activate any selected items in the container */
static void
-activate_selected_items (NautilusIconContainer *container)
+activate_selected_items (NautilusIconContainer *container, int select_location)
{
GList *selection;
@@ -3684,7 +3746,8 @@ activate_selected_items (NautilusIconContainer *container)
if (selection != NULL) {
gtk_signal_emit (GTK_OBJECT (container),
signals[ACTIVATE],
- selection);
+ selection,
+ select_location);
}
g_list_free (selection);
}
@@ -3720,6 +3783,7 @@ nautilus_icon_container_update_icon (NautilusIconContainer *container,
GdkPixbuf *pixbuf, *emblem_pixbuf, *saved_pixbuf;
GList *emblem_scalable_icons, *emblem_pixbufs, *p;
char *editable_text, *additional_text;
+ GtkWidget *embedded_control;
GdkFont *font;
int smooth_font_size;
@@ -3754,8 +3818,7 @@ nautilus_icon_container_update_icon (NautilusIconContainer *container,
eel_scalable_icon_unref (scalable_icon);
- /* in the rare case an image is too small, scale it up */
-
+ /* in the rare case an image is too small, scale it up */
width = gdk_pixbuf_get_width (pixbuf);
height = gdk_pixbuf_get_height (pixbuf);
if (width < min_image_size || height < min_image_size) {
@@ -3763,9 +3826,11 @@ nautilus_icon_container_update_icon (NautilusIconContainer *container,
/* don't let it exceed the maximum width in the other dimension */
scale_factor = MIN (scale_factor, max_image_size / width);
scale_factor = MIN (scale_factor, max_image_size / height);
-
+
scaled_width = floor (width * scale_factor + .5);
scaled_height = floor (height * scale_factor + .5);
+
+ /* scale the image to the calculated size */
saved_pixbuf = pixbuf;
pixbuf = gdk_pixbuf_scale_simple (pixbuf, scaled_width, scaled_height, GDK_INTERP_BILINEAR);
gdk_pixbuf_unref (saved_pixbuf);
@@ -3794,13 +3859,22 @@ nautilus_icon_container_update_icon (NautilusIconContainer *container,
emblem_pixbufs = g_list_reverse (emblem_pixbufs);
eel_scalable_icon_list_free (emblem_scalable_icons);
+ /* get the embedded control, if any */
+ embedded_control = nautilus_icon_canvas_item_get_control (icon->item);
+ if (embedded_control == NULL) {
+ gtk_signal_emit (GTK_OBJECT (container),
+ signals[GET_ICON_CONTROL],
+ icon->data,
+ &embedded_control);
+ }
+
/* Get both editable and non-editable icon text */
gtk_signal_emit (GTK_OBJECT (container),
signals[GET_ICON_TEXT],
icon->data,
&editable_text,
&additional_text);
-
+
/* If name of icon being renamed was changed from elsewhere, end renaming mode.
* Alternatively, we could replace the characters in the editable text widget
* with the new name, but that could cause timing problems if the user just
@@ -3825,6 +3899,8 @@ nautilus_icon_container_update_icon (NautilusIconContainer *container,
"smooth_font", details->smooth_label_font,
NULL);
+ nautilus_icon_canvas_item_set_control (icon->item, embedded_control);
+
nautilus_icon_canvas_item_set_image (icon->item, pixbuf);
nautilus_icon_canvas_item_set_attach_points (icon->item, &attach_points);
nautilus_icon_canvas_item_set_emblems (icon->item, emblem_pixbufs);
@@ -5073,6 +5149,33 @@ update_label_color (EelBackground *background,
}
}
+/* handle the annotation preference changes by updating the icons */
+static void
+nautilus_icon_container_annotation_changed (gpointer user_data)
+{
+ nautilus_icon_container_request_update_all (NAUTILUS_ICON_CONTAINER (user_data));
+}
+
+
+/* handle fetching annotation info from the controller */
+char *
+nautilus_icon_container_get_note_text (NautilusIconContainer *container,
+ gpointer user_data,
+ int emblem_index)
+{
+ NautilusIcon *icon;
+ char *note_text;
+ note_text = NULL;
+
+ icon = (NautilusIcon*) user_data;
+ gtk_signal_emit (GTK_OBJECT (container),
+ signals[GET_ICON_ANNOTATION],
+ icon->data,
+ emblem_index,
+ &note_text);
+
+ return note_text;
+}
/* Return if the icon container is a fixed size */
gboolean
diff --git a/libnautilus-extensions/nautilus-icon-container.h b/libnautilus-extensions/nautilus-icon-container.h
index 30382a19e..4510110ec 100644
--- a/libnautilus-extensions/nautilus-icon-container.h
+++ b/libnautilus-extensions/nautilus-icon-container.h
@@ -107,6 +107,10 @@ typedef struct {
gboolean (* get_stored_icon_position) (NautilusIconContainer *container,
NautilusIconData *data,
NautilusIconPosition *position);
+ void
+ (* get_icon_control) (NautilusIconContainer *container,
+ NautilusIconData *data,
+ GtkWidget **control);
NautilusScalableIcon *
(* get_icon_images) (NautilusIconContainer *container,
NautilusIconData *data,
@@ -120,6 +124,9 @@ typedef struct {
NautilusIconData *data);
char * (* get_icon_drop_target_uri) (NautilusIconContainer *container,
NautilusIconData *data);
+ char * (* get_icon_annotation) (NautilusIconContainer *container,
+ NautilusIconData *data,
+ int annotation_index);
int (* compare_icons) (NautilusIconContainer *container,
NautilusIconData *icon_a,
NautilusIconData *icon_b);
diff --git a/libnautilus-extensions/nautilus-icon-dnd.c b/libnautilus-extensions/nautilus-icon-dnd.c
index 4d5364ea3..059051002 100644
--- a/libnautilus-extensions/nautilus-icon-dnd.c
+++ b/libnautilus-extensions/nautilus-icon-dnd.c
@@ -1279,11 +1279,12 @@ nautilus_icon_dnd_begin_drag (NautilusIconContainer *container,
/* create a pixmap and mask to drag with */
- pixbuf = nautilus_icon_canvas_item_get_image (container->details->drag_icon->item);
-
+
/* we want to drag semi-transparent pixbufs, but X is too slow dealing with
stippled masks, so we had to remove the code; this comment is left as a memorial
to it, with the hope that we get it back someday as X Windows improves */
+
+ pixbuf = nautilus_icon_canvas_item_get_image (container->details->drag_icon->item);
/* compute the image's offset */
nautilus_icon_canvas_item_get_icon_rectangle
diff --git a/libnautilus-extensions/nautilus-icon-private.h b/libnautilus-extensions/nautilus-icon-private.h
index b1b73c147..d31a13265 100644
--- a/libnautilus-extensions/nautilus-icon-private.h
+++ b/libnautilus-extensions/nautilus-icon-private.h
@@ -254,4 +254,8 @@ void nautilus_icon_container_update_scroll_region (NautilusIconC
guint32 nautilus_icon_container_get_label_color (NautilusIconContainer *container,
gboolean first_line);
+char * nautilus_icon_container_get_note_text (NautilusIconContainer *container,
+ gpointer user_data,
+ int emblem_index);
+
#endif /* NAUTILUS_ICON_CONTAINER_PRIVATE_H */
diff --git a/libnautilus-extensions/nautilus-link.c b/libnautilus-extensions/nautilus-link.c
index 8fd7fcaba..76860d397 100644
--- a/libnautilus-extensions/nautilus-link.c
+++ b/libnautilus-extensions/nautilus-link.c
@@ -312,6 +312,35 @@ nautilus_link_local_get_additional_text (const char *path)
(path, NAUTILUS_METADATA_KEY_EXTRA_TEXT);
}
+void nautilus_link_local_get_component_info (const char *path,
+ char **control_moniker, char **control_data)
+{
+ xmlDoc *document;
+ const char *mime_type;
+
+ *control_moniker = NULL;
+ *control_data = NULL;
+
+ /* Check mime type. Exit if it is not a nautilus link */
+ mime_type = gnome_vfs_get_file_mime_type (path, NULL, FALSE);
+ if (strcmp (mime_type, "application/x-nautilus-link") != 0) {
+ return;
+ }
+
+ document = xmlParseFile (path);
+ if (document != NULL) {
+ *control_moniker = xml_get_root_property (document,
+ NAUTILUS_METADATA_KEY_CONTROL_MONIKER);
+
+ *control_data = xml_get_root_property (document,
+ NAUTILUS_METADATA_KEY_CONTROL_DATA);
+
+ xmlFreeDoc (document);
+ }
+}
+
+
+
/* utility to return the local pathname of a cached icon, given the leaf name */
/* if the icons directory hasn't been created yet, create it */
static char *
diff --git a/libnautilus-extensions/nautilus-link.h b/libnautilus-extensions/nautilus-link.h
index 88882ccb5..b9596eb7d 100644
--- a/libnautilus-extensions/nautilus-link.h
+++ b/libnautilus-extensions/nautilus-link.h
@@ -76,14 +76,21 @@ gboolean nautilus_link_local_set_link_uri (const char
* none. Despite the fact that it takes a URI parameter, works only if
* the file is local and does sync. I/O.
*/
-char * nautilus_link_local_get_additional_text (const char *path);
+char * nautilus_link_local_get_additional_text (const char *path);
/* Returns the image associated with a link file. Despite the fact
* that it takes a URI parameter, works only if the file is local and
* does sync. I/O on the link, although it does async. on the image
* and caches if the image is remote.
*/
-char * nautilus_link_local_get_image_uri (const char *path);
+char * nautilus_link_local_get_image_uri (const char *path);
+
+/* returns the moniker of the component associated with a link file, as well as configuration data.
+ * It works only if the file is local and does sync. I/O.
+ */
+void nautilus_link_local_get_component_info (const char *path,
+ char **control_moniker,
+ char **control_data);
/* Returns the link type of a link file.
* Works only if the file is local and does sync. I/O
diff --git a/libnautilus-extensions/nautilus-metadata.h b/libnautilus-extensions/nautilus-metadata.h
index 00f96a2da..70681853f 100644
--- a/libnautilus-extensions/nautilus-metadata.h
+++ b/libnautilus-extensions/nautilus-metadata.h
@@ -72,6 +72,12 @@
#define NAUTILUS_METADATA_KEY_ICON_SCALE "icon_scale"
#define NAUTILUS_METADATA_KEY_CUSTOM_ICON "custom_icon"
+#define NAUTILUS_METADATA_KEY_FILE_DIGEST "digest"
+#define NAUTILUS_METADATA_KEY_NOTES_INFO "notes_info"
+
+#define NAUTILUS_METADATA_KEY_CONTROL_MONIKER "control_moniker"
+#define NAUTILUS_METADATA_KEY_CONTROL_DATA "control_data"
+
/* per link file */
#define NAUTILUS_METADATA_KEY_EXTRA_TEXT "extra_text"
diff --git a/libnautilus-private/Makefile.am b/libnautilus-private/Makefile.am
index c0094f54d..515fdce88 100644
--- a/libnautilus-private/Makefile.am
+++ b/libnautilus-private/Makefile.am
@@ -38,6 +38,7 @@ endif
libnautilus_extensions_la_LDFLAGS = \
$(dependency_static_libs) \
+ $(AMMONITE_LIBS) \
$(BONOBO_PRINT_LIBS) \
$(BONOBOX_LIBS) \
$(ESD_LIBS) \
@@ -63,9 +64,11 @@ nautilus_metafile_server_idl_sources = \
libnautilus_extensions_la_SOURCES = \
$(nautilus_metafile_server_idl_sources) \
+ nautilus-annotation.c \
nautilus-audio-player.c \
nautilus-bonobo-extensions.c \
nautilus-bookmark.c \
+ nautilus-canvas-note-item.c \
nautilus-customization-data.c \
nautilus-dateedit-extensions.c \
nautilus-default-file-icon.c \
@@ -130,9 +133,11 @@ libnautilus_extensions_la_SOURCES = \
# Everything is private for now
noinst_HEADERS = \
+ nautilus-annotation.h \
nautilus-audio-player.h \
nautilus-bonobo-extensions.h \
nautilus-bookmark.h \
+ nautilus-canvas-note-item.h \
nautilus-cdrom-extensions.h \
nautilus-customization-data.h \
nautilus-dateedit-extensions.h \
diff --git a/libnautilus-private/nautilus-annotation.c b/libnautilus-private/nautilus-annotation.c
new file mode 100644
index 000000000..07a2d8512
--- /dev/null
+++ b/libnautilus-private/nautilus-annotation.c
@@ -0,0 +1,1312 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+ nautilus-annotation.c: routines for getting and setting xml-based annotations associated
+ with the digest of a file.
+
+ Copyright (C) 2001 Eazel, Inc.
+
+ This program 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; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program 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. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Andy Hertzfeld <andy@eazel.com>
+*/
+
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to md5_init, call md5_update as
+ * needed on buffers full of bytes, and then call md5_Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+/* parts of this file are :
+ * Written March 1993 by Branko Lankester
+ * Modified June 1993 by Colin Plumb for altered md5.c.
+ * Modified October 1995 by Erik Troan for RPM
+ */
+
+#include <config.h>
+#include "nautilus-annotation.h"
+
+#include "nautilus-file-utilities.h"
+#include "nautilus-file.h"
+#include "nautilus-file-private.h"
+#include "nautilus-global-preferences.h"
+#include "nautilus-metadata.h"
+#include "nautilus-preferences.h"
+#include <eel/eel-string.h>
+#include <eel/eel-xml-extensions.h>
+#include <eel/eel-vfs-extensions.h>
+#include <ghttp.h>
+#include <gnome-xml/parser.h>
+#include <gnome-xml/xmlmemory.h>
+#include <libgnome/gnome-util.h>
+#include <libgnomevfs/gnome-vfs.h>
+#include <libtrilobite/libammonite.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* icon selection callback function. */
+typedef void (* NautilusCalculateDigestCallback) (NautilusFile *file, char *file_digest);
+typedef struct NautilusDigestFileHandle NautilusDigestFileHandle;
+
+typedef struct {
+ guint32 buf[4];
+ guint32 bits[2];
+ guchar in[64];
+ int doByteReverse;
+} MD5Context ;
+
+struct NautilusDigestFileHandle {
+ GnomeVFSAsyncHandle *handle;
+ NautilusCalculateDigestCallback callback;
+ NautilusFile *file;
+ char *buffer;
+ gboolean opened;
+ MD5Context digest_context;
+};
+
+#define READ_CHUNK_SIZE 65536
+#define MAX_DIGESTS_IN_PROGRESS 16
+#define SERVER_URI_TEMPLATE "http://dellbert.differnet.com/get_notes.cgi?ids=%s"
+#define SERVER_POST_URI "http://dellbert.differnet.com/set_notes.cgi"
+#define NOTES_LOOKUP_INTERVAL 3600
+
+static int open_count = 0;
+static int close_count = 0;
+static int digests_in_progress = 0;
+
+static GList* digest_request_queue = NULL;
+static GList* annotation_request_queue = NULL;
+
+static GHashTable *files_awaiting_annotation = NULL;
+
+static void md5_transform (guint32 buf[4], const guint32 in[16]);
+
+static int _ie = 0x44332211;
+static union _endian { int i; char b[4]; } *_endian = (union _endian *)&_ie;
+#define IS_BIG_ENDIAN() (_endian->b[0] == '\x44')
+#define IS_LITTLE_ENDIAN() (_endian->b[0] == '\x11')
+
+static void got_file_digest (NautilusFile *file, const char *file_digest);
+static void process_digest_requests (void);
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void
+_byte_reverse (guchar *buf, guint32 longs)
+{
+ guint32 t;
+ do {
+ t = (guint32) ((guint32) buf[3] << 8 | buf[2]) << 16 |
+ ((guint32) buf[1] << 8 | buf[0]);
+ *(guint32 *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+
+/**
+ * md5_init: Initialise an md5 context object
+ * @ctx: md5 context
+ *
+ * Initialise an md5 buffer.
+ *
+ **/
+static void
+md5_init (MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+
+ if (IS_BIG_ENDIAN())
+ ctx->doByteReverse = 1;
+ else
+ ctx->doByteReverse = 0;
+}
+
+
+
+/**
+ * md5_update: add a buffer to md5 hash computation
+ * @ctx: conetxt object used for md5 computaion
+ * @buf: buffer to add
+ * @len: buffer length
+ *
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes. Use this to progressively construct an md5 hash.
+ **/
+static void
+md5_update (MD5Context *ctx, const guchar *buf, guint32 len)
+{
+ guint32 t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((guint32) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ guchar *p = (guchar *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy (p, buf, len);
+ return;
+ }
+ memcpy (p, buf, t);
+ if (ctx->doByteReverse)
+ _byte_reverse (ctx->in, 16);
+ md5_transform (ctx->buf, (guint32 *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy (ctx->in, buf, 64);
+ if (ctx->doByteReverse)
+ _byte_reverse (ctx->in, 16);
+ md5_transform (ctx->buf, (guint32 *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy (ctx->in, buf, len);
+}
+
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+/**
+ * md5_final: copy the final md5 hash to a bufer
+ * @digest: 16 bytes buffer
+ * @ctx: context containing the calculated md5
+ *
+ * copy the final md5 hash to a bufer
+ **/
+static void
+md5_final (MD5Context *ctx, guchar digest[16])
+{
+ guint32 count;
+ guchar *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset (p, 0, count);
+ if (ctx->doByteReverse)
+ _byte_reverse (ctx->in, 16);
+ md5_transform (ctx->buf, (guint32 *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset (ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset (p, 0, count - 8);
+ }
+ if (ctx->doByteReverse)
+ _byte_reverse (ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((guint32 *) ctx->in)[14] = ctx->bits[0];
+ ((guint32 *) ctx->in)[15] = ctx->bits[1];
+
+ md5_transform (ctx->buf, (guint32 *) ctx->in);
+ if (ctx->doByteReverse)
+ _byte_reverse ((guchar *) ctx->buf, 4);
+ memcpy (digest, ctx->buf, 16);
+}
+
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. md5_Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void
+md5_transform (guint32 buf[4], const guint32 in[16])
+{
+ register guint32 a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP (F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP (F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP (F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP (F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP (F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP (F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP (F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP (F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP (F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP (F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP (F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP (F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP (F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP (F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP (F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP (F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP (F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP (F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP (F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP (F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP (F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP (F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP (F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP (F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP (F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP (F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP (F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP (F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP (F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP (F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP (F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP (F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP (F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP (F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP (F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP (F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP (F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP (F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP (F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP (F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP (F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP (F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP (F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP (F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP (F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP (F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP (F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP (F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP (F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP (F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP (F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP (F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP (F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP (F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP (F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP (F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP (F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP (F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP (F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP (F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP (F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP (F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP (F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP (F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+
+
+/* When close is complete, there's no more work to do. */
+static void
+digest_file_close_callback (GnomeVFSAsyncHandle *handle,
+ GnomeVFSResult result,
+ gpointer callback_data)
+{
+ close_count += 1;
+ g_message ("opened %d, closed %d", open_count, close_count);
+}
+
+/* Close the file and then tell the caller we succeeded, handing off
+ * the buffer to the caller.
+ */
+static void
+digest_file_completed (NautilusDigestFileHandle *digest_handle)
+{
+ guchar digest_result[16];
+ char digest_string [33];
+ char* hex_string = "0123456789abcdef";
+ int index, result_index;
+ int current_value;
+
+ if (digest_handle->opened) {
+
+ gnome_vfs_async_close (digest_handle->handle,
+ digest_file_close_callback,
+ NULL);
+ }
+
+ digests_in_progress -= 1;
+
+ /* Invoke the callback to continue processing the annotation */
+ md5_final (&digest_handle->digest_context, digest_result);
+
+ /* make a hex string for the digest result */
+ digest_string[32] = '\0';
+ for (index = 0; index < 32; index++) {
+ current_value = digest_result[index >> 1];
+ if (index & 1) {
+ result_index = current_value & 15;
+ } else {
+ result_index = (current_value >> 4) & 15;
+ }
+
+ digest_string[index] = hex_string[result_index];
+ }
+
+ (* digest_handle->callback) (digest_handle->file, &digest_string[0]);
+
+ nautilus_file_unref (digest_handle->file);
+ g_free (digest_handle->buffer);
+ g_free (digest_handle);
+
+ /* start new digest requests if necessary */
+ process_digest_requests ();
+
+}
+
+/* Tell the caller we failed. */
+static void
+digest_file_failed (NautilusDigestFileHandle *digest_handle, GnomeVFSResult result)
+{
+ if (digest_handle->opened) {
+ gnome_vfs_async_close (digest_handle->handle,
+ digest_file_close_callback,
+ NULL);
+ }
+ g_free (digest_handle->buffer);
+
+ digests_in_progress -= 1;
+
+ (* digest_handle->callback) (digest_handle->file, NULL);
+
+ nautilus_file_unref (digest_handle->file);
+ g_free (digest_handle);
+
+ /* start new digest requests if necessary */
+ process_digest_requests ();
+}
+
+/* Here is the callback from the file read routine, where we actually accumulate the checksum */
+static void
+calculate_checksum_callback (GnomeVFSAsyncHandle *handle,
+ GnomeVFSResult result,
+ gpointer buffer,
+ GnomeVFSFileSize bytes_requested,
+ GnomeVFSFileSize bytes_read,
+ gpointer callback_data)
+{
+ NautilusDigestFileHandle *digest_handle;
+
+ /* Do a few reality checks. */
+ g_assert (bytes_requested == READ_CHUNK_SIZE);
+
+ digest_handle = callback_data;
+ g_assert (digest_handle->handle == handle);
+ g_assert (bytes_read <= bytes_requested);
+
+ /* Check for a failure. */
+ if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_EOF) {
+ digest_file_failed (digest_handle, result);
+ return;
+ }
+
+ /* accumulate the recently read data into the checksum */
+ if (bytes_read > 0) {
+ md5_update (&digest_handle->digest_context, buffer, bytes_read);
+ }
+
+ /* Read more unless we are at the end of the file. */
+ if (bytes_read > 0 && result == GNOME_VFS_OK) {
+ gnome_vfs_async_read (digest_handle->handle,
+ digest_handle->buffer,
+ READ_CHUNK_SIZE,
+ calculate_checksum_callback,
+ digest_handle);
+ } else {
+ digest_file_completed (digest_handle);
+ }
+}
+
+/* Once the open is finished, read a first chunk. */
+static void
+read_file_open_callback (GnomeVFSAsyncHandle *handle,
+ GnomeVFSResult result,
+ gpointer callback_data)
+{
+ NautilusDigestFileHandle *digest_handle;
+ char *name;
+
+ digest_handle = callback_data;
+ g_assert (digest_handle->handle == handle);
+
+ /* Handle the failure case. */
+ if (result != GNOME_VFS_OK) {
+ name = nautilus_file_get_name (digest_handle->file);
+ g_message ("open failed, filename %s, error was %d", name, result);
+ g_free (name);
+ digest_file_failed (digest_handle, result);
+ return;
+ }
+
+ /* read in the first chunk of the file */
+ digest_handle->opened = TRUE;
+ open_count += 1;
+ gnome_vfs_async_read (digest_handle->handle,
+ digest_handle->buffer,
+ READ_CHUNK_SIZE,
+ calculate_checksum_callback,
+ digest_handle);
+}
+
+/* calculate the digest for the passed-in file asynchronously, invoking the passed in
+ * callback when the calculation has been completed.
+ */
+static NautilusDigestFileHandle*
+calculate_file_digest (NautilusFile *file, NautilusCalculateDigestCallback callback)
+{
+ NautilusDigestFileHandle *handle;
+ char *uri;
+
+
+ /* allocate a digest-handle structure to keep our state */
+
+ handle = g_new0 (NautilusDigestFileHandle, 1);
+ uri = nautilus_file_get_uri (file);
+
+ handle->callback = callback;
+ handle->opened = FALSE;
+ handle->file = file;
+ nautilus_file_ref (file);
+
+ /* allocate the buffer */
+ handle->buffer = g_malloc (READ_CHUNK_SIZE);
+
+ /* initialize the MD5 stuff */
+ md5_init (&handle->digest_context);
+
+ /* open the file */
+ gnome_vfs_async_open (&handle->handle,
+ uri,
+ GNOME_VFS_OPEN_READ,
+ read_file_open_callback,
+ handle);
+ g_free (uri);
+ return handle;
+}
+
+/* process the digest request queue, launching as many requests as we can handle */
+static void
+process_digest_requests (void)
+{
+ GList *current_entry;
+ NautilusFile *file;
+
+ while (digests_in_progress < MAX_DIGESTS_IN_PROGRESS && digest_request_queue != NULL)
+ {
+ /* pull entry off queue */
+ current_entry = digest_request_queue;
+ digest_request_queue = current_entry->next;
+
+ file = NAUTILUS_FILE (current_entry->data);
+
+ /* initiate request */
+ calculate_file_digest (file, (NautilusCalculateDigestCallback) got_file_digest);
+
+ /* dispose of queue entry */
+ nautilus_file_unref (file);
+
+ g_list_free_1 (current_entry);
+ digests_in_progress += 1;
+ }
+}
+
+/* queue the digest request, and start processing it if we haven't exceeded the limit of requests
+ * in progress
+ */
+static void
+queue_file_digest_request (NautilusFile *file)
+{
+ /* if annotation lookup is disabled, don't bother to do all this work */
+ if (!nautilus_preferences_get_boolean (NAUTILUS_PREFERENCES_LOOKUP_ANNOTATIONS)) {
+ return;
+ }
+ nautilus_file_ref (file);
+ digest_request_queue = g_list_append (digest_request_queue, file);
+ process_digest_requests ();
+}
+
+/* given a digest, retrieve an associated file object from the hash table */
+static NautilusFile *
+get_file_from_digest (const char *digest)
+{
+ if (files_awaiting_annotation == NULL) {
+ return NULL;
+ }
+
+ return g_hash_table_lookup (files_awaiting_annotation, digest);
+}
+
+/* given a digest value, return the path to it in the local cache */
+static char *
+get_annotation_path (const char *digest)
+{
+ char *user_directory, *annotation_directory;
+ char *annotation_path, *directory_uri;
+
+ user_directory = nautilus_get_user_directory ();
+ annotation_directory = nautilus_make_path (user_directory, "annotations");
+ annotation_path = nautilus_make_path (annotation_directory, digest);
+
+ /* create the annotation directory if it doesn't exist */
+ if (!g_file_exists (annotation_directory)) {
+ directory_uri = gnome_vfs_get_uri_from_local_path (annotation_directory);
+ gnome_vfs_make_directory (directory_uri,
+ GNOME_VFS_PERM_USER_ALL
+ | GNOME_VFS_PERM_GROUP_ALL
+ | GNOME_VFS_PERM_OTHER_READ);
+ g_free (directory_uri);
+ }
+
+ /* free up the intermediate strings and return the complete path */
+ g_free (user_directory);
+ g_free (annotation_directory);
+
+ return annotation_path;
+}
+
+/* look up the passed-in digest in the local annotation cache */
+static char *
+look_up_local_annotation (NautilusFile *file, const char *digest)
+{
+ GnomeVFSResult result;
+ int file_size;
+ char *uri, *path, *file_data, *buffer;
+
+ path = get_annotation_path (digest);
+ if (g_file_exists (path)) {
+ /* load the file and return it */
+ uri = gnome_vfs_get_uri_from_local_path (path);
+ result = eel_read_entire_file (uri, &file_size, &file_data);
+ g_free (uri);
+ g_free (path);
+ if (result == GNOME_VFS_OK) {
+ /* add a null at the end, so it's a valid string */
+ buffer = g_realloc (file_data, file_size + 1);
+ buffer[file_size] = '\0';
+ return buffer;
+ } else {
+ return NULL;
+ }
+ }
+ g_free (path);
+ return NULL;
+}
+
+static gboolean
+has_local_annotation (const char *digest)
+{
+ gboolean has_annotation;
+ char *path;
+
+ path = get_annotation_path (digest);
+ has_annotation = g_file_exists (path);
+
+ g_free (path);
+ return has_annotation;
+}
+
+/* utility routine to save the passed-in xml document as a local annotations file */
+static void
+save_local_annotations (xmlDocPtr document, const char *digest)
+{
+ char *path;
+
+ path = get_annotation_path (digest);
+ xmlSaveFile (path, document);
+
+ g_free (path);
+}
+
+/* utility routine to add the passed-in xml node to the file associated with the passed-in
+ * digest. If there isn't a file, create one
+ */
+static void
+add_annotations_to_file (xmlNodePtr node_ptr, const char *digest)
+{
+ char *digest_path;
+ xmlDocPtr document;
+
+ digest_path = get_annotation_path (digest);
+
+ /* save the subtree as a new document, by making a new document and adding the new node */
+ document = xmlNewDoc ("1.0");
+ xmlDocSetRootElement (document, node_ptr);
+
+ /* save the xml tree as a file in the cache area */
+ xmlSaveFile (digest_path, document);
+
+ xmlFreeDoc (document);
+ g_free (digest_path);
+}
+
+/* remember the file object by adding it to a hash table */
+static void
+remember_file (NautilusFile *file, const char *digest)
+{
+ nautilus_file_ref (file);
+
+ if (files_awaiting_annotation == NULL) {
+ files_awaiting_annotation = g_hash_table_new (g_str_hash, g_str_equal);
+ /* g_atexit (annotations_file_table_free); */
+ }
+
+ g_hash_table_insert (files_awaiting_annotation, g_strdup (digest), file);
+}
+
+/* forget a file when we're done with it by removing it from the table */
+static void
+forget_file (const char *digest)
+{
+ NautilusFile *file;
+ if (files_awaiting_annotation == NULL) {
+ return;
+ }
+
+ file = g_hash_table_lookup (files_awaiting_annotation, digest);
+ if (file != NULL) {
+ nautilus_file_unref (file);
+ g_hash_table_remove (files_awaiting_annotation, digest);
+ }
+}
+
+/* completion routine invoked when we've loaded the an annotation file from the service.
+ * We must parse it, and walk through it to save the annotations in the local cache.
+ */
+static void
+got_annotations_callback (GnomeVFSResult result,
+ GnomeVFSFileSize file_size,
+ char *file_contents,
+ gpointer callback_data)
+{
+ NautilusFile *file;
+ xmlDocPtr annotations;
+ xmlNodePtr next_annotation, item;
+ xmlNodePtr saved_annotation;
+ int annotation_count;
+ char *buffer, *digest, *info_str;
+ time_t date_stamp;
+
+ /* exit if there was an error */
+ if (result != GNOME_VFS_OK) {
+ g_assert (file_contents == NULL);
+ return;
+ }
+
+ /* inexplicably, the gnome-xml parser requires a zero-terminated array, so add the null at the end. */
+ buffer = g_realloc (file_contents, file_size + 1);
+ buffer[file_size] = '\0';
+ annotations = xmlParseMemory (buffer, file_size);
+ g_free (buffer);
+
+ /* iterate through the xml document, handling each annotation entry */
+ if (annotations != NULL) {
+ next_annotation = xmlDocGetRootElement (annotations)->childs;
+ while (next_annotation != NULL) {
+ if (eel_strcmp (next_annotation->name, "annotations") == 0) {
+ /* get the digest associated with the annotations */
+ digest = xmlGetProp (next_annotation, "digest");
+ if (digest != NULL) {
+ /* count the number of annotations contained in the node */
+ annotation_count = 0;
+ item = next_annotation->childs;
+ while (item != NULL) {
+ if (eel_strcmp (item->name, "annotation") == 0) {
+ annotation_count += 1;
+ }
+ item = item->next;
+ }
+
+ /* write the annotation out to our cache area, if necessary */
+ if (annotation_count > 0) {
+ saved_annotation = xmlCopyNode (next_annotation, TRUE);
+ add_annotations_to_file (saved_annotation, digest);
+ }
+
+ /* retrieve the file object, and update it's count and time stamp */
+
+ file = get_file_from_digest (digest);
+ time (&date_stamp);
+ info_str = g_strdup_printf ("%lu:%d", date_stamp, annotation_count);
+
+ nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_NOTES_INFO, NULL, info_str);
+ g_free (info_str);
+
+ /* issue the changed signal */
+ nautilus_file_emit_changed (file);
+
+ /* remove the file from the hash table and unref it */
+ forget_file (digest);
+ xmlFree (digest);
+ }
+ }
+ next_annotation = next_annotation->next;
+ }
+
+
+ /* free the xml document */
+ xmlFreeDoc (annotations);
+ }
+}
+
+/* format the request, and send it to the server */
+/* the first cut implementation simply sends the digests as a cgi parameter,
+ * but soon we'll want use SOAP or XML-RPC
+ */
+static void
+fetch_annotations_from_server (void)
+{
+ GString *temp_string;
+ GList *current_entry, *save_entry;
+ char *uri;
+
+ /* check to see if there are enough requests, or a long enough delay since the last one */
+
+ current_entry = annotation_request_queue;
+ save_entry = current_entry;
+ annotation_request_queue = NULL;
+
+ /* simple cgi-based request format passed the digests as part of the uri, so
+ * gather the variable parts
+ */
+ temp_string = g_string_new ("");
+ while (current_entry != NULL) {
+ g_string_append (temp_string, (char*) current_entry->data);
+ if (current_entry->next != NULL) {
+ g_string_append (temp_string, ",");
+ }
+ current_entry = current_entry->next;
+ }
+
+
+ uri = g_strdup_printf (SERVER_URI_TEMPLATE, temp_string->str);
+ g_string_free (temp_string, TRUE);
+ eel_g_list_free_deep (save_entry);
+
+ /* read the result from the server asynchronously */
+ eel_read_entire_file_async (uri, got_annotations_callback, NULL);
+ g_free (uri);
+}
+
+
+/* ask the server for an annotation asynchronously */
+static void
+get_annotation_from_server (NautilusFile *file, const char *file_digest)
+{
+ /* see if there's a request for this one already pending - if so, we can return */
+ if (get_file_from_digest (file_digest) != NULL) {
+ return;
+ }
+
+ /* only do this if lookups are enabled */
+ /* if annotation lookup is disabled, don't bother to do all this work */
+ if (!nautilus_preferences_get_boolean (NAUTILUS_PREFERENCES_LOOKUP_ANNOTATIONS)) {
+ return;
+ }
+
+ /* add the request to the queue, and kick it off it there's enough of them */
+ annotation_request_queue = g_list_prepend (annotation_request_queue, g_strdup (file_digest));
+
+ remember_file (file, file_digest);
+ fetch_annotations_from_server ();
+}
+
+/* callback that's invokes when we've finished calculating the file's digest. Remember
+ * it in the metadata, and look up the associated annotation
+ */
+static void
+got_file_digest (NautilusFile *file, const char *file_digest)
+{
+
+ if (file_digest == NULL) {
+ return;
+ }
+
+ /* save the digest in the file metadata */
+ nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_FILE_DIGEST, NULL, file_digest);
+
+ /* lookup the annotations associated with the file. If there is one, flag the change and we're done */
+ if (has_local_annotation (file_digest)) {
+ nautilus_file_emit_changed (file);
+ return;
+ }
+
+ /* there isn't a local annotation, so ask the server for one */
+ get_annotation_from_server (file, file_digest);
+ return;
+}
+
+/* utility routine that takes a passed-in notes-info string and returns true if the
+ * encoded date is old enough to require a new look-up
+ */
+static gboolean
+annotation_is_stale (const char *notes_info)
+{
+ time_t info_date, date_stamp;
+
+ if (notes_info == NULL) {
+ return TRUE;
+ }
+
+ info_date = strtoul (notes_info, NULL, 10);
+ time (&date_stamp);
+
+ /* eventually, the lookup interval should be a preference, not a constant */
+ return (date_stamp - info_date) > NOTES_LOOKUP_INTERVAL;
+}
+
+/* return the annotation associated with a file. If we haven't inspected this file yet,
+ * return NULL but queue a request for an annotation lookup, which will be processed
+ * asynchronously and issue a "file_changed" signal if any is found.
+ */
+char *nautilus_annotation_get_annotation (NautilusFile *file)
+{
+ char *digest;
+ char *annotations;
+ char *digest_info;
+
+ /* if it's a directory, return NULL, at least until we figure out how to handle directory
+ * annotations
+ */
+ if (nautilus_file_is_directory (file)) {
+ return NULL;
+ }
+
+ /* see if there's a digest available in metadata */
+ digest = nautilus_file_get_metadata (file, NAUTILUS_METADATA_KEY_FILE_DIGEST, NULL);
+
+ /* there isn't a digest, so start a request for one going, and return NULL */
+ if (digest == NULL) {
+ queue_file_digest_request (file);
+ return NULL;
+ }
+
+ /* if we haven't update the info in a while, initiate a lookup */
+ digest_info = nautilus_file_get_metadata (file, NAUTILUS_METADATA_KEY_NOTES_INFO, NULL);
+ if (annotation_is_stale (digest_info)) {
+ get_annotation_from_server (file, digest);
+ }
+ g_free (digest_info);
+
+ /* there's a digest, so we if we have the annotations for the file cached locally */
+ annotations = look_up_local_annotation (file, digest);
+ if (annotations != NULL) {
+ g_free (digest);
+ return annotations;
+ }
+
+ g_free (digest);
+ return NULL;
+}
+
+/* utility routine to map raw annotation text into text to be displayed
+ * for now this is pretty naive and only handles free-form text, just returning
+ * the first suitable annotation it can find.
+ */
+char *
+nautilus_annotation_get_display_text (const char *note_text)
+{
+ char *display_text, *temp_text;
+ xmlChar *xml_text;
+ xmlDocPtr annotations;
+ xmlNodePtr next_annotation;
+
+ /* if its an xml file, parse it to extract the display text */
+ if (eel_istr_has_prefix (note_text, "<?xml")) {
+ display_text = NULL;
+ annotations = xmlParseMemory ((char*) note_text, strlen (note_text));
+ if (annotations != NULL) {
+ next_annotation = xmlDocGetRootElement (annotations)->childs;
+ while (next_annotation != NULL) {
+ if (eel_strcmp (next_annotation->name, "annotation") == 0) {
+ xml_text = xmlNodeGetContent (next_annotation);
+ temp_text = (char*) xml_text;
+ while (*temp_text && *temp_text < ' ') temp_text++;
+ display_text = g_strdup (temp_text);
+ xmlFree (xml_text);
+ break;
+ }
+ next_annotation = next_annotation->next;
+ }
+ xmlFreeDoc (annotations);
+ }
+ } else {
+ display_text = g_strdup (note_text);
+ }
+ return display_text;
+}
+
+/* convenience routine to return the display text of an annotation associated
+ * with a file
+ */
+char *
+nautilus_annotation_get_annotation_for_display (NautilusFile *file)
+{
+ char *raw_text, *display_text;
+
+ raw_text = nautilus_annotation_get_annotation (file);
+ if (raw_text != NULL) {
+ display_text = nautilus_annotation_get_display_text (raw_text);
+ g_free (raw_text);
+ return display_text;
+ }
+ return NULL;
+}
+
+/* return the number of annotations associated with the passed in file. If we don't know,
+ * return 0, but queue a request like above
+ */
+int nautilus_annotation_has_annotation (NautilusFile *file)
+{
+ char *digest_info, *digits, *temp_str;
+ int count = 0;
+
+ if (!nautilus_preferences_get_boolean (NAUTILUS_PREFERENCES_DISPLAY_ANNOTATIONS)) {
+ return 0;
+ }
+
+ digest_info = nautilus_file_get_metadata (file, NAUTILUS_METADATA_KEY_NOTES_INFO, NULL);
+
+ if (digest_info != NULL) {
+ digits = strrchr (digest_info, ':');
+ count = atoi (digits + 1);
+ g_free (digest_info);
+
+ /* if the date is stale, launch a new lookup */
+ if (annotation_is_stale (digest_info)) {
+ temp_str = nautilus_annotation_get_annotation (file);
+ g_free (temp_str);
+ }
+ return count;
+ } else {
+ /* initiate fetching the annotations from the server */
+ temp_str = nautilus_annotation_get_annotation (file);
+ g_free (temp_str);
+ }
+ g_free (digest_info);
+ return 0;
+}
+
+/* utility routine for making an HTTP POST request with ghttp */
+static gboolean
+http_post_simple (const char *uri, const char *name, const char *value) {
+ ghttp_request* request;
+ char *ename=NULL, *evalue=NULL, *body=NULL;
+
+ request = ghttp_request_new ();
+ if (!request) {
+ return FALSE;
+ }
+
+ if (ghttp_set_uri (request, (char *)uri) != 0 ||
+ ghttp_set_type (request, ghttp_type_post) != 0) {
+ ghttp_close (request);
+ return FALSE;
+ }
+ ghttp_set_header (request, http_hdr_Connection, "close");
+ ghttp_set_header (request, http_hdr_User_Agent, "Nautilus Annotation");
+ ghttp_set_header (request, http_hdr_Content_Type, "application/x-www-form-urlencoded");
+
+ evalue = gnome_vfs_escape_string (value);
+ if (name) {
+ ename = gnome_vfs_escape_string (name);
+ body = g_strconcat (ename, "=", evalue, NULL);
+ g_free (ename);
+ g_free (evalue);
+ } else {
+ body = evalue;
+ }
+
+ if (ghttp_set_body (request, body, strlen(body)) != 0 ||
+ ghttp_prepare (request) != 0) {
+ ghttp_close (request);
+ return FALSE;
+ }
+
+ if (ghttp_process (request) != ghttp_done) {
+ ghttp_close (request);
+ return FALSE;
+ }
+
+ ghttp_close (request);
+ return TRUE;
+}
+
+/* utility to count the number of annotations in the passed-in xml document */
+static int
+count_annotations (xmlDocPtr xml_document)
+{
+ xmlNodePtr next_annotation;
+ int annotation_count;
+
+ annotation_count = 0;
+ next_annotation = xmlDocGetRootElement (xml_document)->childs;
+
+ while (next_annotation != NULL) {
+ if (eel_strcmp (next_annotation->name, "annotation") == 0) {
+ annotation_count += 1;
+ }
+ next_annotation = next_annotation->next;
+ }
+ return annotation_count;
+}
+
+/*
+ * get_ammonite_get_default_user_username
+ *
+ * Returns username of the currently logged-in default Eazel Service User
+ * or NULL if there isn't one
+ */
+
+
+/* send the local annotations associated with the passed-in digest to the server */
+static void
+nautilus_annotation_send_to_server (const char *digest,
+ const char *annotation_type,
+ const char *annotation_text,
+ const char *date_str)
+{
+ char *user_id;
+ xmlChar *request_text;
+ xmlDocPtr xml_document;
+ xmlNodePtr root_node, annotation_node;
+ int request_size;
+
+ /* get the user name */
+ user_id = ammonite_get_default_user_username ();
+
+ /* if the user wasn't logged in, prompt for it (coming soon, for now, just use anonymous */
+ if (user_id == NULL) {
+ user_id = g_strdup ("anonymous");
+ }
+
+ /* create an xml document to hold the annotation posting */
+ xml_document = xmlNewDoc ("1.0");
+
+ /* create the header node, with the digest attribute */
+ root_node = xmlNewDocNode (xml_document, NULL, "annotations", NULL);
+ xmlDocSetRootElement (xml_document, root_node);
+ xmlSetProp (root_node, "digest", digest);
+ xmlSetProp (root_node, "user", user_id);
+
+ /* set up the annotation payload */
+ annotation_node = xmlNewChild (root_node, NULL, "annotation", NULL);
+ xmlSetProp (annotation_node, "type", annotation_type);
+ xmlSetProp (annotation_node, "date", date_str);
+
+ xmlNodeSetContent (annotation_node, annotation_text);
+
+ /* post the annotation request to the server using ghttp */
+ xmlDocDumpMemory (xml_document, &request_text, &request_size);
+
+ if (!http_post_simple (SERVER_POST_URI, "note", request_text)) {
+ g_message ("post request failed");
+ }
+
+ /* clean up and we're done */
+ xmlFree (request_text);
+ g_free (user_id);
+ xmlFreeDoc (xml_document);
+
+}
+
+/* add an annotation to a file */
+void
+nautilus_annotation_add_annotation (NautilusFile *file,
+ const char *annotation_type,
+ const char *annotation_text,
+ const char *access)
+{
+ char *digest;
+ char *annotations;
+ char *info_str, *date_str;
+ char *annotation_path;
+ int annotation_count;
+ time_t date_stamp;
+ xmlDocPtr xml_document;
+ xmlNodePtr root_node, node;
+ gboolean has_annotation, has_new_annotation;
+
+ /* we can't handle directories yet, so just return. */
+ if (nautilus_file_is_directory (file)) {
+ return;
+ }
+
+ has_new_annotation = annotation_text != NULL && strlen (annotation_text) > 0;
+
+ /* fetch the local annotation, if one exists */
+ digest = nautilus_file_get_metadata (file, NAUTILUS_METADATA_KEY_FILE_DIGEST, NULL);
+
+ /* there isn't a digest, so start a request for one going, and return */
+ /* this shouldn't happen in practice, since the annotation window will have
+ * already created a digest
+ */
+ if (digest == NULL) {
+ queue_file_digest_request (file);
+ return;
+ }
+
+ /* there's a digest, so we if we have the annotations for the file cached locally */
+ annotations = look_up_local_annotation (file, digest);
+ has_annotation = annotations != NULL && strlen (annotations) > 0;
+
+ /* handle the case of no annotation, by removing it if necessary */
+ if (!has_new_annotation) {
+ if (has_annotation) {
+ /* delete the annotation */
+ annotation_path = get_annotation_path (digest);
+ unlink (annotation_path);
+
+ /* set the info to indicate no notes available */
+ time (&date_stamp);
+ info_str = g_strdup_printf ("%lu:%d", date_stamp, 0);
+ nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_NOTES_INFO, NULL, info_str);
+ g_free (info_str);
+
+ nautilus_file_emit_changed (file);
+
+ g_free (annotation_path);
+ }
+
+ g_free (digest);
+ g_free (annotations);
+ return;
+ }
+
+ /* there is a new annotation, but no current annotation exists, so create the
+ * initial xml document from scratch
+ */
+ if (!has_annotation) {
+ xml_document = xmlNewDoc ("1.0");
+ /* create the header node, with the digest attribute */
+ root_node = xmlNewDocNode (xml_document, NULL, "annotations", NULL);
+ xmlDocSetRootElement (xml_document, root_node);
+ xmlSetProp (root_node, "digest", digest);
+ } else {
+ /* open the existing annotation and load it */
+ xml_document = xmlParseMemory (annotations, strlen (annotations));
+ root_node = xmlDocGetRootElement (xml_document);
+ }
+
+ time (&date_stamp);
+ date_str = g_strdup_printf ("%lu", date_stamp);
+
+ /* add the new entry. For now, we only support one entry per file, so we replace the old
+ * one, if it exists, but this will change soon as we support multiple notes per file
+ */
+ if (root_node->childs == NULL) {
+ node = xmlNewChild (root_node, NULL, "annotation", NULL);
+ xmlSetProp (node, "type", annotation_type);
+
+ date_str = g_strdup_printf ("%lu", date_stamp);
+ xmlSetProp (node, "date", date_str);
+ } else {
+ node = root_node->childs;
+ }
+
+ xmlNodeSetContent (node, annotation_text);
+
+ /* save the modified xml document back to the local repository */
+ save_local_annotations (xml_document, digest);
+
+ /* update the metadata date and count */
+ annotation_count = count_annotations (xml_document);
+ info_str = g_strdup_printf ("%lu:%d", date_stamp, annotation_count);
+
+ nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_NOTES_INFO, NULL, info_str);
+ g_free (info_str);
+
+ /* issue file changed symbol to update the emblem */
+ nautilus_file_emit_changed (file);
+
+ /* if the access is global, send it to the server */
+ if (eel_strcmp (access, "global") == 0) {
+ nautilus_annotation_send_to_server (digest, annotation_type, annotation_text, date_str);
+ }
+
+ /* clean up and we're done */
+ xmlFreeDoc (xml_document);
+ g_free (date_str);
+ g_free (digest);
+ g_free (annotations);
+}
+
+/* remove an annotation from a file */
+void nautilus_annotation_remove_annotation (NautilusFile *file, int which_annotation)
+{
+}
+
diff --git a/libnautilus-private/nautilus-annotation.h b/libnautilus-private/nautilus-annotation.h
new file mode 100644
index 000000000..712b276f8
--- /dev/null
+++ b/libnautilus-private/nautilus-annotation.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+ nautilus-annotation.h: routines for getting and setting xml-based annotations associated
+ with the digest of a file.
+
+ Copyright (C) 2000 Eazel, Inc.
+
+ This program 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; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program 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. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Authors: Andy Hertzfeld <andy@eazel.com>
+*/
+
+#ifndef NAUTILUS_ANNOTATION_H
+#define NAUTILUS_ANNOTATION_H
+
+#include <glib.h>
+#include <libnautilus-extensions/nautilus-file.h>
+#include <libnautilus-extensions/nautilus-metadata.h>
+
+char * nautilus_annotation_get_annotation (NautilusFile *file);
+char * nautilus_annotation_get_display_text (const char* annotation_text);
+char * nautilus_annotation_get_annotation_for_display (NautilusFile *file);
+
+int nautilus_annotation_has_annotation (NautilusFile *file);
+
+void nautilus_annotation_add_annotation (NautilusFile *file,
+ const char *annotation_type,
+ const char *annotation_text,
+ const char *access);
+void nautilus_annotation_remove_annotation (NautilusFile *file, int which_annotation);
+
+#endif /* NAUTILUS_ANNOTATION_H */
diff --git a/libnautilus-private/nautilus-canvas-note-item.c b/libnautilus-private/nautilus-canvas-note-item.c
new file mode 100644
index 000000000..6f3607090
--- /dev/null
+++ b/libnautilus-private/nautilus-canvas-note-item.c
@@ -0,0 +1,973 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+ nautilus-canvas-note-item.c: annotation canvas item for nautilus implementation
+
+ Copyright (C) 2001 Eazel, Inc.
+
+ This program 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; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program 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. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ based on gnome_canvas_rect_item by Federico Mena Quintero
+
+ Author: Andy Hertzfeld <andy@eazel.com>
+*/
+
+#include <config.h>
+#include <math.h>
+
+#include <libgnomeui/gnome-canvas.h>
+#include <libgnomeui/gnome-canvas-util.h>
+#include <libgnomeui/gnome-icon-text.h>
+
+#include <gnome-xml/parser.h>
+#include <gnome-xml/xmlmemory.h>
+
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_vpath.h>
+#include <libart_lgpl/art_rgb_svp.h>
+
+#include "nautilus-annotation.h"
+#include "nautilus-canvas-note-item.h"
+#include "nautilus-font-factory.h"
+#include <eel/eel-gdk-extensions.h>
+#include <eel/eel-gdk-pixbuf-extensions.h>
+#include <eel/eel-gnome-extensions.h>
+#include <eel/eel-scalable-font.h>
+#include <eel/eel-smooth-text-layout.h>
+#include <eel/eel-string.h>
+
+enum {
+ ARG_0,
+ ARG_X1,
+ ARG_Y1,
+ ARG_X2,
+ ARG_Y2,
+ ARG_FILL_COLOR,
+ ARG_FILL_COLOR_GDK,
+ ARG_FILL_COLOR_RGBA,
+ ARG_NOTE_TEXT,
+ ARG_OUTLINE_COLOR,
+ ARG_OUTLINE_COLOR_GDK,
+ ARG_OUTLINE_COLOR_RGBA,
+ ARG_FILL_STIPPLE,
+ ARG_OUTLINE_STIPPLE,
+ ARG_WIDTH_PIXELS,
+ ARG_WIDTH_UNITS
+};
+
+#define ANNOTATION_MAX_WIDTH 240
+#define DEFAULT_FONT_SIZE 12
+#define LINE_BREAK_CHARACTERS " -_,;.?/&"
+
+#define ARROW_HEIGHT 16
+#define MIN_ARROW_HALF_WIDTH 4
+#define MAX_ARROW_HALF_WIDTH 12
+
+static void nautilus_canvas_note_item_class_init (NautilusCanvasNoteItemClass *class);
+static void nautilus_canvas_note_item_init (NautilusCanvasNoteItem *note_item);
+static void nautilus_canvas_note_item_destroy (GtkObject *object);
+static void nautilus_canvas_note_item_set_arg (GtkObject *object,
+ GtkArg *arg,
+ guint arg_id);
+static void nautilus_canvas_note_item_get_arg (GtkObject *object,
+ GtkArg *arg,
+ guint arg_id);
+
+static void nautilus_canvas_note_item_realize (GnomeCanvasItem *item);
+static void nautilus_canvas_note_item_unrealize (GnomeCanvasItem *item);
+static void nautilus_canvas_note_item_translate (GnomeCanvasItem *item, double dx, double dy);
+static void nautilus_canvas_note_item_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2);
+
+static void nautilus_canvas_note_item_draw (GnomeCanvasItem *item, GdkDrawable *drawable, int x, int y, int width, int height);
+static void nautilus_canvas_note_item_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf);
+static void nautilus_canvas_note_item_update (GnomeCanvasItem *item, double *affine, ArtSVP *clip_path, int flags);
+static double nautilus_canvas_note_item_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item);
+
+static GnomeCanvasItemClass *note_item_parent_class;
+
+
+GtkType
+nautilus_canvas_note_item_get_type (void)
+{
+ static GtkType note_item_type = 0;
+
+ if (!note_item_type) {
+ GtkTypeInfo note_item_info = {
+ "NautilusCanvasNoteItem",
+ sizeof (NautilusCanvasNoteItem),
+ sizeof (NautilusCanvasNoteItemClass),
+ (GtkClassInitFunc) nautilus_canvas_note_item_class_init,
+ (GtkObjectInitFunc) nautilus_canvas_note_item_init,
+ NULL, /* reserved_1 */
+ NULL, /* reserved_2 */
+ (GtkClassInitFunc) NULL
+ };
+
+ note_item_type = gtk_type_unique (gnome_canvas_item_get_type (), &note_item_info);
+ }
+
+ return note_item_type;
+}
+
+static void
+nautilus_canvas_note_item_class_init (NautilusCanvasNoteItemClass *class)
+{
+ GtkObjectClass *object_class;
+ GnomeCanvasItemClass *item_class;
+
+ object_class = (GtkObjectClass *) class;
+ item_class = (GnomeCanvasItemClass *) class;
+
+ note_item_parent_class = gtk_type_class (gnome_canvas_item_get_type ());
+
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::x1", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_X1);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::y1", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_Y1);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::x2", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_X2);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::y2", GTK_TYPE_DOUBLE, GTK_ARG_READWRITE, ARG_Y2);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::fill_color", GTK_TYPE_STRING, GTK_ARG_WRITABLE, ARG_FILL_COLOR);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::fill_color_gdk", GTK_TYPE_GDK_COLOR, GTK_ARG_READWRITE, ARG_FILL_COLOR_GDK);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::fill_color_rgba", GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_FILL_COLOR_RGBA);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::note_text", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_NOTE_TEXT);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::outline_color", GTK_TYPE_STRING, GTK_ARG_WRITABLE, ARG_OUTLINE_COLOR);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::outline_color_gdk", GTK_TYPE_GDK_COLOR, GTK_ARG_READWRITE, ARG_OUTLINE_COLOR_GDK);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::outline_color_rgba", GTK_TYPE_UINT, GTK_ARG_READWRITE, ARG_OUTLINE_COLOR_RGBA);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::fill_stipple", GTK_TYPE_GDK_WINDOW, GTK_ARG_READWRITE, ARG_FILL_STIPPLE);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::outline_stipple", GTK_TYPE_GDK_WINDOW, GTK_ARG_READWRITE, ARG_OUTLINE_STIPPLE);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::width_pixels", GTK_TYPE_UINT, GTK_ARG_WRITABLE, ARG_WIDTH_PIXELS);
+ gtk_object_add_arg_type ("NautilusCanvasNoteItem::width_units", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_WIDTH_UNITS);
+
+ object_class->destroy = nautilus_canvas_note_item_destroy;
+ object_class->set_arg = nautilus_canvas_note_item_set_arg;
+ object_class->get_arg = nautilus_canvas_note_item_get_arg;
+
+ item_class->realize = nautilus_canvas_note_item_realize;
+ item_class->unrealize = nautilus_canvas_note_item_unrealize;
+ item_class->translate = nautilus_canvas_note_item_translate;
+ item_class->bounds = nautilus_canvas_note_item_bounds;
+
+ item_class->draw = nautilus_canvas_note_item_draw;
+ item_class->point = nautilus_canvas_note_item_point;
+ item_class->update = nautilus_canvas_note_item_update;
+ item_class->render = nautilus_canvas_note_item_render;
+}
+
+static void
+nautilus_canvas_note_item_init (NautilusCanvasNoteItem *note_item)
+{
+ note_item->width = 0.0;
+ note_item->fill_svp = NULL;
+ note_item->outline_svp = NULL;
+}
+
+static void
+nautilus_canvas_note_item_destroy (GtkObject *object)
+{
+ NautilusCanvasNoteItem *note_item;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (NAUTILUS_IS_CANVAS_NOTE_ITEM (object));
+
+ note_item = NAUTILUS_CANVAS_NOTE_ITEM (object);
+
+ if (note_item->fill_stipple)
+ gdk_bitmap_unref (note_item->fill_stipple);
+
+ if (note_item->outline_stipple)
+ gdk_bitmap_unref (note_item->outline_stipple);
+
+ if (note_item->fill_svp)
+ art_svp_free (note_item->fill_svp);
+
+ if (note_item->outline_svp)
+ art_svp_free (note_item->outline_svp);
+
+ if (note_item->note_text)
+ g_free (note_item->note_text);
+
+ if (GTK_OBJECT_CLASS (note_item_parent_class)->destroy)
+ (* GTK_OBJECT_CLASS (note_item_parent_class)->destroy) (object);
+}
+
+static void get_bounds (NautilusCanvasNoteItem *note_item, double *px1, double *py1, double *px2, double *py2)
+{
+ GnomeCanvasItem *item;
+ double x1, y1, x2, y2;
+ int cx1, cy1, cx2, cy2;
+ double hwidth;
+
+ item = GNOME_CANVAS_ITEM (note_item);
+
+ if (note_item->width_pixels)
+ hwidth = (note_item->width / item->canvas->pixels_per_unit) / 2.0;
+ else
+ hwidth = note_item->width / 2.0;
+
+ x1 = note_item->x1;
+ y1 = note_item->y1;
+ x2 = note_item->x2;
+ y2 = note_item->y2;
+
+ gnome_canvas_item_i2w (item, &x1, &y1);
+ gnome_canvas_item_i2w (item, &x2, &y2);
+ gnome_canvas_w2c (item->canvas, x1 - hwidth, y1 - hwidth, &cx1, &cy1);
+ gnome_canvas_w2c (item->canvas, x2 + hwidth, y2 + hwidth, &cx2, &cy2);
+ *px1 = cx1;
+ *py1 = cy1;
+ *px2 = cx2;
+ *py2 = cy2;
+
+ /* Some safety fudging */
+
+ *px1 -= 2;
+ *py1 -= 2;
+ *px2 += 2;
+ *py2 += 2;
+}
+
+/* Convenience function to set a GC's foreground color to the specified pixel value */
+static void
+set_gc_foreground (GdkGC *gc, gulong pixel)
+{
+ GdkColor c;
+
+ if (!gc)
+ return;
+
+ c.pixel = pixel;
+ gdk_gc_set_foreground (gc, &c);
+}
+
+/* Sets the stipple pattern for the specified gc */
+static void
+set_stipple (GdkGC *gc, GdkBitmap **internal_stipple, GdkBitmap *stipple, int reconfigure)
+{
+ if (*internal_stipple && !reconfigure)
+ gdk_bitmap_unref (*internal_stipple);
+
+ *internal_stipple = stipple;
+ if (stipple && !reconfigure)
+ gdk_bitmap_ref (stipple);
+
+ if (gc) {
+ if (stipple) {
+ gdk_gc_set_stipple (gc, stipple);
+ gdk_gc_set_fill (gc, GDK_STIPPLED);
+ } else
+ gdk_gc_set_fill (gc, GDK_SOLID);
+ }
+}
+
+/* Recalculate the outline width of the rectangle/ellipse and set it in its GC */
+static void
+set_outline_gc_width (NautilusCanvasNoteItem *note_item)
+{
+ int width;
+
+ if (!note_item->outline_gc)
+ return;
+
+ if (note_item->width_pixels)
+ width = (int) note_item->width;
+ else
+ width = (int) (note_item->width * note_item->item.canvas->pixels_per_unit + 0.5);
+
+ gdk_gc_set_line_attributes (note_item->outline_gc, width,
+ GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
+}
+
+/* utility to update the canvas item bounding box from the note item's private bounding box */
+static void
+update_item_bounding_box (NautilusCanvasNoteItem *note_item)
+{
+ GnomeCanvasItem *item;
+ item = GNOME_CANVAS_ITEM (note_item);
+
+ item->x1 = note_item->x1;
+ item->y1 = note_item->y1;
+ item->x2 = note_item->x2 + 1;
+ item->y2 = note_item->y2 + 1;
+}
+
+static void
+nautilus_canvas_note_item_set_note_text (NautilusCanvasNoteItem *note_item, const char *new_text)
+{
+ char *display_text;
+ int text_width, height, width;
+ int font_height;
+ GnomeCanvasItem *item;
+ EelScalableFont *scalable_font;
+ GdkFont *font;
+ EelDimensions dimensions;
+ EelSmoothTextLayout *smooth_text_layout;
+
+ item = GNOME_CANVAS_ITEM (note_item);
+
+ if (note_item->note_text) {
+ g_free (note_item->note_text);
+ }
+
+ height = 0; width = 0; /* to avoid compiler complaint */
+ note_item->note_text = g_strdup (new_text);
+
+ /* set the width and height based on the display text */
+ /* this will get more sophisticated as we get fancier */
+ display_text = nautilus_annotation_get_display_text (new_text);
+
+ if (item->canvas->aa) {
+ scalable_font = eel_scalable_font_get_default_font ();
+
+
+ smooth_text_layout = eel_smooth_text_layout_new (
+ display_text, strlen(display_text),
+ scalable_font, DEFAULT_FONT_SIZE, TRUE);
+
+ dimensions = eel_smooth_text_layout_get_dimensions (smooth_text_layout);
+ text_width = dimensions.width + 8;
+ height = dimensions.height + 4;
+ height += ARROW_HEIGHT;
+ gtk_object_unref (GTK_OBJECT (scalable_font));
+ gtk_object_destroy (GTK_OBJECT (smooth_text_layout));
+
+ } else {
+ font = nautilus_font_factory_get_font_from_preferences (DEFAULT_FONT_SIZE);
+ text_width = 8 + gdk_text_measure (font, display_text, strlen (display_text));
+ font_height = gdk_text_height (font, display_text, strlen (display_text));
+ height = font_height * (1 + (text_width / ANNOTATION_MAX_WIDTH));
+ gdk_font_unref (font);
+ }
+
+ width = (text_width < ANNOTATION_MAX_WIDTH) ? text_width : ANNOTATION_MAX_WIDTH;
+
+ /* add some vertical slop for descenders and incorporate scale factor */
+ note_item->x2 = floor (note_item->x1 + (width / item->canvas->pixels_per_unit) + .5);
+ note_item->y2 = floor (note_item->y1 + 4.0 + (height / item->canvas->pixels_per_unit) + .5);
+
+
+ update_item_bounding_box (note_item);
+
+ g_free (display_text);
+}
+
+static void
+nautilus_canvas_note_item_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
+{
+ GnomeCanvasItem *item;
+ NautilusCanvasNoteItem *note_item;
+ GdkColor color = { 0, 0, 0, 0, };
+ GdkColor *pcolor;
+ int have_pixel;
+
+ item = GNOME_CANVAS_ITEM (object);
+ note_item = NAUTILUS_CANVAS_NOTE_ITEM (object);
+ have_pixel = FALSE;
+
+ switch (arg_id) {
+ case ARG_X1:
+ note_item->x1 = GTK_VALUE_DOUBLE (*arg);
+ update_item_bounding_box (note_item);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case ARG_Y1:
+ note_item->y1 = GTK_VALUE_DOUBLE (*arg);
+ update_item_bounding_box (note_item);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case ARG_X2:
+ note_item->x2 = GTK_VALUE_DOUBLE (*arg);
+ update_item_bounding_box (note_item);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case ARG_Y2:
+ note_item->y2 = GTK_VALUE_DOUBLE (*arg);
+ update_item_bounding_box (note_item);
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case ARG_FILL_COLOR:
+ case ARG_FILL_COLOR_GDK:
+ case ARG_FILL_COLOR_RGBA:
+ switch (arg_id) {
+ case ARG_FILL_COLOR:
+ gdk_color_parse (GTK_VALUE_STRING (*arg), &color);
+
+ note_item->fill_color = ((color.red & 0xff00) << 16 |
+ (color.green & 0xff00) << 8 |
+ (color.blue & 0xff00) |
+ 0xff);
+ break;
+
+ case ARG_FILL_COLOR_GDK:
+ pcolor = GTK_VALUE_BOXED (*arg);
+ if (pcolor) {
+ color = *pcolor;
+ gdk_color_context_query_color (item->canvas->cc, &color);
+ have_pixel = TRUE;
+ }
+
+ note_item->fill_color = ((color.red & 0xff00) << 16 |
+ (color.green & 0xff00) << 8 |
+ (color.blue & 0xff00) |
+ 0xff);
+ break;
+
+ case ARG_FILL_COLOR_RGBA:
+ note_item->fill_color = GTK_VALUE_UINT (*arg);
+ break;
+ }
+
+ if (have_pixel)
+ note_item->fill_pixel = color.pixel;
+ else
+ note_item->fill_pixel = gnome_canvas_get_color_pixel (item->canvas, note_item->fill_color);
+
+ if (!item->canvas->aa)
+ set_gc_foreground (note_item->fill_gc, note_item->fill_pixel);
+
+ gnome_canvas_item_request_redraw_svp (item, note_item->fill_svp);
+ break;
+
+ case ARG_OUTLINE_COLOR:
+ case ARG_OUTLINE_COLOR_GDK:
+ case ARG_OUTLINE_COLOR_RGBA:
+ switch (arg_id) {
+ case ARG_OUTLINE_COLOR:
+ gdk_color_parse (GTK_VALUE_STRING (*arg), &color);
+
+ note_item->outline_color = ((color.red & 0xff00) << 16 |
+ (color.green & 0xff00) << 8 |
+ (color.blue & 0xff00) |
+ 0xff);
+ break;
+
+ case ARG_OUTLINE_COLOR_GDK:
+ pcolor = GTK_VALUE_BOXED (*arg);
+ if (pcolor) {
+ color = *pcolor;
+ gdk_color_context_query_color (item->canvas->cc, &color);
+ have_pixel = TRUE;
+ }
+
+ note_item->outline_color = ((color.red & 0xff00) << 16 |
+ (color.green & 0xff00) << 8 |
+ (color.blue & 0xff00) |
+ 0xff);
+ break;
+
+ case ARG_OUTLINE_COLOR_RGBA:
+ note_item->outline_color = GTK_VALUE_UINT (*arg);
+ break;
+ }
+
+ if (have_pixel)
+ note_item->outline_pixel = color.pixel;
+ else
+ note_item->outline_pixel = gnome_canvas_get_color_pixel (item->canvas,
+ note_item->outline_color);
+
+ if (!item->canvas->aa)
+ set_gc_foreground (note_item->outline_gc, note_item->outline_pixel);
+
+ gnome_canvas_item_request_redraw_svp (item, note_item->outline_svp);
+ break;
+
+ case ARG_NOTE_TEXT:
+ nautilus_canvas_note_item_set_note_text (note_item, GTK_VALUE_STRING (*arg));
+ break;
+
+ case ARG_FILL_STIPPLE:
+ if (!item->canvas->aa)
+ set_stipple (note_item->fill_gc, &note_item->fill_stipple, GTK_VALUE_BOXED (*arg), FALSE);
+
+ break;
+
+ case ARG_OUTLINE_STIPPLE:
+ if (!item->canvas->aa)
+ set_stipple (note_item->outline_gc, &note_item->outline_stipple, GTK_VALUE_BOXED (*arg), FALSE);
+ break;
+
+ case ARG_WIDTH_PIXELS:
+ note_item->width = GTK_VALUE_UINT (*arg);
+ note_item->width_pixels = TRUE;
+ if (!item->canvas->aa)
+ set_outline_gc_width (note_item);
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ case ARG_WIDTH_UNITS:
+ note_item->width = fabs (GTK_VALUE_DOUBLE (*arg));
+ note_item->width_pixels = FALSE;
+ if (!item->canvas->aa)
+ set_outline_gc_width (note_item);
+
+ gnome_canvas_item_request_update (item);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* Allocates a GdkColor structure filled with the specified pixel, and puts it into the specified
+ * arg for returning it in the get_arg method.
+ */
+static void
+get_color_arg (NautilusCanvasNoteItem *note_item, gulong pixel, GtkArg *arg)
+{
+ GdkColor *color;
+
+ color = g_new (GdkColor, 1);
+ color->pixel = pixel;
+ gdk_color_context_query_color (GNOME_CANVAS_ITEM (note_item)->canvas->cc, color);
+ GTK_VALUE_BOXED (*arg) = color;
+}
+
+static void
+nautilus_canvas_note_item_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
+{
+ NautilusCanvasNoteItem *note_item;
+ GnomeCanvasItem *item;
+
+ item = GNOME_CANVAS_ITEM (object);
+ note_item = NAUTILUS_CANVAS_NOTE_ITEM (object);
+
+ switch (arg_id) {
+ case ARG_X1:
+ GTK_VALUE_DOUBLE (*arg) = note_item->x1;
+ break;
+
+ case ARG_Y1:
+ GTK_VALUE_DOUBLE (*arg) = note_item->y1;
+ break;
+
+ case ARG_X2:
+ GTK_VALUE_DOUBLE (*arg) = note_item->x2;
+ break;
+
+ case ARG_Y2:
+ GTK_VALUE_DOUBLE (*arg) = note_item->y2;
+ break;
+
+ case ARG_FILL_COLOR_GDK:
+ get_color_arg (note_item, note_item->fill_pixel, arg);
+ break;
+
+ case ARG_OUTLINE_COLOR_GDK:
+ get_color_arg (note_item, note_item->outline_pixel, arg);
+ break;
+
+ case ARG_FILL_COLOR_RGBA:
+ GTK_VALUE_UINT (*arg) = note_item->fill_color;
+ break;
+
+ case ARG_OUTLINE_COLOR_RGBA:
+ GTK_VALUE_UINT (*arg) = note_item->outline_color;
+ break;
+
+ case ARG_FILL_STIPPLE:
+ GTK_VALUE_BOXED (*arg) = note_item->fill_stipple;
+ break;
+
+ case ARG_OUTLINE_STIPPLE:
+ GTK_VALUE_BOXED (*arg) = note_item->outline_stipple;
+ break;
+
+ case ARG_NOTE_TEXT:
+ GTK_VALUE_STRING (*arg) = note_item->note_text;
+ break;
+ default:
+ arg->type = GTK_TYPE_INVALID;
+ break;
+ }
+}
+
+static void
+nautilus_canvas_note_item_realize (GnomeCanvasItem *item)
+{
+ NautilusCanvasNoteItem *note_item;
+
+ note_item = NAUTILUS_CANVAS_NOTE_ITEM (item);
+
+ if (note_item_parent_class->realize)
+ (* note_item_parent_class->realize) (item);
+
+ if (!item->canvas->aa) {
+ note_item->fill_gc = gdk_gc_new (item->canvas->layout.bin_window);
+ note_item->outline_gc = gdk_gc_new (item->canvas->layout.bin_window);
+ }
+}
+
+static void
+nautilus_canvas_note_item_unrealize (GnomeCanvasItem *item)
+{
+ NautilusCanvasNoteItem *note_item;
+
+ note_item = NAUTILUS_CANVAS_NOTE_ITEM (item);
+
+ if (!item->canvas->aa) {
+ gdk_gc_unref (note_item->fill_gc);
+ note_item->fill_gc = NULL;
+ gdk_gc_unref (note_item->outline_gc);
+ note_item->outline_gc = NULL;
+ }
+
+ if (note_item_parent_class->unrealize)
+ (* note_item_parent_class->unrealize) (item);
+}
+
+static void
+nautilus_canvas_note_item_translate (GnomeCanvasItem *item, double dx, double dy)
+{
+ NautilusCanvasNoteItem *note_item;
+
+ note_item = NAUTILUS_CANVAS_NOTE_ITEM (item);
+
+ note_item->x1 += dx;
+ note_item->y1 += dy;
+ note_item->x2 += dx;
+ note_item->y2 += dy;
+
+ update_item_bounding_box (note_item);
+
+ if (item->canvas->aa) {
+ gnome_canvas_item_request_update (item);
+ }
+}
+
+static void
+nautilus_canvas_note_item_bounds (GnomeCanvasItem *item, double *x1, double *y1, double *x2, double *y2)
+{
+ NautilusCanvasNoteItem *note_item;
+ double hwidth;
+
+ note_item = NAUTILUS_CANVAS_NOTE_ITEM (item);
+
+ if (note_item->width_pixels)
+ hwidth = (note_item->width / item->canvas->pixels_per_unit) / 2.0;
+ else
+ hwidth = note_item->width / 2.0;
+
+ *x1 = note_item->x1 - hwidth;
+ *y1 = note_item->y1 - hwidth;
+ *x2 = note_item->x2 + hwidth;
+ *y2 = note_item->y2 + hwidth;
+}
+
+/* utility routine to map raw annotation text into text to be displayed */
+/* for now this is pretty naive and only handles free-form text, just returning
+ * the first suitable annotation it can find
+ */
+
+/* utility routine to draw a text string into the passed-in item */
+static void
+draw_item_aa_text (GnomeCanvasBuf *buf, GnomeCanvasItem *item, const char *note_text)
+{
+ EelScalableFont *font;
+ GdkPixbuf *text_pixbuf;
+ ArtIRect item_bounds, dest_bounds;
+ int width, height;
+ EelSmoothTextLayout *smooth_text_layout;
+
+ font = eel_scalable_font_get_default_font ();
+
+ eel_gnome_canvas_item_get_canvas_bounds (item, &item_bounds);
+ width = item_bounds.x1 - item_bounds.x0;
+ height = item_bounds.y1 - item_bounds.y0;
+
+ text_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+ TRUE,
+ 8,
+ width,
+ height);
+ eel_gdk_pixbuf_fill_rectangle_with_color (text_pixbuf, NULL,
+ EEL_RGBA_COLOR_PACK (0, 0, 0, 0));
+
+ smooth_text_layout = eel_smooth_text_layout_new (
+ note_text, strlen(note_text),
+ font, DEFAULT_FONT_SIZE, TRUE);
+
+ eel_smooth_text_layout_set_line_wrap_width (smooth_text_layout, width - 4);
+
+ dest_bounds.x0 = 0;
+ dest_bounds.y0 = ARROW_HEIGHT;
+ dest_bounds.x1 = width;
+ dest_bounds.y1 = height;
+
+ eel_smooth_text_layout_draw_to_pixbuf
+ (smooth_text_layout, text_pixbuf,
+ 0, 0, &dest_bounds, GTK_JUSTIFY_LEFT,
+ FALSE, EEL_RGBA_COLOR_OPAQUE_BLACK,
+ EEL_OPACITY_FULLY_OPAQUE);
+
+ gtk_object_destroy (GTK_OBJECT (smooth_text_layout));
+
+ eel_gnome_canvas_draw_pixbuf (buf, text_pixbuf, item_bounds.x0 + 4, item_bounds.y0 + 2);
+
+ gdk_pixbuf_unref (text_pixbuf);
+ gtk_object_unref (GTK_OBJECT (font));
+}
+
+static void
+nautilus_canvas_note_item_render (GnomeCanvasItem *item,
+ GnomeCanvasBuf *buf)
+{
+ NautilusCanvasNoteItem *note_item;
+ char *display_text;
+
+ note_item = NAUTILUS_CANVAS_NOTE_ITEM (item);
+
+ if (note_item->fill_svp != NULL) {
+ gnome_canvas_render_svp (buf, note_item->fill_svp, note_item->fill_color);
+ }
+
+ /* draw the annotation text, if necessary */
+ if (note_item->note_text) {
+ display_text = nautilus_annotation_get_display_text (note_item->note_text);
+ if (display_text && strlen (display_text)) {
+ draw_item_aa_text (buf, item, display_text);
+ }
+ g_free (display_text);
+ }
+
+ if (note_item->outline_svp != NULL) {
+ gnome_canvas_render_svp (buf, note_item->outline_svp, note_item->outline_color);
+ }
+}
+
+static void
+nautilus_canvas_note_item_draw (GnomeCanvasItem *item, GdkDrawable *drawable, int x, int y, int width, int height)
+{
+ NautilusCanvasNoteItem *note_item;
+ GdkFont *font;
+ char* display_text;
+ double i2w[6], w2c[6], i2c[6];
+ int x1, y1, x2, y2;
+ ArtPoint i1, i2;
+ ArtPoint c1, c2;
+ GnomeIconTextInfo *text_info;
+
+ note_item = NAUTILUS_CANVAS_NOTE_ITEM (item);
+
+ /* Get canvas pixel coordinates */
+ gnome_canvas_item_i2w_affine (item, i2w);
+ gnome_canvas_w2c_affine (item->canvas, w2c);
+ art_affine_multiply (i2c, i2w, w2c);
+
+ i1.x = note_item->x1;
+ i1.y = note_item->y1;
+ i2.x = note_item->x2;
+ i2.y = note_item->y2;
+ art_affine_point (&c1, &i1, i2c);
+ art_affine_point (&c2, &i2, i2c);
+ x1 = c1.x;
+ y1 = c1.y;
+ x2 = c2.x;
+ y2 = c2.y;
+
+ if (note_item->fill_stipple)
+ gnome_canvas_set_stipple_origin (item->canvas, note_item->fill_gc);
+
+ gdk_draw_rectangle (drawable,
+ note_item->fill_gc,
+ TRUE,
+ x1 - x,
+ y1 - y,
+ x2 - x1 + 1,
+ y2 - y1 + 1);
+
+ /* draw the annotation text */
+ if (note_item->note_text) {
+ font = nautilus_font_factory_get_font_from_preferences (DEFAULT_FONT_SIZE);
+ display_text = nautilus_annotation_get_display_text (note_item->note_text);
+
+ text_info = gnome_icon_layout_text
+ (font, display_text,
+ LINE_BREAK_CHARACTERS,
+ x2 - x1 - 2, TRUE);
+
+ gnome_icon_paint_text (text_info, drawable, note_item->outline_gc,
+ x1 - x + 4, y1 - y + 4, GTK_JUSTIFY_LEFT);
+ gnome_icon_text_info_free (text_info);
+
+ gdk_font_unref (font);
+ g_free (display_text);
+ }
+
+ if (note_item->outline_stipple)
+ gnome_canvas_set_stipple_origin (item->canvas, note_item->outline_gc);
+
+ gdk_draw_rectangle (drawable,
+ note_item->outline_gc,
+ FALSE,
+ x1 - x,
+ y1 - y,
+ x2 - x1,
+ y2 - y1);
+}
+
+static double
+nautilus_canvas_note_item_point (GnomeCanvasItem *item, double x, double y, int cx, int cy, GnomeCanvasItem **actual_item)
+{
+ NautilusCanvasNoteItem *note_item;
+ double x1, y1, x2, y2;
+ double hwidth;
+ double dx, dy;
+
+ note_item = NAUTILUS_CANVAS_NOTE_ITEM (item);
+
+ *actual_item = item;
+
+ /* Find the bounds for the rectangle plus its outline width */
+
+ x1 = note_item->x1;
+ y1 = note_item->y1;
+ x2 = note_item->x2;
+ y2 = note_item->y2;
+
+ if (note_item->width_pixels)
+ hwidth = (note_item->width / item->canvas->pixels_per_unit) / 2.0;
+ else
+ hwidth = note_item->width / 2.0;
+
+ x1 -= hwidth;
+ y1 -= hwidth;
+ x2 += hwidth;
+ y2 += hwidth;
+
+ /* Is point inside rectangle (which can be hollow if it has no fill set)? */
+
+ if ((x >= x1) && (y >= y1) && (x <= x2) && (y <= y2)) {
+ return 0.0;
+ }
+
+ /* Point is outside rectangle */
+
+ if (x < x1)
+ dx = x1 - x;
+ else if (x > x2)
+ dx = x - x2;
+ else
+ dx = 0.0;
+
+ if (y < y1)
+ dy = y1 - y;
+ else if (y > y2)
+ dy = y - y2;
+ else
+ dy = 0.0;
+
+ return sqrt (dx * dx + dy * dy);
+}
+
+static void
+nautilus_canvas_note_item_update (GnomeCanvasItem *item, double affine[6], ArtSVP *clip_path, gint flags)
+{
+ NautilusCanvasNoteItem *note_item;
+ ArtVpath vpath[9];
+ ArtVpath *vpath2;
+ ArtSVP *stroke_svp;
+ double x0, y0, x1, y1;
+ double round_off_amount;
+ double midpoint;
+ double arrow_half_width;
+ note_item = NAUTILUS_CANVAS_NOTE_ITEM (item);
+
+ if (note_item_parent_class->update)
+ (* note_item_parent_class->update) (item, affine, clip_path, flags);
+
+ if (item->canvas->aa) {
+ x0 = note_item->x1;
+ y0 = note_item->y1 + ARROW_HEIGHT;
+ x1 = note_item->x2;
+ y1 = note_item->y2;
+
+ round_off_amount = item->canvas->pixels_per_unit / 2;
+
+ gnome_canvas_item_reset_bounds (item);
+ midpoint = (x1 + x0) / 2;
+ arrow_half_width = (x1 - x0) / 16;
+ arrow_half_width = CLAMP (arrow_half_width, MIN_ARROW_HALF_WIDTH, MAX_ARROW_HALF_WIDTH);
+
+ vpath[0].code = ART_MOVETO;
+ vpath[0].x = x0 - round_off_amount;
+ vpath[0].y = y0 - round_off_amount;
+
+ vpath[1].code = ART_LINETO;
+ vpath[1].x = x0 - round_off_amount;
+ vpath[1].y = y1 + round_off_amount;
+
+ vpath[2].code = ART_LINETO;
+ vpath[2].x = x1 + round_off_amount;
+ vpath[2].y = y1 + round_off_amount;
+
+ vpath[3].code = ART_LINETO;
+ vpath[3].x = x1 + round_off_amount;
+ vpath[3].y = y0 - round_off_amount;
+
+ vpath[4].code = ART_LINETO;
+ vpath[4].x = midpoint + arrow_half_width + round_off_amount;
+ vpath[4].y = y0 - round_off_amount;
+
+ vpath[5].code = ART_LINETO;
+ vpath[5].x = midpoint + round_off_amount;
+ vpath[5].y = note_item->y1 - round_off_amount;
+
+ vpath[6].code = ART_LINETO;
+ vpath[6].x = midpoint - arrow_half_width - round_off_amount;
+ vpath[6].y = y0 - round_off_amount;
+
+ vpath[7].code = ART_LINETO;
+ vpath[7].x = x0 - round_off_amount;
+ vpath[7].y = y0 - round_off_amount;
+
+ vpath[8].code = ART_END;
+ vpath[8].x = 0;
+ vpath[8].y = 0;
+
+ vpath2 = art_vpath_affine_transform (vpath, affine);
+
+ gnome_canvas_item_update_svp_clip (item, &note_item->fill_svp, art_svp_from_vpath (vpath2), clip_path);
+
+ stroke_svp = art_svp_vpath_stroke (vpath2,
+ ART_PATH_STROKE_JOIN_MITER,
+ ART_PATH_STROKE_CAP_BUTT,
+ (note_item->width_pixels) ? note_item->width : (note_item->width * item->canvas->pixels_per_unit),
+ 4,
+ 25);
+
+ gnome_canvas_item_update_svp_clip (item, &note_item->outline_svp, stroke_svp, clip_path);
+ art_free (vpath2);
+
+ eel_gnome_canvas_item_request_redraw
+ (GNOME_CANVAS_ITEM (item));
+
+ } else {
+ /* xlib rendering - just update the bbox */
+
+ set_gc_foreground (note_item->fill_gc, note_item->fill_pixel);
+ set_gc_foreground (note_item->outline_gc, note_item->outline_pixel);
+ set_stipple (note_item->fill_gc, &note_item->fill_stipple, note_item->fill_stipple, TRUE);
+ set_stipple (note_item->outline_gc, &note_item->outline_stipple, note_item->outline_stipple, TRUE);
+ set_outline_gc_width (note_item);
+
+ get_bounds (note_item, &x0, &y0, &x1, &y1);
+ gnome_canvas_update_bbox (item, x0, y0, x1, y1);
+ }
+}
+
diff --git a/libnautilus-private/nautilus-canvas-note-item.h b/libnautilus-private/nautilus-canvas-note-item.h
new file mode 100644
index 000000000..3c763c3df
--- /dev/null
+++ b/libnautilus-private/nautilus-canvas-note-item.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+ nautilus-canvas-note-item.h: annotation canvas item for Nautilus
+
+ Copyright (C) 2001 Eazel, Inc.
+
+ This program 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; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program 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. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ based on gnome_canvas_rect_item by Federico Mena Quintero
+
+ Author: Andy Hertzfeld <andy@eazel.com>
+*/
+
+#ifndef NAUTILUS_CANVAS_NOTE_ITEM_H
+#define NAUTILUS_CANVAS_NOTE_ITEM_H
+
+#include <libgnome/gnome-defs.h>
+#include <libgnomeui/gnome-canvas.h>
+
+#include <libart_lgpl/art_svp.h>
+
+BEGIN_GNOME_DECLS
+
+#define NAUTILUS_TYPE_CANVAS_NOTE_ITEM (nautilus_canvas_note_item_get_type ())
+#define NAUTILUS_CANVAS_NOTE_ITEM(obj) (GTK_CHECK_CAST ((obj), NAUTILUS_TYPE_CANVAS_NOTE_ITEM, NautilusCanvasNoteItem))
+#define NAUTILUS_CANVAS_NOTE_ITEM_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), NAUTILUS_CANVAS_NOTE_ITEM, NautilusCanvasNoteItemClass))
+#define NAUTILUS_IS_CANVAS_NOTE_ITEM(obj) (GTK_CHECK_TYPE ((obj), NAUTILUS_TYPE_CANVAS_NOTE_ITEM))
+#define NAUTILUS_IS_CANVAS_NOTE_ITEM_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_CANVAS_NOTE_ITEM))
+
+
+typedef struct _NautilusCanvasNoteItem NautilusCanvasNoteItem;
+typedef struct _NautilusCanvasNoteItemClass NautilusCanvasNoteItemClass;
+
+struct _NautilusCanvasNoteItem {
+ GnomeCanvasItem item;
+
+ double x1; /* canvas coordinates of bounding box */
+ double y1;
+ double x2;
+ double y2;
+
+ double width; /* Outline width */
+
+ guint fill_color; /* Fill color, RGBA */
+ guint outline_color; /* Outline color, RGBA */
+
+ gulong fill_pixel; /* Fill color */
+ gulong outline_pixel; /* Outline color */
+
+ GdkBitmap *fill_stipple; /* Stipple for fill */
+ GdkBitmap *outline_stipple; /* Stipple for outline */
+
+ GdkGC *fill_gc; /* GC for filling */
+ GdkGC *outline_gc; /* GC for outline */
+
+ /* text message */
+ char *note_text; /* text for annotation */
+
+ /* Antialiased specific stuff follows */
+ ArtSVP *fill_svp; /* The SVP for the filled shape */
+ ArtSVP *outline_svp; /* The SVP for the outline shape */
+
+ /* Configuration flags */
+ unsigned int width_pixels : 1; /* Is outline width specified in pixels or units? */
+};
+
+struct _NautilusCanvasNoteItemClass {
+ GnomeCanvasItemClass parent_class;
+};
+
+/* Standard Gtk function */
+GtkType nautilus_canvas_note_item_get_type (void);
+
+
+END_GNOME_DECLS
+
+#endif
diff --git a/libnautilus-private/nautilus-file-utilities.c b/libnautilus-private/nautilus-file-utilities.c
index d015879cd..5f6c5c990 100644
--- a/libnautilus-private/nautilus-file-utilities.c
+++ b/libnautilus-private/nautilus-file-utilities.c
@@ -224,7 +224,7 @@ nautilus_get_user_main_directory (void)
/* If this fails to create the directory, nautilus_application_startup will
* notice and refuse to launch.
*/
-
+
/* install the default link sets */
nautilus_link_set_install (user_main_directory, "apps");
nautilus_link_set_install (user_main_directory, "home");
diff --git a/libnautilus-private/nautilus-file.c b/libnautilus-private/nautilus-file.c
index 8bd2b7c23..e251032dc 100644
--- a/libnautilus-private/nautilus-file.c
+++ b/libnautilus-private/nautilus-file.c
@@ -25,6 +25,7 @@
#include <config.h>
#include "nautilus-file.h"
+#include "nautilus-annotation.h"
#include "nautilus-directory-metafile.h"
#include "nautilus-directory-notify.h"
#include "nautilus-directory-private.h"
@@ -1500,6 +1501,10 @@ prepend_automatic_emblem_names (NautilusFile *file,
{
/* Prepend in reverse order. */
+ if (nautilus_annotation_has_annotation (file) > 0) {
+ names = g_list_prepend
+ (names, g_strdup (NAUTILUS_FILE_EMBLEM_ANNOTATION));
+ }
if (nautilus_file_is_in_trash (file)) {
names = g_list_prepend
(names, g_strdup (NAUTILUS_FILE_EMBLEM_NAME_TRASH));
diff --git a/libnautilus-private/nautilus-file.h b/libnautilus-private/nautilus-file.h
index 8a4892969..820cae932 100644
--- a/libnautilus-private/nautilus-file.h
+++ b/libnautilus-private/nautilus-file.h
@@ -71,6 +71,7 @@ typedef enum {
#define NAUTILUS_FILE_EMBLEM_NAME_CANT_READ "noread"
#define NAUTILUS_FILE_EMBLEM_NAME_CANT_WRITE "nowrite"
#define NAUTILUS_FILE_EMBLEM_NAME_TRASH "trash"
+#define NAUTILUS_FILE_EMBLEM_ANNOTATION "note"
typedef void (*NautilusFileCallback) (NautilusFile *file,
gpointer callback_data);
diff --git a/libnautilus-private/nautilus-global-preferences.c b/libnautilus-private/nautilus-global-preferences.c
index c9d59c17a..5603d7dee 100644
--- a/libnautilus-private/nautilus-global-preferences.c
+++ b/libnautilus-private/nautilus-global-preferences.c
@@ -368,6 +368,18 @@ static const PreferenceDefault preference_defaults[] = {
{ NAUTILUS_USER_LEVEL_NOVICE, GINT_TO_POINTER (TRUE) },
{ USER_LEVEL_NONE }
},
+ { NAUTILUS_PREFERENCES_LOOKUP_ANNOTATIONS,
+ PREFERENCE_BOOLEAN,
+ NAUTILUS_USER_LEVEL_INTERMEDIATE,
+ { NAUTILUS_USER_LEVEL_NOVICE, GINT_TO_POINTER (FALSE) },
+ { USER_LEVEL_NONE }
+ },
+ { NAUTILUS_PREFERENCES_DISPLAY_ANNOTATIONS,
+ PREFERENCE_BOOLEAN,
+ NAUTILUS_USER_LEVEL_INTERMEDIATE,
+ { NAUTILUS_USER_LEVEL_NOVICE, GINT_TO_POINTER (FALSE) },
+ { USER_LEVEL_NONE }
+ },
{ NAUTILUS_PREFERENCES_PREVIEW_SOUND,
PREFERENCE_INTEGER,
NAUTILUS_USER_LEVEL_INTERMEDIATE,
@@ -654,6 +666,14 @@ global_preferences_install_defaults (void)
preference_defaults[i].visible_user_level);
}
+ nautilus_preferences_default_set_boolean (NAUTILUS_PREFERENCES_LOOKUP_ANNOTATIONS,
+ NAUTILUS_USER_LEVEL_NOVICE,
+ FALSE);
+
+ nautilus_preferences_default_set_boolean (NAUTILUS_PREFERENCES_DISPLAY_ANNOTATIONS,
+ NAUTILUS_USER_LEVEL_NOVICE,
+ FALSE);
+
/* Add the gnome-vfs path to the list of monitored directories - for proxy settings */
nautilus_preferences_monitor_directory (SYSTEM_GNOME_VFS_PATH);
@@ -1041,6 +1061,24 @@ static PreferenceDialogItem tradeoffs_items[] = {
{ NULL }
};
+static PreferenceDialogItem annotation_items[] = {
+ { N_("Lookup File Annotations"),
+ NAUTILUS_PREFERENCES_LOOKUP_ANNOTATIONS,
+ N_("Use the service to lookup file annotations"),
+ NAUTILUS_PREFERENCE_ITEM_BOOLEAN,
+ NULL,
+ 0
+ },
+ { N_("Display File Annotations"),
+ NAUTILUS_PREFERENCES_DISPLAY_ANNOTATIONS,
+ N_("Display emblems for file annotations"),
+ NAUTILUS_PREFERENCE_ITEM_BOOLEAN,
+ NULL,
+ 0
+ },
+ { NULL, NULL, NULL, 0, NULL, 0 }
+};
+
static GtkWidget *
global_preferences_create_dialog (void)
{
@@ -1105,6 +1143,11 @@ global_preferences_create_dialog (void)
_("Speed Tradeoffs"),
tradeoffs_items);
+ /* Annotations */
+ global_preferences_populate_pane (preference_box,
+ _("Annotations"),
+ annotation_items);
+
/* Update the dialog so that the right items show up based on the current user level */
nautilus_preferences_dialog_update (NAUTILUS_PREFERENCES_DIALOG (prefs_dialog));
diff --git a/libnautilus-private/nautilus-global-preferences.h b/libnautilus-private/nautilus-global-preferences.h
index d52cd42fe..fdaac1e2b 100644
--- a/libnautilus-private/nautilus-global-preferences.h
+++ b/libnautilus-private/nautilus-global-preferences.h
@@ -110,6 +110,10 @@ enum
NAUTILUS_DEFAULT_FOLDER_VIEWER_LIST_VIEW
};
+/* enabling annotations */
+#define NAUTILUS_PREFERENCES_LOOKUP_ANNOTATIONS "preferences/lookup_annotations"
+#define NAUTILUS_PREFERENCES_DISPLAY_ANNOTATIONS "preferences/display_annotations"
+
/* Icon View */
#define NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_SORT_IN_REVERSE_ORDER "icon-view/default_sort_in_reverse_order"
#define NAUTILUS_PREFERENCES_ICON_VIEW_DEFAULT_SORT_ORDER "icon-view/default_sort_order"
diff --git a/libnautilus-private/nautilus-icon-canvas-item.c b/libnautilus-private/nautilus-icon-canvas-item.c
index 2e9c11bb6..58b729a95 100644
--- a/libnautilus-private/nautilus-icon-canvas-item.c
+++ b/libnautilus-private/nautilus-icon-canvas-item.c
@@ -3,7 +3,6 @@
/* Nautilus - Icon canvas item class for icon container.
*
* Copyright (C) 2000 Eazel, Inc
- *
* Author: Andy Hertzfeld <andy@eazel.com>
*
* This library is free software; you can redistribute it and/or
@@ -29,8 +28,10 @@
#include <string.h>
#include <stdio.h>
#include <gtk/gtksignal.h>
+#include <gtk/gtkmain.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <libgnome/gnome-i18n.h>
+#include <libgnomeui/gnome-canvas-rect-ellipse.h>
#include <libgnomeui/gnome-canvas-util.h>
#include <libgnomeui/gnome-icon-text.h>
#include <libart_lgpl/art_rgb.h>
@@ -38,6 +39,7 @@
#include <libart_lgpl/art_rgb_rgba_affine.h>
#include <libart_lgpl/art_svp_vpath.h>
#include "nautilus-icon-private.h"
+#include "nautilus-canvas-note-item.h"
#include <eel/eel-string.h>
#include <eel/eel-art-extensions.h>
#include <eel/eel-glib-extensions.h>
@@ -54,9 +56,6 @@
#include <eel/eel-smooth-text-layout.h>
#include <eel/eel-smooth-text-layout-cache.h>
-/* Comment this out if the new smooth fonts code give you problems
- * This isnt meant to be permanent. Its just a precaution.
- */
#define EMBLEM_SPACING 2
/* gap between bottom of icon and start of text box */
@@ -80,6 +79,17 @@ struct NautilusIconCanvasItemDetails {
GdkFont *font;
NautilusEmblemAttachPoints *attach_points;
+ /* stuff for controls; if this gets too big, we'll put it in a separate struct */
+ GtkWidget *control; /* optional Bonobo control*/
+ guint control_destroy_id;
+
+ /* stuff for annotations - since these are infrequently used, we probably should
+ * combine them into a structure so we only use a pointer for every item eventually
+ */
+ GnomeCanvasItem *annotation;
+ int annotation_time_out;
+ int note_state;
+
/* Size of the text at current font. */
int text_width;
int text_height;
@@ -93,7 +103,7 @@ struct NautilusIconCanvasItemDetails {
guint is_highlighted_for_drop : 1;
guint show_stretch_handles : 1;
guint is_prelit : 1;
-
+ guint in_control_destroy : 1;
gboolean is_renaming;
/* Font stuff whilst in smooth mode */
@@ -112,7 +122,7 @@ enum {
ARG_EDITABLE_TEXT,
ARG_ADDITIONAL_TEXT,
ARG_FONT,
- ARG_HIGHLIGHTED_FOR_SELECTION,
+ ARG_HIGHLIGHTED_FOR_SELECTION,
ARG_HIGHLIGHTED_AS_KEYBOARD_FOCUS,
ARG_HIGHLIGHTED_FOR_DROP,
ARG_MODIFIER,
@@ -210,9 +220,13 @@ static void get_icon_canvas_rectangle (NautilusIconCanvasIt
static void emblem_layout_reset (EmblemLayout *layout,
NautilusIconCanvasItem *icon_item,
const ArtIRect *icon_rect);
-static gboolean emblem_layout_next (EmblemLayout *layout,
+static gboolean emblem_layout_next (EmblemLayout *layout,
GdkPixbuf **emblem_pixbuf,
ArtIRect *emblem_rect);
+static void get_emblem_rectangle (NautilusIconCanvasItem *icon_item,
+ int which_emblem,
+ ArtIRect *rect);
+
static void draw_pixbuf (GdkPixbuf *pixbuf,
GdkDrawable *drawable,
int x,
@@ -222,7 +236,6 @@ static gboolean hit_test_stretch_handle (NautilusIconCanvasIt
static gboolean icon_canvas_item_is_smooth (const NautilusIconCanvasItem *icon_item);
-
EEL_DEFINE_CLASS_BOILERPLATE (NautilusIconCanvasItem, nautilus_icon_canvas_item, GNOME_TYPE_CANVAS_ITEM)
static EelSmoothTextLayoutCache *layout_cache;
@@ -308,7 +321,8 @@ nautilus_icon_canvas_item_initialize (NautilusIconCanvasItem *icon_item)
/* set up the default font and size */
icon_item->details->smooth_font_size = 12;
- icon_item->details->smooth_font = eel_scalable_font_get_default_font ();
+ icon_item->details->smooth_font = eel_scalable_font_get_default_font ();
+ icon_item->details->annotation_time_out = -1;
}
/* Destroy handler for the icon canvas item. */
@@ -339,6 +353,11 @@ nautilus_icon_canvas_item_destroy (GtkObject *object)
gdk_font_unref (details->font);
}
+ if (details->control && !details->in_control_destroy) {
+ gtk_signal_disconnect (GTK_OBJECT (details->control), details->control_destroy_id);
+ gtk_widget_destroy (details->control);
+ }
+
gtk_object_unref (GTK_OBJECT (icon_item->details->smooth_font));
icon_item->details->smooth_font = NULL;
@@ -370,6 +389,43 @@ nautilus_icon_canvas_item_invalidate_label_size (NautilusIconCanvasItem *item)
item->details->text_height = -1;
}
+/* abstraction layer for icon width and height, to separate it from pixbuf with and height */
+static int
+nautilus_icon_canvas_item_get_icon_width (NautilusIconCanvasItem *item)
+{
+ GtkRequisition size_requisition;
+ double scale_factor = GNOME_CANVAS_ITEM (item)->canvas->pixels_per_unit;
+
+ if (item->details->control != NULL) {
+ gtk_widget_size_request (item->details->control, &size_requisition);
+ return size_requisition.width * scale_factor;
+ }
+
+ if (item->details->pixbuf == NULL) {
+ return NAUTILUS_ICON_SIZE_STANDARD;
+ }
+
+ return gdk_pixbuf_get_width (item->details->pixbuf);
+}
+
+static int
+nautilus_icon_canvas_item_get_icon_height (NautilusIconCanvasItem *item)
+{
+ GtkRequisition size_requisition;
+ double scale_factor = GNOME_CANVAS_ITEM (item)->canvas->pixels_per_unit;
+
+ if (item->details->control != NULL) {
+ gtk_widget_size_request (item->details->control, &size_requisition);
+ return size_requisition.height * scale_factor;
+ }
+ if (item->details->pixbuf == NULL) {
+ return NAUTILUS_ICON_SIZE_STANDARD;
+ }
+
+ return gdk_pixbuf_get_height (item->details->pixbuf);
+}
+
+
/* Set_arg handler for the icon item. */
static void
nautilus_icon_canvas_item_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
@@ -452,8 +508,8 @@ nautilus_icon_canvas_item_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
case ARG_SMOOTH_FONT_SIZE:
nautilus_icon_canvas_item_set_smooth_font_size (NAUTILUS_ICON_CANVAS_ITEM (object),
GTK_VALUE_INT (*arg));
- break;
-
+ break;
+
default:
g_warning ("nautilus_icons_view_item_item_set_arg on unknown argument");
return;
@@ -462,12 +518,27 @@ nautilus_icon_canvas_item_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
gnome_canvas_item_request_update (GNOME_CANVAS_ITEM (object));
}
+/* handler for the control's destroy signal */
+static void
+do_control_destroy (GtkObject *object, gpointer data)
+{
+ NautilusIconCanvasItemDetails *details;
+
+ details = NAUTILUS_ICON_CANVAS_ITEM (data)->details;
+
+ details->in_control_destroy = TRUE;
+
+ gtk_object_destroy (GTK_OBJECT (data));
+}
+
/* Get_arg handler for the icon item */
static void
nautilus_icon_canvas_item_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
{
NautilusIconCanvasItemDetails *details;
+ GnomeCanvasItem *item;
+ item = GNOME_CANVAS_ITEM (object);
details = NAUTILUS_ICON_CANVAS_ITEM (object)->details;
switch (arg_id) {
@@ -515,12 +586,27 @@ GdkPixbuf *
nautilus_icon_canvas_item_get_image (NautilusIconCanvasItem *item)
{
NautilusIconCanvasItemDetails *details;
-
+ int width, height;
+ GdkPixbuf *pixbuf;
+
g_return_val_if_fail (NAUTILUS_IS_ICON_CANVAS_ITEM (item), NULL);
details = item->details;
- return details->pixbuf;
+ if (details->control) {
+ width = details->control->allocation.width;
+ height = details->control->allocation.height;
+ pixbuf = eel_gdk_pixbuf_get_from_window_safe (details->control->window,
+ details->control->allocation.x,
+ details->control->allocation.y,
+ details->control->allocation.width,
+ details->control->allocation.height);
+ } else {
+ pixbuf = details->pixbuf;
+ gdk_pixbuf_ref (pixbuf);
+ }
+
+ return pixbuf;
}
void
@@ -622,6 +708,12 @@ recompute_bounding_box (NautilusIconCanvasItem *icon_item)
item->y1 = top_left.y;
item->x2 = bottom_right.x;
item->y2 = bottom_right.y;
+
+ if (icon_item->details->control)
+ gtk_layout_move (GTK_LAYOUT (item->canvas), icon_item->details->control,
+ item->x1 + item->canvas->zoom_xofs,
+ item->y1 + item->canvas->zoom_yofs);
+
}
static void
@@ -636,13 +728,16 @@ compute_text_rectangle (NautilusIconCanvasItem *item,
text_rect->y1 = text_rect->y0 + item->details->text_height;
}
+
void
nautilus_icon_canvas_item_update_bounds (NautilusIconCanvasItem *item)
{
ArtIRect before, after, emblem_rect;
EmblemLayout emblem_layout;
GdkPixbuf *emblem_pixbuf;
-
+ GtkRequisition size_requisition;
+ int item_width, item_height;
+
/* Compute new bounds. */
eel_gnome_canvas_item_get_current_canvas_bounds
(GNOME_CANVAS_ITEM (item), &before);
@@ -669,6 +764,16 @@ nautilus_icon_canvas_item_update_bounds (NautilusIconCanvasItem *item)
art_irect_union (&item->details->emblem_rect, &item->details->emblem_rect, &emblem_rect);
}
+ /* if there is an embedded control, make a size request and size accordingly */
+ if (item->details->control) {
+ /* size the control appropriately */
+ gtk_widget_size_request (item->details->control, &size_requisition);
+ item_width = size_requisition.width * GNOME_CANVAS_ITEM (item)->canvas->pixels_per_unit;
+ item_height = size_requisition.height * GNOME_CANVAS_ITEM (item)->canvas->pixels_per_unit;
+
+ gtk_widget_set_usize (item->details->control, item_width, item_height);
+ }
+
/* Send out the bounds_changed signal and queue a redraw. */
eel_gnome_canvas_request_redraw_rectangle
(GNOME_CANVAS_ITEM (item)->canvas, &before);
@@ -809,7 +914,7 @@ draw_or_measure_label_text (NautilusIconCanvasItem *item,
canvas_item = GNOME_CANVAS_ITEM (item);
if (drawable != NULL) {
- icon_width = details->pixbuf == NULL ? 0 : gdk_pixbuf_get_width (details->pixbuf);
+ icon_width = details->pixbuf == NULL ? 0 : nautilus_icon_canvas_item_get_icon_width (item);
gc = gdk_gc_new (canvas_item->canvas->layout.bin_window);
gdk_gc_get_values (gc, &save_gc);
}
@@ -1125,6 +1230,7 @@ emblem_layout_next (EmblemLayout *layout,
/* Advance to the next emblem. */
layout->emblem = layout->emblem->next;
+ layout->index += 1;
attach_points = layout->icon_item->details->attach_points;
if (attach_points != NULL) {
@@ -1135,8 +1241,6 @@ emblem_layout_next (EmblemLayout *layout,
x = layout->icon_rect.x0 + attach_points->points[layout->index].x;
y = layout->icon_rect.y0 + attach_points->points[layout->index].y;
- layout->index += 1;
-
/* Return the rectangle and pixbuf. */
*emblem_pixbuf = pixbuf;
emblem_rect->x0 = x - width / 2;
@@ -1211,11 +1315,17 @@ emblem_layout_next (EmblemLayout *layout,
/* Return the rectangle and pixbuf. */
*emblem_pixbuf = pixbuf;
- emblem_rect->x0 = x - width / 2;
- emblem_rect->y0 = y - height / 2;
+ if (layout->icon_item->details->control) {
+ emblem_rect->x0 = x;
+ emblem_rect->y0 = y;
+ } else {
+ emblem_rect->x0 = x - width / 2;
+ emblem_rect->y0 = y - height / 2;
+ }
+
emblem_rect->x1 = emblem_rect->x0 + width;
emblem_rect->y1 = emblem_rect->y0 + height;
-
+
return TRUE;
}
@@ -1330,10 +1440,6 @@ nautilus_icon_canvas_item_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
icon_item = NAUTILUS_ICON_CANVAS_ITEM (item);
details = icon_item->details;
- /* Draw the pixbuf. */
- if (details->pixbuf == NULL) {
- return;
- }
/* Compute icon rectangle in drawable coordinates. */
icon_rect = icon_item->details->canvas_rect;
@@ -1341,12 +1447,29 @@ nautilus_icon_canvas_item_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
icon_rect.y0 -= y;
icon_rect.x1 -= x;
icon_rect.y1 -= y;
+ /* draw the icon or widget */
+ if (icon_item->details->control) {
+ gtk_widget_queue_draw (icon_item->details->control);
+ } else {
+ if (details->pixbuf != NULL) {
+
+ /* Compute icon rectangle in drawable coordinates. */
+ get_icon_canvas_rectangle (icon_item, &icon_rect);
+ icon_rect.x0 -= x;
+ icon_rect.y0 -= y;
+ icon_rect.x1 -= x;
+ icon_rect.y1 -= y;
+
+ /* if the pre-lit or selection flag is set, make a pre-lit or darkened pixbuf and draw that instead */
+ temp_pixbuf = map_pixbuf (icon_item);
+ draw_pixbuf (temp_pixbuf, drawable, icon_rect.x0, icon_rect.y0);
+
+ if (temp_pixbuf != details->pixbuf) {
+ gdk_pixbuf_unref (temp_pixbuf);
+ }
+
+ }
- /* if the pre-lit or selection flag is set, make a pre-lit or darkened pixbuf and draw that instead */
- temp_pixbuf = map_pixbuf (icon_item);
- draw_pixbuf (temp_pixbuf, drawable, icon_rect.x0, icon_rect.y0);
- if (temp_pixbuf != details->pixbuf) {
- gdk_pixbuf_unref (temp_pixbuf);
}
/* Draw the emblem pixbufs. */
@@ -1431,7 +1554,7 @@ draw_or_measure_label_text_aa (NautilusIconCanvasItem *item,
if (destination_pixbuf == NULL ) {
icon_width = 0;
} else {
- icon_width = details->pixbuf == NULL ? 0 : gdk_pixbuf_get_width (details->pixbuf);
+ icon_width = details->pixbuf == NULL ? 0 : nautilus_icon_canvas_item_get_icon_width (item);
}
max_text_width = floor (nautilus_icon_canvas_item_get_max_text_width (item));
@@ -1706,15 +1829,21 @@ nautilus_icon_canvas_item_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
gnome_canvas_buf_ensure_buf (buf);
buf->is_bg = FALSE;
}
-
- /* draw the icon */
- eel_gnome_canvas_draw_pixbuf (buf, temp_pixbuf, icon_rect.x0, icon_rect.y0);
+
+ /* draw the icon or widget */
+ if (icon_item->details->control) {
+ gtk_widget_queue_draw (icon_item->details->control);
+ } else {
+ eel_gnome_canvas_draw_pixbuf (buf, temp_pixbuf, icon_rect.x0, icon_rect.y0);
+ }
if (temp_pixbuf != icon_item->details->pixbuf) {
gdk_pixbuf_unref (temp_pixbuf);
}
-
- /* draw the emblems */
+
+ /* draw the emblems */
+ get_icon_canvas_rectangle (icon_item, &icon_rect);
+
emblem_layout_reset (&emblem_layout, icon_item, &icon_rect);
while (emblem_layout_next (&emblem_layout, &emblem_pixbuf, &emblem_rect)) {
eel_gnome_canvas_draw_pixbuf (buf, emblem_pixbuf, emblem_rect.x0, emblem_rect.y0);
@@ -1728,6 +1857,124 @@ nautilus_icon_canvas_item_render (GnomeCanvasItem *item, GnomeCanvasBuf *buf)
draw_label_text_aa (icon_item, buf, icon_rect.x0, icon_rect.y1, x_delta);
}
+/* create an annotation for the emblem designated by the passed-in index */
+static void
+create_annotation (NautilusIconCanvasItem *icon_item, int emblem_index)
+{
+ uint fill_color, outline_color;
+ double top, left;
+ double right, bottom;
+ ArtDRect icon_rect;
+ ArtIRect emblem_rect;
+ int emblem_x, emblem_y;
+ double world_emblem_x, world_emblem_y;
+ int annotation_width;
+ char *note_text;
+ GnomeCanvas *canvas;
+ GnomeCanvasItem *item;
+
+ /* compute the position for the top left of the annotation */
+ nautilus_icon_canvas_item_get_icon_rectangle (icon_item, &icon_rect);
+ left = icon_rect.x0 + 8.0;
+ top = icon_rect.y0 + 8.0;
+
+ fill_color = 0xFFFF75E0;
+ outline_color = 0x000000FF;
+
+ canvas = GNOME_CANVAS_ITEM (icon_item)->canvas;
+ item = GNOME_CANVAS_ITEM (icon_item);
+
+ note_text = nautilus_icon_container_get_note_text (NAUTILUS_ICON_CONTAINER (canvas), icon_item->user_data, emblem_index);
+
+ icon_item->details->annotation = gnome_canvas_item_new
+ (gnome_canvas_root (canvas),
+ nautilus_canvas_note_item_get_type (),
+ "x1", left,
+ "y1", top,
+ "fill_color_rgba", fill_color,
+ "outline_color_rgba", outline_color,
+ "note_text", note_text,
+ "width_pixels", 1,
+ NULL);
+
+ g_free (note_text);
+
+ /* reposition the item now that it's had a chance to be properly sized */
+ if (canvas->aa) {
+ get_emblem_rectangle (icon_item, emblem_index, &emblem_rect);
+ annotation_width = icon_item->details->annotation->x2 - icon_item->details->annotation->x1;
+
+ emblem_x = (emblem_rect.x0 + emblem_rect.x1) / 2;
+ emblem_y = (emblem_rect.y0 + emblem_rect.y1) / 2;
+ gnome_canvas_c2w (canvas, emblem_x, emblem_y, &world_emblem_x, &world_emblem_y);
+
+ left = world_emblem_x - (annotation_width / 2.0 );
+ top = world_emblem_y;
+ right = left + annotation_width;
+ bottom = top + icon_item->details->annotation->y2 - icon_item->details->annotation->y1;
+
+ gnome_canvas_item_set (icon_item->details->annotation, "x1", left, "y1", top, "x2", right, "y2", bottom, NULL);
+ }
+
+ gnome_canvas_item_raise_to_top (icon_item->details->annotation);
+}
+
+/* remove any annotation that's showing */
+static void
+remove_annotation (NautilusIconCanvasItem *icon_item)
+{
+ if (icon_item->details->annotation != NULL) {
+ gtk_object_destroy (GTK_OBJECT (icon_item->details->annotation));
+ icon_item->details->annotation = NULL;
+ icon_item->details->note_state = 0;
+ }
+}
+
+/* handle the timeout firing by creating the annotation */
+static int
+create_annotation_timeout_callback (gpointer callback_data)
+{
+ NautilusIconCanvasItem *icon_item;
+
+ icon_item = NAUTILUS_ICON_CANVAS_ITEM (callback_data);
+ create_annotation (icon_item, icon_item->details->note_state);
+
+ icon_item->details->annotation_time_out = -1;
+ return 0;
+}
+
+/* manage showing and hiding annotations, based on mouse-over the passed-in emblem */
+static void
+nautilus_icon_canvas_item_set_note_state (NautilusIconCanvasItem *icon_item, int new_state)
+{
+ /* nothing to do if nothing changed */
+ if (new_state == icon_item->details->note_state) {
+ return;
+ }
+
+ /* if there already is a timeout in progress and we're showing one, just wait for it to fire */
+ if (new_state > 0 && icon_item->details->annotation_time_out >= 0) {
+ return;
+ }
+
+ /* get rid of the old annotation, if there was one */
+ if (icon_item->details->annotation) {
+ remove_annotation (icon_item);
+ }
+
+ if (icon_item->details->annotation_time_out >= 0) {
+ gtk_timeout_remove (icon_item->details->annotation_time_out);
+ icon_item->details->annotation_time_out = -1;
+ }
+
+ icon_item->details->note_state = new_state;
+
+ /* add a timeout to create the new annotation */
+ if (new_state > 0) {
+ icon_item->details->annotation_time_out = gtk_timeout_add (750, create_annotation_timeout_callback, icon_item);
+ }
+}
+
/* handle events */
@@ -1735,7 +1982,12 @@ static int
nautilus_icon_canvas_item_event (GnomeCanvasItem *item, GdkEvent *event)
{
NautilusIconCanvasItem *icon_item;
-
+ GdkEventMotion *motion_event;
+ ArtIRect hit_rect;
+ ArtDRect world_rect;
+ HitType hit_type;
+ int hit_index, emblem_state;
+
icon_item = NAUTILUS_ICON_CANVAS_ITEM (item);
switch (event->type) {
@@ -1782,10 +2034,29 @@ nautilus_icon_canvas_item_event (GnomeCanvasItem *item, GdkEvent *event)
icon_item->details->is_prelit = FALSE;
icon_item->details->is_active = 0;
icon_item->details->is_highlighted_for_drop = FALSE;
+
+ nautilus_icon_canvas_item_set_note_state (icon_item, 0);
gnome_canvas_item_request_update (item);
}
return TRUE;
+
+ case GDK_MOTION_NOTIFY:
+ motion_event = (GdkEventMotion*) event;
+
+ world_rect.x0 = motion_event->x;
+ world_rect.y0 = motion_event->y;
+ world_rect.x1 = world_rect.x0 + 1.0;
+ world_rect.y1 = world_rect.y0 + 1.0;
+
+ eel_gnome_canvas_world_to_canvas_rectangle
+ (GNOME_CANVAS_ITEM (item)->canvas, &world_rect, &hit_rect);
+ /* hit-test so we can handle tooltips for emblems */
+ nautilus_icon_canvas_item_hit_test_full (icon_item, &hit_rect, &hit_type, &hit_index);
+ emblem_state = hit_type == EMBLEM_HIT ? hit_index : 0;
+ nautilus_icon_canvas_item_set_note_state (icon_item, emblem_state);
+ return TRUE;
+
default:
/* Don't eat up other events; icon container might use them. */
return FALSE;
@@ -1838,10 +2109,14 @@ hit_test_pixbuf (GdkPixbuf *pixbuf, const ArtIRect *pixbuf_location, const ArtIR
return FALSE;
}
-static gboolean
-hit_test (NautilusIconCanvasItem *icon_item, const ArtIRect *canvas_rect)
+gboolean
+nautilus_icon_canvas_item_hit_test_full (NautilusIconCanvasItem *icon_item,
+ const ArtIRect *canvas_rect,
+ HitType *hit_type,
+ int *hit_index)
{
NautilusIconCanvasItemDetails *details;
+ ArtIRect icon_rect;
ArtIRect emblem_rect;
EmblemLayout emblem_layout;
GdkPixbuf *emblem_pixbuf;
@@ -1855,12 +2130,40 @@ hit_test (NautilusIconCanvasItem *icon_item, const ArtIRect *canvas_rect)
return FALSE;
}
+ /* default to -1, which means nothing was hit */
+ if (hit_index != NULL) {
+ *hit_index = -1;
+ }
+
/* Check for hits in the stretch handles. */
if (hit_test_stretch_handle (icon_item, canvas_rect)) {
+ if (hit_type != NULL) {
+ *hit_type = STRETCH_HANDLE_HIT;
+ }
return TRUE;
}
/* Check for hit in the icon. If we're highlighted for dropping, anywhere in the rect is OK */
+ get_icon_canvas_rectangle (icon_item, &icon_rect);
+
+ /* Check for hit in the emblem pixbufs first, since they appear on top of the icon. */
+ emblem_layout_reset (&emblem_layout, icon_item, &icon_item->details->canvas_rect);
+ while (emblem_layout_next (&emblem_layout, &emblem_pixbuf, &emblem_rect)) {
+ if (hit_test_pixbuf (emblem_pixbuf, &emblem_rect, canvas_rect)) {
+ if (hit_type != NULL) {
+ *hit_type = EMBLEM_HIT;
+ }
+ if (hit_index != NULL) {
+ *hit_index = emblem_layout.index;
+ }
+ return TRUE;
+ }
+ }
+
+ if (hit_type != NULL) {
+ *hit_type = ICON_HIT;
+ }
+
if (icon_item->details->is_highlighted_for_drop) {
if (eel_art_irect_hits_irect (&icon_item->details->canvas_rect, canvas_rect)) {
return TRUE;
@@ -1874,17 +2177,17 @@ hit_test (NautilusIconCanvasItem *icon_item, const ArtIRect *canvas_rect)
/* Check for hit in the text. */
if (eel_art_irect_hits_irect (&details->text_rect, canvas_rect)
&& !icon_item->details->is_renaming) {
+ if (hit_type != NULL) {
+ *hit_type = LABEL_HIT;
+ }
return TRUE;
}
- /* Check for hit in the emblem pixbufs. */
- emblem_layout_reset (&emblem_layout, icon_item, &icon_item->details->canvas_rect);
- while (emblem_layout_next (&emblem_layout, &emblem_pixbuf, &emblem_rect)) {
- if (hit_test_pixbuf (emblem_pixbuf, &emblem_rect, canvas_rect)) {
- return TRUE;
- }
+ /* there wasn't a hit, so indicate that */
+ if (hit_type != NULL) {
+ *hit_type = NO_HIT;
}
-
+
return FALSE;
}
@@ -1900,7 +2203,7 @@ nautilus_icon_canvas_item_point (GnomeCanvasItem *item, double x, double y, int
canvas_rect.y0 = cy;
canvas_rect.x1 = cx + 1;
canvas_rect.y1 = cy + 1;
- if (hit_test (NAUTILUS_ICON_CANVAS_ITEM (item), &canvas_rect)) {
+ if (nautilus_icon_canvas_item_hit_test_rectangle (NAUTILUS_ICON_CANVAS_ITEM (item), &canvas_rect)) {
return 0.0;
} else {
/* This value means not hit.
@@ -1939,8 +2242,8 @@ nautilus_icon_canvas_item_bounds (GnomeCanvasItem *item,
icon_rect.x1 = 0;
icon_rect.y1 = 0;
} else {
- icon_rect.x1 = gdk_pixbuf_get_width (details->pixbuf);
- icon_rect.y1 = gdk_pixbuf_get_height (details->pixbuf);
+ icon_rect.x1 = nautilus_icon_canvas_item_get_icon_width (icon_item);
+ icon_rect.y1 = nautilus_icon_canvas_item_get_icon_height (icon_item);
}
/* Compute text rectangle. */
@@ -1986,10 +2289,32 @@ nautilus_icon_canvas_item_get_icon_rectangle (NautilusIconCanvasItem *item,
pixbuf = item->details->pixbuf;
pixels_per_unit = GNOME_CANVAS_ITEM (item)->canvas->pixels_per_unit;
- rect->x1 = rect->x0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_width (pixbuf)) / pixels_per_unit;
- rect->y1 = rect->y0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_height (pixbuf)) / pixels_per_unit;
+ rect->x1 = rect->x0 + (pixbuf == NULL ? 0 : nautilus_icon_canvas_item_get_icon_width (item)) / pixels_per_unit;
+ rect->y1 = rect->y0 + (pixbuf == NULL ? 0 : nautilus_icon_canvas_item_get_icon_height (item)) / pixels_per_unit;
}
+static void
+get_emblem_rectangle (NautilusIconCanvasItem *icon_item,
+ int which_emblem,
+ ArtIRect *rect)
+{
+ EmblemLayout emblem_layout;
+ GdkPixbuf *pixbuf;
+ int emblem_index;
+
+ emblem_layout_reset (&emblem_layout, icon_item, &icon_item->details->canvas_rect);
+ emblem_index = 0;
+
+ rect->x0 = 0;
+ rect->y0 = 0;
+ rect->x1 = 0;
+ rect->y1 = 0;
+
+ while (emblem_index < which_emblem && emblem_layout_next (&emblem_layout, &pixbuf, rect)) {
+ emblem_index += 1;
+ }
+}
+
/* Get the rectangle of the icon only, in canvas coordinates. */
static void
get_icon_canvas_rectangle (NautilusIconCanvasItem *item,
@@ -2013,8 +2338,8 @@ get_icon_canvas_rectangle (NautilusIconCanvasItem *item,
pixbuf = item->details->pixbuf;
- rect->x1 = rect->x0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_width (pixbuf));
- rect->y1 = rect->y0 + (pixbuf == NULL ? 0 : gdk_pixbuf_get_height (pixbuf));
+ rect->x1 = rect->x0 + (pixbuf == NULL ? 0 : nautilus_icon_canvas_item_get_icon_width (item));
+ rect->y1 = rect->y0 + (pixbuf == NULL ? 0 : nautilus_icon_canvas_item_get_icon_height (item));
}
void
@@ -2110,10 +2435,11 @@ nautilus_icon_canvas_item_hit_test_stretch_handles (NautilusIconCanvasItem *item
gboolean
nautilus_icon_canvas_item_hit_test_rectangle (NautilusIconCanvasItem *item, const ArtIRect *canvas_rect)
{
+
g_return_val_if_fail (NAUTILUS_IS_ICON_CANVAS_ITEM (item), FALSE);
g_return_val_if_fail (canvas_rect != NULL, FALSE);
- return hit_test (item, canvas_rect);
+ return nautilus_icon_canvas_item_hit_test_full (item, canvas_rect, NULL, NULL);
}
const char *
@@ -2171,6 +2497,41 @@ nautilus_icon_canvas_item_set_smooth_font (NautilusIconCanvasItem *icon_item,
}
}
+GtkWidget *
+nautilus_icon_canvas_item_get_control (NautilusIconCanvasItem *icon_item)
+{
+ return icon_item->details->control;
+}
+
+void
+nautilus_icon_canvas_item_set_control (NautilusIconCanvasItem *icon_item, GtkWidget *control)
+{
+ GnomeCanvasItem *item;
+
+ if (icon_item->details->control == control) {
+ return;
+ }
+
+ item = GNOME_CANVAS_ITEM (icon_item);
+ if (icon_item->details->control) {
+ gtk_signal_disconnect (GTK_OBJECT (icon_item->details->control), icon_item->details->control_destroy_id);
+ gtk_container_remove (GTK_CONTAINER (item->canvas), icon_item->details->control);
+ icon_item->details->control = NULL;
+ }
+
+ if (control) {
+ icon_item->details->control = control;
+ icon_item->details->control_destroy_id = gtk_signal_connect (GTK_OBJECT (control),
+ "destroy",
+ (GtkSignalFunc) do_control_destroy,
+ item);
+ gtk_widget_show (control);
+ gtk_layout_put (GTK_LAYOUT (item->canvas), control,
+ item->x1 + item->canvas->zoom_xofs,
+ item->y1 + item->canvas->zoom_yofs);
+ }
+}
+
void
nautilus_icon_canvas_item_set_smooth_font_size (NautilusIconCanvasItem *icon_item,
int font_size)
diff --git a/libnautilus-private/nautilus-icon-canvas-item.h b/libnautilus-private/nautilus-icon-canvas-item.h
index f7be68e70..2f9f90a34 100644
--- a/libnautilus-private/nautilus-icon-canvas-item.h
+++ b/libnautilus-private/nautilus-icon-canvas-item.h
@@ -44,6 +44,14 @@ BEGIN_GNOME_DECLS
#define NAUTILUS_IS_ICON_CANVAS_ITEM_CLASS(klass) \
(GTK_CHECK_CLASS_TYPE ((klass), NAUTILUS_TYPE_ICON_CANVAS_ITEM))
+typedef enum {
+ NO_HIT,
+ ICON_HIT,
+ LABEL_HIT,
+ STRETCH_HANDLE_HIT,
+ EMBLEM_HIT
+} HitType;
+
typedef struct NautilusIconCanvasItem NautilusIconCanvasItem;
typedef struct NautilusIconCanvasItemClass NautilusIconCanvasItemClass;
typedef struct NautilusIconCanvasItemDetails NautilusIconCanvasItemDetails;
@@ -80,10 +88,19 @@ const char *nautilus_icon_canvas_item_get_editable_text (NautilusIconCanv
void nautilus_icon_canvas_item_set_renaming (NautilusIconCanvasItem *icon_item,
gboolean state);
+GtkWidget * nautilus_icon_canvas_item_get_control (NautilusIconCanvasItem *icon_item);
+void nautilus_icon_canvas_item_set_control (NautilusIconCanvasItem *icon_item,
+ GtkWidget *control);
+
/* geometry and hit testing */
gboolean nautilus_icon_canvas_item_hit_test_rectangle (NautilusIconCanvasItem *item,
const ArtIRect *canvas_rect);
+gboolean nautilus_icon_canvas_item_hit_test_full (NautilusIconCanvasItem *icon_item,
+ const ArtIRect *canvas_rect,
+ HitType *hit_type,
+ int *hit_index);
+
gboolean nautilus_icon_canvas_item_hit_test_stretch_handles (NautilusIconCanvasItem *item,
const ArtPoint *world_point);
void nautilus_icon_canvas_item_invalidate_label_size (NautilusIconCanvasItem *item);
diff --git a/libnautilus-private/nautilus-icon-container.c b/libnautilus-private/nautilus-icon-container.c
index 9daecc782..e41f03522 100644
--- a/libnautilus-private/nautilus-icon-container.c
+++ b/libnautilus-private/nautilus-icon-container.c
@@ -119,10 +119,12 @@ enum {
*/
};
-static void activate_selected_items (NautilusIconContainer *container);
+static void activate_selected_items (NautilusIconContainer *container,
+ int select_location);
static void nautilus_icon_container_initialize_class (NautilusIconContainerClass *class);
static void nautilus_icon_container_initialize (NautilusIconContainer *container);
static void nautilus_icon_container_theme_changed (gpointer user_data);
+static void nautilus_icon_container_annotation_changed (gpointer user_data);
static void compute_stretch (StretchState *start,
StretchState *current);
@@ -166,10 +168,12 @@ enum {
CONTEXT_CLICK_SELECTION,
MIDDLE_CLICK,
GET_CONTAINER_URI,
+ GET_ICON_CONTROL,
GET_ICON_IMAGES,
GET_ICON_TEXT,
GET_ICON_URI,
GET_ICON_DROP_TARGET_URI,
+ GET_ICON_ANNOTATION,
GET_STORED_ICON_POSITION,
ICON_POSITION_CHANGED,
ICON_TEXT_CHANGED,
@@ -2286,7 +2290,6 @@ select_previous_or_next_name (NautilusIconContainer *container,
}
/* GtkObject methods. */
-
static void
destroy (GtkObject *object)
{
@@ -2333,6 +2336,10 @@ destroy (GtkObject *object)
gtk_object_destroy (GTK_OBJECT (container->details->rename_widget));
}
+ /* remove the annotation preference callbacks */
+ nautilus_preferences_remove_callback (NAUTILUS_PREFERENCES_LOOKUP_ANNOTATIONS, nautilus_icon_container_annotation_changed, container);
+ nautilus_preferences_remove_callback (NAUTILUS_PREFERENCES_DISPLAY_ANNOTATIONS, nautilus_icon_container_annotation_changed, container);
+
/* FIXME: The code to extract colors from the theme should be in FMDirectoryView, not here.
* The NautilusIconContainer class should simply provide calls to set the colors.
*/
@@ -2530,13 +2537,42 @@ button_press_event (GtkWidget *widget,
return return_value;
}
+/* utility routine to determine which portion of an icon was clicked, and return the
+ * emblem index if an emblem was clicked on
+ */
+static int
+hit_test_item (NautilusIconCanvasItem *icon_item, GdkEventButton *event)
+{
+ ArtDRect world_rect;
+ ArtIRect canvas_rect;
+ HitType hit_type;
+ int emblem_index;
+
+ gnome_canvas_window_to_world (GNOME_CANVAS_ITEM (icon_item)->canvas, event->x, event->y,
+ &world_rect.x0, &world_rect.y0);
+ world_rect.x1 = world_rect.x0 + 1.0;
+ world_rect.y1 = world_rect.y0 + 1.0;
+
+ eel_gnome_canvas_world_to_canvas_rectangle
+ (GNOME_CANVAS_ITEM (icon_item)->canvas, &world_rect, &canvas_rect);
+
+ if (nautilus_icon_canvas_item_hit_test_full (icon_item, &canvas_rect, &hit_type, &emblem_index)) {
+ if (hit_type == EMBLEM_HIT) {
+ return emblem_index;
+ }
+ }
+ return 0;
+}
+
static void
nautilus_icon_container_did_not_drag (NautilusIconContainer *container,
GdkEventButton *event)
{
+ int click_location;
NautilusIconContainerDetails *details;
+
details = container->details;
-
+
if (!button_event_modifies_selection (event) && !details->drag_icon->is_selected) {
gboolean selection_changed;
@@ -2565,7 +2601,8 @@ nautilus_icon_container_did_not_drag (NautilusIconContainer *container,
* NautilusList goes the other way because its "links" seem
* much more link-like.
*/
- activate_selected_items (container);
+ click_location = hit_test_item (details->drag_icon->item, event);
+ activate_selected_items (container, click_location);
}
}
}
@@ -2997,7 +3034,7 @@ key_press_event (GtkWidget *widget,
break;
case GDK_Return:
case GDK_KP_Enter:
- activate_selected_items (container);
+ activate_selected_items (container, 0);
handled = TRUE;
break;
case GDK_Escape:
@@ -3070,9 +3107,10 @@ nautilus_icon_container_initialize_class (NautilusIconContainerClass *class)
object_class->type,
GTK_SIGNAL_OFFSET (NautilusIconContainerClass,
activate),
- gtk_marshal_NONE__POINTER,
- GTK_TYPE_NONE, 1,
- GTK_TYPE_POINTER);
+ gtk_marshal_NONE__POINTER_INT,
+ GTK_TYPE_NONE, 2,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_INT);
signals[CONTEXT_CLICK_SELECTION]
= gtk_signal_new ("context_click_selection",
GTK_RUN_LAST,
@@ -3147,6 +3185,16 @@ nautilus_icon_container_initialize_class (NautilusIconContainerClass *class)
gtk_marshal_NONE__POINTER,
GTK_TYPE_NONE, 1,
GTK_TYPE_POINTER);
+ signals[GET_ICON_CONTROL]
+ = gtk_signal_new ("get_icon_control",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (NautilusIconContainerClass,
+ get_icon_control),
+ gtk_marshal_NONE__POINTER_POINTER,
+ GTK_TYPE_NONE, 2,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_POINTER);
signals[GET_ICON_IMAGES]
= gtk_signal_new ("get_icon_images",
GTK_RUN_LAST,
@@ -3187,6 +3235,16 @@ nautilus_icon_container_initialize_class (NautilusIconContainerClass *class)
eel_gtk_marshal_STRING__POINTER,
GTK_TYPE_STRING, 1,
GTK_TYPE_POINTER);
+ signals[GET_ICON_ANNOTATION]
+ = gtk_signal_new ("get_icon_annotation",
+ GTK_RUN_LAST,
+ object_class->type,
+ GTK_SIGNAL_OFFSET (NautilusIconContainerClass,
+ get_icon_annotation),
+ eel_gtk_marshal_STRING__POINTER_INT,
+ GTK_TYPE_STRING, 2,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_INT);
signals[COMPARE_ICONS]
= gtk_signal_new ("compare_icons",
GTK_RUN_LAST,
@@ -3384,6 +3442,10 @@ nautilus_icon_container_initialize (NautilusIconContainer *container)
gtk_signal_connect (GTK_OBJECT (container), "focus-out-event", handle_focus_out_event, NULL);
+ /* add callbacks to notify us when the annotation state changes */
+ nautilus_preferences_add_callback (NAUTILUS_PREFERENCES_LOOKUP_ANNOTATIONS, nautilus_icon_container_annotation_changed, container);
+ nautilus_preferences_add_callback (NAUTILUS_PREFERENCES_DISPLAY_ANNOTATIONS, nautilus_icon_container_annotation_changed, container);
+
/* FIXME: The code to extract colors from the theme should be in FMDirectoryView, not here.
* The NautilusIconContainer class should simply provide calls to set the colors.
*/
@@ -3519,7 +3581,7 @@ handle_icon_button_press (NautilusIconContainer *container,
details->drag_button = 0;
details->drag_icon = NULL;
- activate_selected_items (container);
+ activate_selected_items (container, 0);
}
return TRUE;
@@ -3674,7 +3736,7 @@ icon_destroy (NautilusIconContainer *container,
/* activate any selected items in the container */
static void
-activate_selected_items (NautilusIconContainer *container)
+activate_selected_items (NautilusIconContainer *container, int select_location)
{
GList *selection;
@@ -3684,7 +3746,8 @@ activate_selected_items (NautilusIconContainer *container)
if (selection != NULL) {
gtk_signal_emit (GTK_OBJECT (container),
signals[ACTIVATE],
- selection);
+ selection,
+ select_location);
}
g_list_free (selection);
}
@@ -3720,6 +3783,7 @@ nautilus_icon_container_update_icon (NautilusIconContainer *container,
GdkPixbuf *pixbuf, *emblem_pixbuf, *saved_pixbuf;
GList *emblem_scalable_icons, *emblem_pixbufs, *p;
char *editable_text, *additional_text;
+ GtkWidget *embedded_control;
GdkFont *font;
int smooth_font_size;
@@ -3754,8 +3818,7 @@ nautilus_icon_container_update_icon (NautilusIconContainer *container,
eel_scalable_icon_unref (scalable_icon);
- /* in the rare case an image is too small, scale it up */
-
+ /* in the rare case an image is too small, scale it up */
width = gdk_pixbuf_get_width (pixbuf);
height = gdk_pixbuf_get_height (pixbuf);
if (width < min_image_size || height < min_image_size) {
@@ -3763,9 +3826,11 @@ nautilus_icon_container_update_icon (NautilusIconContainer *container,
/* don't let it exceed the maximum width in the other dimension */
scale_factor = MIN (scale_factor, max_image_size / width);
scale_factor = MIN (scale_factor, max_image_size / height);
-
+
scaled_width = floor (width * scale_factor + .5);
scaled_height = floor (height * scale_factor + .5);
+
+ /* scale the image to the calculated size */
saved_pixbuf = pixbuf;
pixbuf = gdk_pixbuf_scale_simple (pixbuf, scaled_width, scaled_height, GDK_INTERP_BILINEAR);
gdk_pixbuf_unref (saved_pixbuf);
@@ -3794,13 +3859,22 @@ nautilus_icon_container_update_icon (NautilusIconContainer *container,
emblem_pixbufs = g_list_reverse (emblem_pixbufs);
eel_scalable_icon_list_free (emblem_scalable_icons);
+ /* get the embedded control, if any */
+ embedded_control = nautilus_icon_canvas_item_get_control (icon->item);
+ if (embedded_control == NULL) {
+ gtk_signal_emit (GTK_OBJECT (container),
+ signals[GET_ICON_CONTROL],
+ icon->data,
+ &embedded_control);
+ }
+
/* Get both editable and non-editable icon text */
gtk_signal_emit (GTK_OBJECT (container),
signals[GET_ICON_TEXT],
icon->data,
&editable_text,
&additional_text);
-
+
/* If name of icon being renamed was changed from elsewhere, end renaming mode.
* Alternatively, we could replace the characters in the editable text widget
* with the new name, but that could cause timing problems if the user just
@@ -3825,6 +3899,8 @@ nautilus_icon_container_update_icon (NautilusIconContainer *container,
"smooth_font", details->smooth_label_font,
NULL);
+ nautilus_icon_canvas_item_set_control (icon->item, embedded_control);
+
nautilus_icon_canvas_item_set_image (icon->item, pixbuf);
nautilus_icon_canvas_item_set_attach_points (icon->item, &attach_points);
nautilus_icon_canvas_item_set_emblems (icon->item, emblem_pixbufs);
@@ -5073,6 +5149,33 @@ update_label_color (EelBackground *background,
}
}
+/* handle the annotation preference changes by updating the icons */
+static void
+nautilus_icon_container_annotation_changed (gpointer user_data)
+{
+ nautilus_icon_container_request_update_all (NAUTILUS_ICON_CONTAINER (user_data));
+}
+
+
+/* handle fetching annotation info from the controller */
+char *
+nautilus_icon_container_get_note_text (NautilusIconContainer *container,
+ gpointer user_data,
+ int emblem_index)
+{
+ NautilusIcon *icon;
+ char *note_text;
+ note_text = NULL;
+
+ icon = (NautilusIcon*) user_data;
+ gtk_signal_emit (GTK_OBJECT (container),
+ signals[GET_ICON_ANNOTATION],
+ icon->data,
+ emblem_index,
+ &note_text);
+
+ return note_text;
+}
/* Return if the icon container is a fixed size */
gboolean
diff --git a/libnautilus-private/nautilus-icon-container.h b/libnautilus-private/nautilus-icon-container.h
index 30382a19e..4510110ec 100644
--- a/libnautilus-private/nautilus-icon-container.h
+++ b/libnautilus-private/nautilus-icon-container.h
@@ -107,6 +107,10 @@ typedef struct {
gboolean (* get_stored_icon_position) (NautilusIconContainer *container,
NautilusIconData *data,
NautilusIconPosition *position);
+ void
+ (* get_icon_control) (NautilusIconContainer *container,
+ NautilusIconData *data,
+ GtkWidget **control);
NautilusScalableIcon *
(* get_icon_images) (NautilusIconContainer *container,
NautilusIconData *data,
@@ -120,6 +124,9 @@ typedef struct {
NautilusIconData *data);
char * (* get_icon_drop_target_uri) (NautilusIconContainer *container,
NautilusIconData *data);
+ char * (* get_icon_annotation) (NautilusIconContainer *container,
+ NautilusIconData *data,
+ int annotation_index);
int (* compare_icons) (NautilusIconContainer *container,
NautilusIconData *icon_a,
NautilusIconData *icon_b);
diff --git a/libnautilus-private/nautilus-icon-dnd.c b/libnautilus-private/nautilus-icon-dnd.c
index 4d5364ea3..059051002 100644
--- a/libnautilus-private/nautilus-icon-dnd.c
+++ b/libnautilus-private/nautilus-icon-dnd.c
@@ -1279,11 +1279,12 @@ nautilus_icon_dnd_begin_drag (NautilusIconContainer *container,
/* create a pixmap and mask to drag with */
- pixbuf = nautilus_icon_canvas_item_get_image (container->details->drag_icon->item);
-
+
/* we want to drag semi-transparent pixbufs, but X is too slow dealing with
stippled masks, so we had to remove the code; this comment is left as a memorial
to it, with the hope that we get it back someday as X Windows improves */
+
+ pixbuf = nautilus_icon_canvas_item_get_image (container->details->drag_icon->item);
/* compute the image's offset */
nautilus_icon_canvas_item_get_icon_rectangle
diff --git a/libnautilus-private/nautilus-icon-private.h b/libnautilus-private/nautilus-icon-private.h
index b1b73c147..d31a13265 100644
--- a/libnautilus-private/nautilus-icon-private.h
+++ b/libnautilus-private/nautilus-icon-private.h
@@ -254,4 +254,8 @@ void nautilus_icon_container_update_scroll_region (NautilusIconC
guint32 nautilus_icon_container_get_label_color (NautilusIconContainer *container,
gboolean first_line);
+char * nautilus_icon_container_get_note_text (NautilusIconContainer *container,
+ gpointer user_data,
+ int emblem_index);
+
#endif /* NAUTILUS_ICON_CONTAINER_PRIVATE_H */
diff --git a/libnautilus-private/nautilus-link.c b/libnautilus-private/nautilus-link.c
index 8fd7fcaba..76860d397 100644
--- a/libnautilus-private/nautilus-link.c
+++ b/libnautilus-private/nautilus-link.c
@@ -312,6 +312,35 @@ nautilus_link_local_get_additional_text (const char *path)
(path, NAUTILUS_METADATA_KEY_EXTRA_TEXT);
}
+void nautilus_link_local_get_component_info (const char *path,
+ char **control_moniker, char **control_data)
+{
+ xmlDoc *document;
+ const char *mime_type;
+
+ *control_moniker = NULL;
+ *control_data = NULL;
+
+ /* Check mime type. Exit if it is not a nautilus link */
+ mime_type = gnome_vfs_get_file_mime_type (path, NULL, FALSE);
+ if (strcmp (mime_type, "application/x-nautilus-link") != 0) {
+ return;
+ }
+
+ document = xmlParseFile (path);
+ if (document != NULL) {
+ *control_moniker = xml_get_root_property (document,
+ NAUTILUS_METADATA_KEY_CONTROL_MONIKER);
+
+ *control_data = xml_get_root_property (document,
+ NAUTILUS_METADATA_KEY_CONTROL_DATA);
+
+ xmlFreeDoc (document);
+ }
+}
+
+
+
/* utility to return the local pathname of a cached icon, given the leaf name */
/* if the icons directory hasn't been created yet, create it */
static char *
diff --git a/libnautilus-private/nautilus-link.h b/libnautilus-private/nautilus-link.h
index 88882ccb5..b9596eb7d 100644
--- a/libnautilus-private/nautilus-link.h
+++ b/libnautilus-private/nautilus-link.h
@@ -76,14 +76,21 @@ gboolean nautilus_link_local_set_link_uri (const char
* none. Despite the fact that it takes a URI parameter, works only if
* the file is local and does sync. I/O.
*/
-char * nautilus_link_local_get_additional_text (const char *path);
+char * nautilus_link_local_get_additional_text (const char *path);
/* Returns the image associated with a link file. Despite the fact
* that it takes a URI parameter, works only if the file is local and
* does sync. I/O on the link, although it does async. on the image
* and caches if the image is remote.
*/
-char * nautilus_link_local_get_image_uri (const char *path);
+char * nautilus_link_local_get_image_uri (const char *path);
+
+/* returns the moniker of the component associated with a link file, as well as configuration data.
+ * It works only if the file is local and does sync. I/O.
+ */
+void nautilus_link_local_get_component_info (const char *path,
+ char **control_moniker,
+ char **control_data);
/* Returns the link type of a link file.
* Works only if the file is local and does sync. I/O
diff --git a/libnautilus-private/nautilus-metadata.h b/libnautilus-private/nautilus-metadata.h
index 00f96a2da..70681853f 100644
--- a/libnautilus-private/nautilus-metadata.h
+++ b/libnautilus-private/nautilus-metadata.h
@@ -72,6 +72,12 @@
#define NAUTILUS_METADATA_KEY_ICON_SCALE "icon_scale"
#define NAUTILUS_METADATA_KEY_CUSTOM_ICON "custom_icon"
+#define NAUTILUS_METADATA_KEY_FILE_DIGEST "digest"
+#define NAUTILUS_METADATA_KEY_NOTES_INFO "notes_info"
+
+#define NAUTILUS_METADATA_KEY_CONTROL_MONIKER "control_moniker"
+#define NAUTILUS_METADATA_KEY_CONTROL_DATA "control_data"
+
/* per link file */
#define NAUTILUS_METADATA_KEY_EXTRA_TEXT "extra_text"
diff --git a/nautilus-clean.sh b/nautilus-clean.sh
index bbdc458b7..d6b86c357 100755
--- a/nautilus-clean.sh
+++ b/nautilus-clean.sh
@@ -96,6 +96,7 @@ nautilus-summary-view \
nautilus-text-view \
nautilus-throbber \
nautilus-tree-view \
+nautilus-vcard \
trilobite-eazel-install-service \
trilobite-eazel-time-view \
"
diff --git a/src/file-manager/Makefile.am b/src/file-manager/Makefile.am
index a1c825f98..0366f104f 100644
--- a/src/file-manager/Makefile.am
+++ b/src/file-manager/Makefile.am
@@ -19,6 +19,7 @@ INCLUDES = \
libnautilus_file_manager_la_SOURCES= \
+ fm-annotation-window.c \
fm-desktop-icon-view.c \
fm-directory-view.c \
fm-error-reporting.c \
@@ -31,6 +32,7 @@ libnautilus_file_manager_la_SOURCES= \
$(NULL)
noinst_HEADERS = \
+ fm-annotation-window.h \
fm-desktop-icon-view.h \
fm-directory-view.h \
fm-error-reporting.h \
diff --git a/src/file-manager/fm-annotation-window.c b/src/file-manager/fm-annotation-window.c
new file mode 100644
index 000000000..022cbb0c4
--- /dev/null
+++ b/src/file-manager/fm-annotation-window.c
@@ -0,0 +1,400 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* fm-annotation-window.c - window that lets user modify file annotations
+
+ Copyright (C) 2001 Eazel, Inc.
+
+ The Gnome Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Library 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. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Authors: Andy Hertzfeld <andy@eazel.com>
+*/
+
+#include <config.h>
+#include "fm-annotation-window.h"
+
+#include "fm-error-reporting.h"
+#include <gtk/gtkfilesel.h>
+#include <gtk/gtkhbox.h>
+#include <gtk/gtkhseparator.h>
+#include <gtk/gtklabel.h>
+#include <gtk/gtkmain.h>
+#include <gtk/gtknotebook.h>
+#include <gtk/gtkoptionmenu.h>
+#include <gtk/gtkpixmap.h>
+#include <gtk/gtkscrolledwindow.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtktable.h>
+#include <gtk/gtktext.h>
+#include <gtk/gtkvbox.h>
+#include <libgnome/gnome-defs.h>
+#include <libgnome/gnome-i18n.h>
+#include <libgnomeui/gnome-dialog.h>
+#include <libgnomeui/gnome-uidefs.h>
+#include <libgnomevfs/gnome-vfs.h>
+#include <libnautilus-extensions/nautilus-annotation.h>
+#include <libnautilus-extensions/nautilus-customization-data.h>
+#include <eel/eel-ellipsizing-label.h>
+#include <libnautilus-extensions/nautilus-entry.h>
+#include <libnautilus-extensions/nautilus-file-attributes.h>
+#include <libnautilus-extensions/nautilus-file-utilities.h>
+#include <libnautilus-extensions/nautilus-font-factory.h>
+#include <eel/eel-gdk-pixbuf-extensions.h>
+#include <eel/eel-glib-extensions.h>
+#include <libnautilus-extensions/nautilus-global-preferences.h>
+#include <eel/eel-gnome-extensions.h>
+#include <eel/eel-gtk-extensions.h>
+#include <eel/eel-gtk-macros.h>
+#include <libnautilus-extensions/nautilus-icon-factory.h>
+#include <eel/eel-image.h>
+#include <libnautilus-extensions/nautilus-link.h>
+#include <libnautilus-extensions/nautilus-metadata.h>
+#include <eel/eel-stock-dialogs.h>
+#include <eel/eel-string.h>
+#include <libnautilus-extensions/nautilus-undo-signal-handlers.h>
+#include <libnautilus/nautilus-undo.h>
+#include <eel/eel-wrap-table.h>
+#include <eel/eel-labeled-image.h>
+#include <eel/eel-viewport.h>
+#include <string.h>
+
+struct FMAnnotationWindowDetails {
+ NautilusFile *file;
+ GtkWidget *file_icon;
+ GtkLabel *file_title;
+ GtkWidget *text_field;
+ char *access_mode;
+};
+
+
+static void real_destroy (GtkObject *object);
+static void fm_annotation_window_initialize_class (FMAnnotationWindowClass *class);
+static void fm_annotation_window_initialize (FMAnnotationWindow *window);
+static FMAnnotationWindow* create_annotation_window
+ (NautilusFile *file,
+ FMDirectoryView *directroy_view);
+
+EEL_DEFINE_CLASS_BOILERPLATE (FMAnnotationWindow, fm_annotation_window, GNOME_TYPE_DIALOG)
+
+static void
+fm_annotation_window_initialize_class (FMAnnotationWindowClass *class)
+{
+ GtkObjectClass *object_class;
+
+ object_class = GTK_OBJECT_CLASS (class);
+ object_class->destroy = real_destroy;
+}
+
+static void
+fm_annotation_window_initialize (FMAnnotationWindow *window)
+{
+ window->details = g_new0 (FMAnnotationWindowDetails, 1);
+ window->details->file = NULL;
+
+ /* default to local access only */
+ window->details->access_mode = g_strdup ("local");
+}
+
+static void
+real_destroy (GtkObject *object)
+{
+ FMAnnotationWindow *window;
+
+ window = FM_ANNOTATION_WINDOW (object);
+
+ nautilus_file_unref (window->details->file);
+
+ g_free (window->details->access_mode);
+ g_free (window->details);
+
+ EEL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
+}
+
+static GdkPixbuf *
+get_pixbuf_for_annotation_window (NautilusFile *file)
+{
+ g_assert (NAUTILUS_IS_FILE (file));
+
+ return nautilus_icon_factory_get_pixbuf_for_file (file, NULL, NAUTILUS_ICON_SIZE_LARGE, TRUE);
+}
+
+
+static void
+update_annotation_window_icon (EelImage *image)
+{
+ GdkPixbuf *pixbuf;
+ NautilusFile *file;
+
+ g_assert (EEL_IS_IMAGE (image));
+
+ file = gtk_object_get_data (GTK_OBJECT (image), "nautilus_file");
+
+ g_assert (NAUTILUS_IS_FILE (file));
+
+ pixbuf = get_pixbuf_for_annotation_window (file);
+
+ eel_image_set_pixbuf (image, pixbuf);
+
+ gdk_pixbuf_unref (pixbuf);
+}
+
+static GtkWidget *
+create_image_widget_for_file (NautilusFile *file)
+{
+ GtkWidget *image;
+ GdkPixbuf *pixbuf;
+
+ pixbuf = get_pixbuf_for_annotation_window (file);
+
+ image = eel_image_new (NULL);
+
+ eel_image_set_pixbuf (EEL_IMAGE (image), pixbuf);
+
+ gdk_pixbuf_unref (pixbuf);
+
+ nautilus_file_ref (file);
+ gtk_object_set_data_full (GTK_OBJECT (image),
+ "nautilus_file",
+ file,
+ (GtkDestroyNotify) nautilus_file_unref);
+
+ gtk_signal_connect_object_while_alive (nautilus_icon_factory_get (),
+ "icons_changed",
+ update_annotation_window_icon,
+ GTK_OBJECT (image));
+
+ gtk_signal_connect_object_while_alive (GTK_OBJECT (file),
+ "changed",
+ update_annotation_window_icon,
+ GTK_OBJECT (image));
+ return image;
+}
+
+
+static void
+update_annotation_window_title (GtkWindow *window, NautilusFile *file)
+{
+ char *name, *title;
+
+ g_assert (NAUTILUS_IS_FILE (file));
+ g_assert (GTK_IS_WINDOW (window));
+
+ name = nautilus_file_get_name (file);
+ title = g_strdup_printf (_("%s Annotation"), name);
+ gtk_window_set_title (window, title);
+
+ g_free (name);
+ g_free (title);
+}
+
+/* callback for access menu items */
+static void
+set_access_mode (GtkWidget *menu_item, const char* access_mode)
+{
+ FMAnnotationWindow *window;
+ window = FM_ANNOTATION_WINDOW (gtk_object_get_user_data (GTK_OBJECT (menu_item)));
+ g_free (window->details->access_mode);
+ window->details->access_mode = g_strdup (access_mode);
+}
+
+/* utility to create and append an access menu item */
+static void
+add_access_menu_item (FMAnnotationWindow *window, GtkWidget *access_menu, const char *menu_label, const char *id)
+{
+ GtkWidget *menu_item;
+
+ menu_item = gtk_menu_item_new_with_label (menu_label);
+ gtk_widget_show (menu_item);
+ gtk_menu_append (GTK_MENU (access_menu), menu_item);
+
+ gtk_object_set_user_data (GTK_OBJECT (menu_item), window);
+
+ gtk_signal_connect_full (GTK_OBJECT (menu_item),
+ "activate",
+ GTK_SIGNAL_FUNC (set_access_mode),
+ NULL,
+ g_strdup (id),
+ g_free,
+ FALSE,
+ FALSE);
+
+}
+
+/* create the option table for the annotation window */
+static GtkWidget *
+create_options_table (FMAnnotationWindow *window)
+{
+ GtkWidget *table;
+ GtkWidget *label;
+ GtkWidget *type_menu_vbox;
+ GtkWidget *access_menu_vbox;
+ GtkWidget *option_menu;
+ GtkWidget *new_menu;
+ GtkWidget *menu_item;
+
+ table = gtk_table_new (2, 2, FALSE);
+
+ label = gtk_label_new (_("Type:"));
+ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 4, 4);
+
+ /* type options will eventually be derived from an xml file, but there's only one
+ * for now
+ */
+ type_menu_vbox = gtk_vbox_new (FALSE, GNOME_PAD_SMALL);
+ option_menu = gtk_option_menu_new ();
+ gtk_box_pack_end (GTK_BOX (type_menu_vbox), option_menu, TRUE, FALSE, 0);
+ gtk_table_attach(GTK_TABLE(table), type_menu_vbox, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 4, 4);
+
+ new_menu = gtk_menu_new ();
+ menu_item = gtk_menu_item_new_with_label (_("free form note"));
+ gtk_widget_show (new_menu);
+ gtk_widget_show (menu_item);
+ gtk_menu_append (GTK_MENU (new_menu), menu_item);
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), new_menu);
+
+ /* now make the access option menu */
+ label = gtk_label_new (_("Access:"));
+ gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 4, 4);
+
+ access_menu_vbox = gtk_vbox_new (FALSE, GNOME_PAD_SMALL);
+ option_menu = gtk_option_menu_new ();
+ gtk_box_pack_end (GTK_BOX (access_menu_vbox), option_menu, TRUE, FALSE, 0);
+ gtk_table_attach(GTK_TABLE(table), access_menu_vbox, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 4, 4);
+
+ new_menu = gtk_menu_new ();
+ gtk_widget_show (new_menu);
+
+ add_access_menu_item (window, new_menu, _("local only"), "local");
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), new_menu);
+
+ add_access_menu_item (window, new_menu, _("share globally"), "global");
+ return table;
+}
+
+/* this callback is invoked when the OK or Cancel buttons are clicked, to add an annotation and dismiss the window */
+static void
+annotation_clicked_callback (GtkWidget *dialog, int which_button, gpointer user_data)
+{
+ char *notes_text;
+ FMAnnotationWindow *window;
+
+ window = FM_ANNOTATION_WINDOW (dialog);
+
+ if (which_button == GNOME_OK) {
+ notes_text = gtk_editable_get_chars (GTK_EDITABLE (window->details->text_field), 0 , -1);
+ /* type field is hard-wired for now */
+ nautilus_annotation_add_annotation (window->details->file,
+ "text",
+ notes_text,
+ window->details->access_mode);
+ g_free (notes_text);
+ }
+ gtk_widget_destroy (dialog);
+}
+
+/* create the annotation window, and allocate its widgets */
+static FMAnnotationWindow *
+create_annotation_window (NautilusFile *file, FMDirectoryView *directory_view)
+{
+ FMAnnotationWindow *window;
+ GtkWidget *hbox;
+ GtkBox *content_box;
+ GtkWidget *label;
+ GtkWidget *table;
+ char *file_name, *title;
+ GdkFont *font;
+ int position;
+ char *annotation_text;
+
+ window = FM_ANNOTATION_WINDOW (gtk_widget_new (fm_annotation_window_get_type (), NULL));
+
+ window->details->file = nautilus_file_ref (file);
+
+ gtk_container_set_border_width (GTK_CONTAINER (window), GNOME_PAD);
+ gtk_window_set_policy (GTK_WINDOW (window), FALSE, TRUE, FALSE);
+ gtk_window_set_wmclass (GTK_WINDOW (window), "file_annotation", "Nautilus");
+
+ /* add the buttons */
+ gnome_dialog_append_buttons (GNOME_DIALOG (window), GNOME_STOCK_BUTTON_OK, GNOME_STOCK_BUTTON_CANCEL, NULL);
+ gnome_dialog_set_default( GNOME_DIALOG (window), GNOME_OK);
+
+ gtk_signal_connect (GTK_OBJECT (window), "clicked", (GtkSignalFunc) annotation_clicked_callback, NULL);
+
+ /* get the container box of the dialog */
+ content_box = GTK_BOX (GNOME_DIALOG (window)->vbox);
+
+ /* allocate an hbox to hold the icon and the title */
+ hbox = gtk_hbox_new (FALSE, GNOME_PAD);
+ gtk_box_pack_start (GTK_BOX (content_box), hbox, FALSE, FALSE, GNOME_PAD);
+
+ /* allocate an icon and title */
+ window->details->file_icon = create_image_widget_for_file (window->details->file);
+ gtk_box_pack_start (GTK_BOX (hbox), window->details->file_icon, FALSE, FALSE, GNOME_PAD);
+
+ file_name = nautilus_file_get_name (window->details->file);
+ title = g_strdup_printf (_("Add Annotation to %s"), file_name);
+ label = gtk_label_new (title);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, GNOME_PAD);
+ g_free (file_name);
+ g_free (title);
+
+ /* now allocate a table to hold and populate the option boxes */
+ table = create_options_table (window);
+ gtk_box_pack_start (GTK_BOX (content_box), table, FALSE, FALSE, GNOME_PAD);
+
+ /* add the views as defined by the currently selected type */
+ /* at first, there is only one hardwired type, so just add it here */
+ window->details->text_field = gtk_text_new (NULL, NULL);
+
+ font = nautilus_font_factory_get_font_from_preferences (14);
+ eel_gtk_widget_set_font (window->details->text_field, font);
+ gdk_font_unref (font);
+
+ gtk_text_set_editable (GTK_TEXT (window->details->text_field), TRUE);
+ gtk_box_pack_start (GTK_BOX (content_box), window->details->text_field, TRUE, TRUE, 0);
+
+ /* set up the annotation field with the initial text, if any */
+ annotation_text = nautilus_annotation_get_annotation_for_display (window->details->file);
+ position = 0;
+ if (annotation_text) {
+ gtk_editable_insert_text (GTK_EDITABLE (window->details->text_field),
+ annotation_text,
+ strlen (annotation_text),
+ &position);
+
+ g_free (annotation_text);
+ }
+
+ update_annotation_window_title (GTK_WINDOW (window), window->details->file);
+
+ gtk_widget_show_all (GTK_WIDGET (window));
+ return window;
+}
+
+void
+fm_annotation_window_present (NautilusFile *file, FMDirectoryView *directory_view)
+{
+ g_return_if_fail (NAUTILUS_IS_FILE (file));
+ g_return_if_fail (FM_IS_DIRECTORY_VIEW (directory_view));
+
+ if (nautilus_file_is_directory (file)) {
+ eel_show_error_dialog (_("Sorry, but you currently can't add an annotation to a directory."), _("Can't annotate directory"), NULL);
+ return;
+ }
+
+ create_annotation_window (file, directory_view);
+}
+
diff --git a/src/file-manager/fm-annotation-window.h b/src/file-manager/fm-annotation-window.h
new file mode 100644
index 000000000..6b96195bc
--- /dev/null
+++ b/src/file-manager/fm-annotation-window.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/* fm-annotation-window.h - interface for window that lets user add and edit
+ file annotations.
+
+ Copyright (C) 2001 Eazel, Inc.
+
+ The Gnome Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Gnome Library 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. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Gnome Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Authors: Andy Hertzfeld <andy@eazel.com>
+*/
+
+#ifndef FM_ANNOTATION_WINDOW_H
+#define FM_ANNOTATION_WINDOW_H
+
+#include "fm-directory-view.h"
+
+#include <gtk/gtkwindow.h>
+#include <libgnomeui/gnome-dialog.h>
+#include <libnautilus-extensions/nautilus-file.h>
+
+typedef struct FMAnnotationWindow FMAnnotationWindow;
+
+#define FM_TYPE_ANNOTATION_WINDOW \
+ (fm_annotation_window_get_type ())
+#define FM_ANNOTATION_WINDOW(obj) \
+ (GTK_CHECK_CAST ((obj), FM_TYPE_ANNOTATION_WINDOW, FMAnnotationWindow))
+#define FM_ANNOTATION_WINDOW_CLASS(klass) \
+ (GTK_CHECK_CLASS_CAST ((klass), FM_TYPE_ANNOTATION_WINDOW, FMAnnotationWindowClass))
+#define FM_IS_ANNOTATION_WINDOW(obj) \
+ (GTK_CHECK_TYPE ((obj), FM_TYPE_ANNOTATION_WINDOW))
+#define FM_IS_ANNOTATION_WINDOW_CLASS(klass) \
+ (GTK_CHECK_CLASS_TYPE ((klass), FM_TYPE_ANNOTATION_WINDOW))
+
+typedef struct FMAnnotationWindowDetails FMAnnotationWindowDetails;
+
+struct FMAnnotationWindow {
+ GnomeDialog dialog;
+ FMAnnotationWindowDetails *details;
+};
+
+struct FMAnnotationWindowClass {
+ GnomeDialogClass parent_class;
+};
+
+typedef struct FMAnnotationWindowClass FMAnnotationWindowClass;
+
+GtkType fm_annotation_window_get_type (void);
+void fm_annotation_window_present (NautilusFile *file,
+ FMDirectoryView *directory_view);
+
+#endif /* FM_ANNOTATION_WINDOW_H */
diff --git a/src/file-manager/fm-icon-view.c b/src/file-manager/fm-icon-view.c
index 96c8bf578..004225d32 100644
--- a/src/file-manager/fm-icon-view.c
+++ b/src/file-manager/fm-icon-view.c
@@ -25,9 +25,11 @@
#include <config.h>
#include "fm-icon-view.h"
+#include "fm-annotation-window.h"
#include "fm-desktop-icon-view.h"
#include "fm-error-reporting.h"
#include "fm-icon-text-window.h"
+#include <bonobo/bonobo-widget.h>
#include <bonobo/bonobo-ui-util.h>
#include <ctype.h>
#include <errno.h>
@@ -45,6 +47,7 @@
#include <libgnomevfs/gnome-vfs-xfer.h>
#include <libnautilus-extensions/nautilus-audio-player.h>
#include <eel/eel-background.h>
+#include <libnautilus-extensions/nautilus-annotation.h>
#include <libnautilus-extensions/nautilus-bonobo-extensions.h>
#include <libnautilus-extensions/nautilus-directory-background.h>
#include <libnautilus-extensions/nautilus-directory.h>
@@ -73,6 +76,7 @@
/* Paths to use when creating & referring to Bonobo menu items */
#define MENU_PATH_RENAME "/menu/File/File Items Placeholder/Rename"
+#define MENU_PATH_ANNOTATE "/menu/File/File Items Placeholder/Annotate"
#define MENU_PATH_CUSTOMIZE_ICON_TEXT "/menu/Edit/Global Edit Items Placeholder/Icon Text"
#define MENU_PATH_STRETCH_ICON "/menu/Edit/Edit Items Placeholder/Stretch"
#define MENU_PATH_UNSTRETCH_ICONS "/menu/Edit/Edit Items Placeholder/Unstretch"
@@ -86,6 +90,7 @@
#define COMMAND_PREFIX "/commands/"
#define COMMAND_RENAME "/commands/Rename"
+#define COMMAND_ANNOTATE "/commands/Annotate"
#define COMMAND_STRETCH_ICON "/commands/Stretch"
#define COMMAND_UNSTRETCH_ICONS "/commands/Unstretch"
#define COMMAND_TIGHTER_LAYOUT "/commands/Tighter Layout"
@@ -401,6 +406,23 @@ rename_icon_callback (BonoboUIComponent *component, gpointer callback_data, cons
}
static void
+annotate_callback (BonoboUIComponent *component, gpointer callback_data, const char *verb)
+{
+ GList *selected_files;
+ NautilusFile *first_file;
+
+ g_assert (FM_IS_ICON_VIEW (callback_data));
+
+ /* show the annotation window */
+ selected_files = fm_directory_view_get_selection (FM_DIRECTORY_VIEW (callback_data));
+ if (selected_files != NULL) {
+ first_file = NAUTILUS_FILE (selected_files->data);
+ fm_annotation_window_present (first_file, FM_DIRECTORY_VIEW (callback_data));
+ g_list_free (selected_files);
+ }
+}
+
+static void
set_tighter_layout (FMIconView *icon_view, gboolean new_value)
{
fm_icon_view_set_directory_tighter_layout (icon_view,
@@ -1251,6 +1273,7 @@ fm_icon_view_merge_menus (FMDirectoryView *view)
FMIconView *icon_view;
BonoboUIVerb verbs [] = {
BONOBO_UI_VERB ("Rename", rename_icon_callback),
+ BONOBO_UI_VERB ("Annotate", annotate_callback),
BONOBO_UI_VERB ("Icon Text", customize_icon_text_callback),
BONOBO_UI_VERB ("Stretch", show_stretch_handles_callback),
BONOBO_UI_VERB ("Unstretch", unstretch_icons_callback),
@@ -1340,6 +1363,10 @@ fm_icon_view_update_menus (FMDirectoryView *view)
COMMAND_RENAME,
selection_count == 1
&& nautilus_file_can_rename (selection->data));
+
+ nautilus_bonobo_set_sensitive (icon_view->details->ui,
+ COMMAND_ANNOTATE,
+ selection_count == 1);
bonobo_ui_component_thaw (icon_view->details->ui, NULL);
@@ -1420,14 +1447,57 @@ fm_icon_view_set_selection (FMDirectoryView *view, GList *selection)
(get_icon_container (FM_ICON_VIEW (view)), selection);
}
+/* utility routine to return the specified keyword, given a file and emblem index */
+static char *
+get_keyword_by_index (NautilusFile *file, int emblem_index)
+{
+ GList *keyword_list, *selected_keyword;
+ char *keyword;
+
+ keyword_list = nautilus_file_get_emblem_names (file);
+ if (keyword_list == NULL) {
+ return NULL;
+ }
+
+ selected_keyword = g_list_nth (keyword_list, emblem_index - 1);
+ if (selected_keyword == NULL) {
+ eel_g_list_free_deep (keyword_list);
+ return NULL;
+ }
+
+ keyword = g_strdup (selected_keyword->data);
+ eel_g_list_free_deep (keyword_list);
+
+ return keyword;
+}
+
static void
icon_container_activate_callback (NautilusIconContainer *container,
GList *file_list,
+ int emblem_index,
FMIconView *icon_view)
{
+ char *keyword;
+ NautilusFile *file;
+
g_assert (FM_IS_ICON_VIEW (icon_view));
g_assert (container == get_icon_container (icon_view));
+ /* if there is a single file to be activated, and the user clicked on an emblem,
+ * implement the emblem-specific action instead of activation. For now,
+ * we just handle annotations.
+ */
+ if (emblem_index > 0 && file_list != NULL && file_list->next == NULL) {
+ file = NAUTILUS_FILE (file_list->data);
+ keyword = get_keyword_by_index (file, emblem_index);
+ if (eel_strcmp (keyword, "note") == 0) {
+ fm_annotation_window_present (file, FM_DIRECTORY_VIEW (icon_view));
+ g_free (keyword);
+ return;
+ }
+ g_free (keyword);
+ }
+
fm_directory_view_activate_files (FM_DIRECTORY_VIEW (icon_view), file_list);
}
@@ -1833,6 +1903,38 @@ get_icon_images_callback (NautilusIconContainer *container,
return nautilus_icon_factory_get_icon_for_file (file, modifier, smooth_graphics);
}
+/* return the Bonobo control associated with the icon, if any */
+static void
+get_icon_control_callback (NautilusIconContainer *container,
+ NautilusFile *file,
+ GtkWidget **control,
+ FMIconView *icon_view)
+{
+ Bonobo_UIContainer ui_container;
+ char *control_moniker, *control_data;
+ char *uri, *path;
+ *control = NULL;
+
+ if (nautilus_file_is_nautilus_link (file)) {
+ uri = nautilus_file_get_uri (file);
+ path = gnome_vfs_get_local_path_from_uri (uri);
+ if (path != NULL) {
+ nautilus_link_local_get_component_info (path, &control_moniker, &control_data);
+ if (control_moniker && strlen (control_moniker) > 0) {
+ ui_container = fm_directory_view_get_bonobo_ui_container (FM_DIRECTORY_VIEW (icon_view));
+ *control = bonobo_widget_new_control (control_moniker, ui_container);
+ g_free (control_moniker);
+ }
+ if (control_data && strlen (control_data) > 0) {
+ bonobo_widget_set_property (BONOBO_WIDGET (*control), "configuration", control_data, NULL);
+ g_free (control_data);
+ }
+ g_free (path);
+ }
+ g_free (uri);
+ }
+}
+
static char *
get_icon_uri_callback (NautilusIconContainer *container,
NautilusFile *file,
@@ -1897,6 +1999,7 @@ get_icon_text_callback (NautilusIconContainer *container,
g_assert (additional_text != NULL);
g_assert (FM_IS_ICON_VIEW (icon_view));
+
/* In the smallest zoom mode, no text is drawn. */
if (fm_icon_view_get_zoom_level (icon_view) == NAUTILUS_ZOOM_LEVEL_SMALLEST) {
*editable_text = NULL;
@@ -1904,7 +2007,7 @@ get_icon_text_callback (NautilusIconContainer *container,
/* Strip the suffix for nautilus object xml files. */
*editable_text = nautilus_file_get_name (file);
}
-
+
/* Handle link files specially. */
if (nautilus_file_is_nautilus_link (file)) {
/* FIXME bugzilla.eazel.com 2531: Does sync. I/O and works only locally. */
@@ -1955,6 +2058,26 @@ get_icon_text_callback (NautilusIconContainer *container,
g_strfreev (text_array);
}
+/* this callback returns the annotation text associated with the passed-in file and emblem index */
+static char *
+get_icon_annotation_callback (NautilusIconContainer *container,
+ NautilusFile *file,
+ int emblem_index,
+ FMIconView *icon_view)
+{
+ char *keyword;
+
+ keyword = get_keyword_by_index (file, emblem_index);
+
+ /* if the keyword is "note", return the file annotation instead */
+ if (eel_strcmp (keyword, "note") == 0) {
+ g_free (keyword);
+ keyword = nautilus_annotation_get_annotation (file);
+ }
+
+ return keyword;
+}
+
/* Preferences changed callbacks */
static void
fm_icon_view_text_attribute_names_changed (FMDirectoryView *directory_view)
@@ -2446,6 +2569,10 @@ create_icon_container (FMIconView *icon_view)
GTK_SIGNAL_FUNC (get_icon_images_callback),
icon_view);
gtk_signal_connect (GTK_OBJECT (icon_container),
+ "get_icon_control",
+ GTK_SIGNAL_FUNC (get_icon_control_callback),
+ icon_view);
+ gtk_signal_connect (GTK_OBJECT (icon_container),
"get_icon_uri",
GTK_SIGNAL_FUNC (get_icon_uri_callback),
icon_view);
@@ -2458,6 +2585,10 @@ create_icon_container (FMIconView *icon_view)
GTK_SIGNAL_FUNC (get_icon_text_callback),
icon_view);
gtk_signal_connect (GTK_OBJECT (icon_container),
+ "get_icon_annotation",
+ GTK_SIGNAL_FUNC (get_icon_annotation_callback),
+ icon_view);
+ gtk_signal_connect (GTK_OBJECT (icon_container),
"move_copy_items",
GTK_SIGNAL_FUNC (icon_view_move_copy_items),
directory_view);
diff --git a/src/file-manager/nautilus-icon-view-ui.xml b/src/file-manager/nautilus-icon-view-ui.xml
index 4cbe8b0ba..26ac06eb9 100644
--- a/src/file-manager/nautilus-icon-view-ui.xml
+++ b/src/file-manager/nautilus-icon-view-ui.xml
@@ -3,6 +3,9 @@
<cmd name="Rename"
_label="Rename"
_tip="Rename selected icon"/>
+ <cmd name="Annotate"
+ _label="Add Annotation..."
+ _tip="Add an annotation to this file"/>
<cmd name="Icon Text"
_label="Icon Captions..."
_tip="Choose which information appears beneath each icon's name"/>
@@ -153,6 +156,7 @@
<popup name="selection">
<placeholder name="File Actions">
<menuitem name="Rename" verb="Rename"/>
+ <menuitem name="Annotate" verb="Annotate"/>
</placeholder>
<placeholder name="Icon Appearance Items">
<menuitem name="Stretch" verb="Stretch"/>
diff --git a/src/nautilus-property-browser.c b/src/nautilus-property-browser.c
index f4c649c05..2fe2243e3 100644
--- a/src/nautilus-property-browser.c
+++ b/src/nautilus-property-browser.c
@@ -198,6 +198,8 @@ static void element_clicked_callback (GtkWidget
#define MAX_ICON_WIDTH 63
#define MAX_ICON_HEIGHT 63
#define COLOR_SQUARE_SIZE 48
+#define MAX_EMBLEM_HEIGHT 52
+#define STANDARD_BUTTON_IMAGE_HEIGHT 42
#define LABELED_IMAGE_SPACING 2
#define IMAGE_TABLE_X_SPACING 6
@@ -1951,6 +1953,7 @@ nautilus_property_browser_update_contents (NautilusPropertyBrowser *property_bro
IMAGE_TABLE_X_SPACING);
eel_wrap_table_set_y_spacing (EEL_WRAP_TABLE (property_browser->details->content_table),
IMAGE_TABLE_Y_SPACING);
+ gtk_container_set_border_width (GTK_CONTAINER (property_browser->details->content_table), 8);
gtk_signal_connect (GTK_OBJECT (property_browser->details->content_table),
"child_pressed",