diff options
author | Johannes Schmid <jhs@gnome.org> | 2009-10-28 10:57:46 +0100 |
---|---|---|
committer | Johannes Schmid <jhs@gnome.org> | 2009-10-28 10:57:46 +0100 |
commit | c229306a18f6453c144397bfbbf2284afa9731eb (patch) | |
tree | fd37ba747b10ec03b968762f01386ab0d7b926c3 /gtk | |
parent | 249be999a6699752980a5e16e45b45c4db18dabf (diff) | |
parent | 4ff709c24b8d4b3e26b3d513fde0676e9c43f897 (diff) | |
download | gtk+-c229306a18f6453c144397bfbbf2284afa9731eb.tar.gz |
Merge branch 'master' into toolpalette
Diffstat (limited to 'gtk')
138 files changed, 11429 insertions, 5659 deletions
diff --git a/gtk/Makefile.am b/gtk/Makefile.am index f34ba852d5..4b1ceded26 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -178,6 +178,7 @@ gtk_public_h_sources = \ gtkcellrendererpixbuf.h \ gtkcellrendererprogress.h \ gtkcellrendererspin.h \ + gtkcellrendererspinner.h\ gtkcellrenderertext.h \ gtkcellrenderertoggle.h \ gtkcellview.h \ @@ -190,7 +191,6 @@ gtk_public_h_sources = \ gtkcombobox.h \ gtkcomboboxentry.h \ gtkcontainer.h \ - gtkcurve.h \ gtkdebug.h \ gtkdialog.h \ gtkdnd.h \ @@ -211,7 +211,6 @@ gtk_public_h_sources = \ gtkfontbutton.h \ gtkfontsel.h \ gtkframe.h \ - gtkgamma.h \ gtkgc.h \ gtkhandlebox.h \ gtkhbbox.h \ @@ -232,7 +231,6 @@ gtk_public_h_sources = \ gtkimmodule.h \ gtkimmulticontext.h \ gtkinfobar.h \ - gtkinputdialog.h \ gtkinvisible.h \ gtkitem.h \ gtklabel.h \ @@ -289,6 +287,7 @@ gtk_public_h_sources = \ gtksizegroup.h \ gtksocket.h \ gtkspinbutton.h \ + gtkspinner.h \ gtkstatusbar.h \ gtkstatusicon.h \ gtkstock.h \ @@ -435,6 +434,7 @@ gtk_base_c_sources = \ gtkcellrendererpixbuf.c \ gtkcellrendererprogress.c \ gtkcellrendererspin.c \ + gtkcellrendererspinner.c\ gtkcellrenderertext.c \ gtkcellrenderertoggle.c \ gtkcellview.c \ @@ -446,7 +446,6 @@ gtk_base_c_sources = \ gtkcombobox.c \ gtkcomboboxentry.c \ gtkcontainer.c \ - gtkcurve.c \ gtkdialog.c \ gtkdrawingarea.c \ gtkeditable.c \ @@ -471,7 +470,6 @@ gtk_base_c_sources = \ gtkfontbutton.c \ gtkfontsel.c \ gtkframe.c \ - gtkgamma.c \ gtkgc.c \ gtkhandlebox.c \ gtkhbbox.c \ @@ -559,6 +557,7 @@ gtk_base_c_sources = \ gtkshow.c \ gtksocket.c \ gtkspinbutton.c \ + gtkspinner.c \ gtkstatusbar.c \ gtkstatusicon.c \ gtkstock.c \ @@ -634,7 +633,10 @@ gtk_public_h_sources += \ gtkclist.h \ gtkcombo.h \ gtkctree.h \ + gtkcurve.h \ gtkfilesel.h \ + gtkgamma.h \ + gtkinputdialog.h \ gtkitemfactory.h \ gtklist.h \ gtklistitem.h \ @@ -650,7 +652,9 @@ gtk_base_c_sources += \ gtkclist.c \ gtkcombo.c \ gtkctree.c \ + gtkcurve.c \ gtkfilesel.c \ + gtkgamma.c \ gtkitemfactory.c \ gtklist.c \ gtklistitem.c \ @@ -915,8 +919,19 @@ if DISABLE_EXPLICIT_DEPS $(SHELL) $(top_srcdir)/sanitize-la.sh $(DESTDIR)$(libdir)/$(gtktargetlib) endif +if USE_QUARTZ +install-mac-key-theme: + $(mkinstalldirs) $(DESTDIR)$(datadir)/themes/Mac/gtk-2.0-key + $(INSTALL_DATA) $(srcdir)/gtkrc.key.mac $(DESTDIR)$(datadir)/themes/Mac/gtk-2.0-key/gtkrc +uninstall-mac-key-theme: + rm -f $(DESTDIR)$(datadir)/themes/Mac/gtk-2.0-key/gtkrc +else +install-mac-key-theme: +uninstall-mac-key-theme: +endif + # Install a RC file for the default GTK+ theme, and key themes -install-data-local: install-ms-lib install-def-file +install-data-local: install-ms-lib install-def-file install-mac-key-theme $(mkinstalldirs) $(DESTDIR)$(datadir)/themes/Raleigh/gtk-2.0 $(INSTALL_DATA) $(srcdir)/gtkrc.default $(DESTDIR)$(datadir)/themes/Raleigh/gtk-2.0/gtkrc $(mkinstalldirs) $(DESTDIR)$(datadir)/themes/Default/gtk-2.0-key @@ -924,7 +939,7 @@ install-data-local: install-ms-lib install-def-file $(mkinstalldirs) $(DESTDIR)$(datadir)/themes/Emacs/gtk-2.0-key $(INSTALL_DATA) $(srcdir)/gtkrc.key.emacs $(DESTDIR)$(datadir)/themes/Emacs/gtk-2.0-key/gtkrc -uninstall-local: uninstall-ms-lib uninstall-def-file +uninstall-local: uninstall-ms-lib uninstall-def-file uninstall-mac-key-theme rm -f $(DESTDIR)$(datadir)/themes/Raleigh/gtk-2.0/gtkrc rm -f $(DESTDIR)$(datadir)/themes/Default/gtk-2.0-key/gtkrc rm -f $(DESTDIR)$(datadir)/themes/Emacs/gtk-2.0-key/gtkrc @@ -951,8 +966,42 @@ LDADDS = \ bin_PROGRAMS = \ gtk-query-immodules-2.0 \ gtk-update-icon-cache + bin_SCRIPTS = gtk-builder-convert +if OS_WIN32 + +# Workaround for UAC silliness: programs with "update" in their name +# are believed to be installers and require elevated privileges to be +# used... Use a manifest file to tell Windows that +# gtk-update-icon-cache.exe doesn't require any special privileges. + +GTK_UPDATE_ICON_CACHE_MANIFEST = gtk-update-icon-cache.exe.manifest + +bin_SCRIPTS += \ + $(GTK_UPDATE_ICON_CACHE_MANIFEST) + +gtk-update-icon-cache.exe.manifest: + (echo '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' ; \ + echo '<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">' ; \ + echo ' <assemblyIdentity version="1.0.0.0"' ; \ + echo ' processorArchitecture="'$(LIB_EXE_MACHINE_FLAG)'"' ; \ + echo ' name="gtk-update-icon-cache.exe"' ; \ + echo ' type="win32"/>' ; \ + echo ' <!-- Identify the application security requirements. -->' ; \ + echo ' <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">' ; \ + echo ' <security>' ; \ + echo ' <requestedPrivileges>' ; \ + echo ' <requestedExecutionLevel' ; \ + echo ' level="asInvoker"' ; \ + echo ' uiAccess="false"/>' ; \ + echo ' </requestedPrivileges>' ; \ + echo ' </security>' ; \ + echo ' </trustInfo>' ; \ + echo '</assembly>' ) >$@ + +endif + gtk_query_immodules_2_0_DEPENDENCIES = $(DEPS) gtk_query_immodules_2_0_LDADD = $(LDADDS) @@ -1271,7 +1320,7 @@ gtk_update_icon_cache_program = \ endif gtkbuiltincache.h: @REBUILD@ stamp-icons - $(MAKE) $(AM_MAKEFLAGS) gtk-update-icon-cache$(EXEEXT) + $(MAKE) $(AM_MAKEFLAGS) gtk-update-icon-cache$(EXEEXT) $(GTK_UPDATE_ICON_CACHE_MANIFEST) $(gtk_update_icon_cache_program) --force --ignore-theme-index \ --source builtin_icons stock-icons > gtkbuiltincache.h.tmp && \ mv gtkbuiltincache.h.tmp gtkbuiltincache.h @@ -1298,6 +1347,7 @@ EXTRA_DIST += \ gtkrc.default \ gtkrc.key.default \ gtkrc.key.emacs \ + gtkrc.key.mac \ makefile.msc \ makefile.msc.in \ makegtkalias.pl \ diff --git a/gtk/compose-parse.py b/gtk/compose-parse.py index dfa94925fd..2aa13569b9 100755 --- a/gtk/compose-parse.py +++ b/gtk/compose-parse.py @@ -425,30 +425,61 @@ except: """ Look if there is a lookaside (supplementary) compose file in the current directory, and if so, open, then merge with upstream Compose file. """ +xorg_compose_sequences_raw = [] +for seq in composefile.readlines(): + xorg_compose_sequences_raw.append(seq) + try: composefile_lookaside = open(FILENAME_COMPOSE_SUPPLEMENTARY, 'r') + for seq in composefile_lookaside.readlines(): + xorg_compose_sequences_raw.append(seq) except IOError, (errno, strerror): - if not opt_quiet: + if opt_verbose: print "I/O error(%s): %s" % (errno, strerror) print "Did not find lookaside compose file. Continuing..." except: print "Unexpected error: ", sys.exc_info()[0] sys.exit(-1) -xorg_compose_sequences_raw = [] -for seq in composefile.readlines(): - xorg_compose_sequences_raw.append(seq) -for seq in composefile_lookaside.readlines(): - xorg_compose_sequences_raw.append(seq) - """ Parse the compose file in xorg_compose_sequences""" xorg_compose_sequences = [] xorg_compose_sequences_algorithmic = [] linenum_compose = 0 +comment_nest_depth = 0 for line in xorg_compose_sequences_raw: linenum_compose += 1 line = line.strip() - if line is "" or match("^XCOMM", line) or match("^#", line): + if match("^XCOMM", line) or match("^#", line): + continue + + line = sub(r"\/\*([^\*]*|[\*][^/])\*\/", "", line) + + comment_start = line.find("/*") + + if comment_start >= 0: + if comment_nest_depth == 0: + line = line[:comment_start] + else: + line = "" + + comment_nest_depth += 1 + else: + comment_end = line.find("*/") + + if comment_end >= 0: + comment_nest_depth -= 1 + + if comment_nest_depth < 0: + print "Invalid comment %(linenum_compose)d in %(filename)s: \ + Closing '*/' without opening '/*'" % { "linenum_compose": linenum_compose, "filename": filename_compose } + exit(-1) + + if comment_nest_depth > 0: + line = "" + else: + line = line[comment_end + 2:] + + if line is "": continue #line = line[:-1] @@ -59,6 +59,7 @@ #include <gtk/gtkcellrendererpixbuf.h> #include <gtk/gtkcellrendererprogress.h> #include <gtk/gtkcellrendererspin.h> +#include <gtk/gtkcellrendererspinner.h> #include <gtk/gtkcellrenderertext.h> #include <gtk/gtkcellrenderertoggle.h> #include <gtk/gtkcellview.h> @@ -71,7 +72,6 @@ #include <gtk/gtkcombobox.h> #include <gtk/gtkcomboboxentry.h> #include <gtk/gtkcontainer.h> -#include <gtk/gtkcurve.h> #include <gtk/gtkdebug.h> #include <gtk/gtkdialog.h> #include <gtk/gtkdnd.h> @@ -92,7 +92,6 @@ #include <gtk/gtkfontbutton.h> #include <gtk/gtkfontsel.h> #include <gtk/gtkframe.h> -#include <gtk/gtkgamma.h> #include <gtk/gtkgc.h> #include <gtk/gtkhandlebox.h> #include <gtk/gtkhbbox.h> @@ -112,7 +111,6 @@ #include <gtk/gtkimcontextsimple.h> #include <gtk/gtkimmulticontext.h> #include <gtk/gtkinfobar.h> -#include <gtk/gtkinputdialog.h> #include <gtk/gtkinvisible.h> #include <gtk/gtkitem.h> #include <gtk/gtklabel.h> @@ -168,6 +166,7 @@ #include <gtk/gtksizegroup.h> #include <gtk/gtksocket.h> #include <gtk/gtkspinbutton.h> +#include <gtk/gtkspinner.h> #include <gtk/gtkstatusbar.h> #include <gtk/gtkstatusicon.h> #include <gtk/gtkstock.h> @@ -226,7 +225,10 @@ #include <gtk/gtkclist.h> #include <gtk/gtkcombo.h> #include <gtk/gtkctree.h> +#include <gtk/gtkcurve.h> #include <gtk/gtkfilesel.h> +#include <gtk/gtkgamma.h> +#include <gtk/gtkinputdialog.h> #include <gtk/gtkitemfactory.h> #include <gtk/gtklist.h> #include <gtk/gtklistitem.h> diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols index a53fb5abbf..214832753c 100644 --- a/gtk/gtk.symbols +++ b/gtk/gtk.symbols @@ -541,7 +541,9 @@ gtk_button_box_set_layout #if IN_HEADER(__GTK_BUTTON_H__) #if IN_FILE(__GTK_BUTTON_C__) gtk_button_clicked +#ifndef GTK_DISABLE_DEPRECATED gtk_button_enter +#endif gtk_button_get_alignment gtk_button_get_focus_on_click gtk_button_get_image @@ -551,13 +553,17 @@ gtk_button_get_relief gtk_button_get_type G_GNUC_CONST gtk_button_get_use_stock gtk_button_get_use_underline +#ifndef GTK_DISABLE_DEPRECATED gtk_button_leave +#endif gtk_button_new gtk_button_new_from_stock gtk_button_new_with_label gtk_button_new_with_mnemonic +#ifndef GTK_DISABLE_DEPRECATED gtk_button_pressed gtk_button_released +#endif gtk_button_set_alignment gtk_button_set_focus_on_click gtk_button_set_image @@ -624,11 +630,19 @@ gtk_cell_renderer_activate #ifndef GTK_DISABLE_DEPRECATED gtk_cell_renderer_editing_canceled #endif +gtk_cell_renderer_get_alignment gtk_cell_renderer_get_fixed_size +gtk_cell_renderer_get_padding +gtk_cell_renderer_get_sensitive gtk_cell_renderer_get_size gtk_cell_renderer_get_type G_GNUC_CONST +gtk_cell_renderer_get_visible gtk_cell_renderer_render +gtk_cell_renderer_set_alignment gtk_cell_renderer_set_fixed_size +gtk_cell_renderer_set_padding +gtk_cell_renderer_set_sensitive +gtk_cell_renderer_set_visible gtk_cell_renderer_start_editing gtk_cell_renderer_stop_editing #endif @@ -662,6 +676,13 @@ gtk_cell_renderer_spin_new #endif #endif +#if IN_HEADER(__GTK_CELL_RENDERER_SPINNER_H__) +#if IN_FILE(__GTK_CELL_RENDERER_SPINNER_C__) +gtk_cell_renderer_spinner_get_type G_GNUC_CONST +gtk_cell_renderer_spinner_new +#endif +#endif + #if IN_HEADER(__GTK_CELL_RENDERER_PROGRESS_H__) #if IN_FILE(__GTK_CELL_RENDERER_PROGRESS_C__) gtk_cell_renderer_progress_get_type G_GNUC_CONST @@ -679,10 +700,12 @@ gtk_cell_renderer_text_set_fixed_height_from_font #if IN_HEADER(__GTK_CELL_RENDERER_TOGGLE_H__) #if IN_FILE(__GTK_CELL_RENDERER_TOGGLE_C__) +gtk_cell_renderer_toggle_get_activatable gtk_cell_renderer_toggle_get_active gtk_cell_renderer_toggle_get_radio gtk_cell_renderer_toggle_get_type G_GNUC_CONST gtk_cell_renderer_toggle_new +gtk_cell_renderer_toggle_set_activatable gtk_cell_renderer_toggle_set_active gtk_cell_renderer_toggle_set_radio #endif @@ -1088,6 +1111,7 @@ gtk_ctree_unselect_recursive #if IN_HEADER(__GTK_CURVE_H__) #if IN_FILE(__GTK_CURVE_C__) +#ifndef GTK_DISABLE_DEPRECATED gtk_curve_get_type G_GNUC_CONST gtk_curve_get_vector gtk_curve_new @@ -1098,6 +1122,7 @@ gtk_curve_set_range gtk_curve_set_vector #endif #endif +#endif #if IN_HEADER(__GTK_WINDOW_DECORATE_H__) #if IN_FILE(__GTK_WINDOW_DECORATE_C__) @@ -1116,6 +1141,7 @@ gtk_dialog_add_buttons G_GNUC_NULL_TERMINATED gtk_dialog_get_action_area gtk_dialog_get_content_area gtk_dialog_get_has_separator +gtk_dialog_get_widget_for_response gtk_dialog_get_response_for_widget gtk_dialog_get_type G_GNUC_CONST gtk_dialog_new @@ -1266,6 +1292,7 @@ gtk_paint_resize_grip gtk_paint_shadow gtk_paint_shadow_gap gtk_paint_slider +gtk_paint_spinner gtk_paint_tab gtk_paint_vline gtk_border_new G_GNUC_MALLOC @@ -1521,6 +1548,8 @@ gtk_file_chooser_get_select_multiple gtk_file_chooser_get_show_hidden gtk_file_chooser_set_do_overwrite_confirmation gtk_file_chooser_get_do_overwrite_confirmation +gtk_file_chooser_set_create_folders +gtk_file_chooser_get_create_folders gtk_file_chooser_get_type G_GNUC_CONST gtk_file_chooser_get_uri gtk_file_chooser_get_uris @@ -1752,10 +1781,12 @@ gtk_frame_set_shadow_type #if IN_HEADER(__GTK_GAMMA_CURVE_H__) #if IN_FILE(__GTK_GAMMA_CURVE_C__) +#ifndef GTK_DISABLE_DEPRECATED gtk_gamma_curve_get_type G_GNUC_CONST gtk_gamma_curve_new #endif #endif +#endif #if IN_HEADER(__GTK_GC_H__) #if IN_FILE(__GTK_GC_C__) @@ -1976,6 +2007,7 @@ gtk_icon_theme_set_search_path_utf8 gtk_icon_view_get_column_spacing gtk_icon_view_get_columns gtk_icon_view_get_cursor +gtk_icon_view_get_item_padding gtk_icon_view_get_item_width gtk_icon_view_get_margin gtk_icon_view_get_markup_column @@ -2003,6 +2035,7 @@ gtk_icon_view_select_path gtk_icon_view_set_column_spacing gtk_icon_view_set_columns gtk_icon_view_set_cursor +gtk_icon_view_set_item_padding gtk_icon_view_set_item_width gtk_icon_view_set_margin gtk_icon_view_set_markup_column @@ -2405,10 +2438,12 @@ gtk_main_quit #if IN_HEADER(__GTK_INPUTDIALOG_H__) #if IN_FILE(__GTK_INPUTDIALOG_C__) +#ifndef GTK_DISABLE_DEPRECATED gtk_input_dialog_get_type G_GNUC_CONST gtk_input_dialog_new #endif #endif +#endif #if IN_HEADER(__gtk_marshal_MARSHAL_H__) #if IN_FILE(__gtk_marshal_MARSHAL_C__) @@ -2605,7 +2640,9 @@ gtk_notebook_popup_enable gtk_notebook_prepend_page gtk_notebook_prepend_page_menu gtk_notebook_prev_page +#ifndef GTK_DISABLE_DEPRECATED gtk_notebook_query_tab_label_packing +#endif gtk_notebook_remove_page gtk_notebook_reorder_child gtk_notebook_set_current_page @@ -2621,7 +2658,9 @@ gtk_notebook_set_scrollable gtk_notebook_set_show_border gtk_notebook_set_show_tabs gtk_notebook_set_tab_label +#ifndef GTK_DISABLE_DEPRECATED gtk_notebook_set_tab_label_packing +#endif gtk_notebook_set_tab_label_text gtk_notebook_set_tab_pos gtk_notebook_set_window_creation_hook @@ -2943,6 +2982,8 @@ gtk_printer_option_clear_has_conflict gtk_printer_option_set_boolean gtk_printer_option_allocate_choices gtk_printer_option_choices_from_array +gtk_printer_option_set_activates_default +gtk_printer_option_get_activates_default #endif #endif #endif @@ -3004,6 +3045,7 @@ gtk_print_operation_set_allow_async gtk_print_operation_set_default_page_setup gtk_print_operation_get_default_page_setup gtk_print_operation_set_print_settings +gtk_print_operation_get_n_pages_to_print gtk_print_operation_get_print_settings gtk_print_operation_set_job_name gtk_print_operation_set_n_pages @@ -3277,6 +3319,7 @@ gtk_radio_tool_button_set_group #if IN_FILE(__GTK_RANGE_C__) gtk_range_get_adjustment gtk_range_get_fill_level +gtk_range_get_flippable gtk_range_get_inverted gtk_range_get_lower_stepper_sensitivity gtk_range_get_restrict_to_fill_level @@ -3287,6 +3330,7 @@ gtk_range_get_upper_stepper_sensitivity gtk_range_get_value gtk_range_set_adjustment gtk_range_set_fill_level +gtk_range_set_flippable gtk_range_set_increments gtk_range_set_inverted gtk_range_set_lower_stepper_sensitivity @@ -3782,6 +3826,15 @@ gtk_spin_button_update #endif #endif +#if IN_HEADER(__GTK_SPINNER_H__) +#if IN_FILE(__GTK_SPINNER_C__) +gtk_spinner_get_type G_GNUC_CONST +gtk_spinner_new +gtk_spinner_start +gtk_spinner_stop +#endif +#endif + #if IN_HEADER(__GTK_STATUSBAR_H__) #if IN_FILE(__GTK_STATUSBAR_C__) gtk_statusbar_get_context_id @@ -4396,6 +4449,7 @@ gtk_tooltip_set_custom gtk_tooltip_set_icon gtk_tooltip_set_icon_from_stock gtk_tooltip_set_icon_from_icon_name +gtk_tooltip_set_icon_from_gicon gtk_tooltip_set_markup gtk_tooltip_set_text gtk_tooltip_set_tip_area @@ -4877,6 +4931,7 @@ gtk_viewport_get_hadjustment gtk_viewport_get_shadow_type gtk_viewport_get_type G_GNUC_CONST gtk_viewport_get_vadjustment +gtk_viewport_get_bin_window gtk_viewport_new gtk_viewport_set_hadjustment gtk_viewport_set_shadow_type @@ -4954,7 +5009,9 @@ gtk_widget_error_bell gtk_widget_event gtk_widget_freeze_child_notify gtk_widget_get_accessible +gtk_widget_get_allocation gtk_widget_get_ancestor +gtk_widget_get_app_paintable gtk_widget_get_child_requisition gtk_widget_get_child_visible gtk_widget_get_clipboard @@ -4966,6 +5023,7 @@ gtk_widget_get_default_style gtk_widget_get_default_visual gtk_widget_get_direction gtk_widget_get_display +gtk_widget_get_double_buffered gtk_widget_get_events gtk_widget_get_extension_events gtk_widget_get_has_tooltip @@ -4976,20 +5034,27 @@ gtk_widget_get_pango_context gtk_widget_get_parent gtk_widget_get_parent_window gtk_widget_get_pointer +gtk_widget_get_receives_default gtk_widget_get_root_window gtk_widget_get_screen +gtk_widget_get_sensitive gtk_widget_get_settings gtk_widget_get_size_request gtk_widget_get_snapshot +gtk_widget_get_state gtk_widget_get_style gtk_widget_get_tooltip_markup gtk_widget_get_tooltip_text gtk_widget_get_tooltip_window gtk_widget_get_toplevel gtk_widget_get_type G_GNUC_CONST +gtk_widget_get_visible gtk_widget_get_visual gtk_widget_grab_default gtk_widget_grab_focus +gtk_widget_has_default +gtk_widget_has_focus +gtk_widget_has_grab gtk_widget_has_screen gtk_widget_hide gtk_widget_hide_all @@ -4997,6 +5062,9 @@ gtk_widget_hide_on_delete gtk_widget_intersect gtk_widget_is_ancestor gtk_widget_is_focus +gtk_widget_is_sensitive +gtk_widget_is_toplevel +gtk_widget_is_drawable gtk_widget_keynav_failed gtk_widget_list_accel_closures gtk_widget_list_mnemonic_labels @@ -5039,6 +5107,7 @@ gtk_widget_reset_rc_styles gtk_widget_reset_shapes gtk_widget_send_expose gtk_widget_set_accel_path +gtk_widget_set_allocation gtk_widget_set_app_paintable gtk_widget_set_child_visible gtk_widget_set_colormap @@ -5054,6 +5123,7 @@ gtk_widget_set_name gtk_widget_set_no_show_all gtk_widget_set_parent gtk_widget_set_parent_window +gtk_widget_set_receives_default gtk_widget_set_redraw_on_allocate gtk_widget_set_scroll_adjustments gtk_widget_set_sensitive @@ -5063,6 +5133,8 @@ gtk_widget_set_style gtk_widget_set_tooltip_markup gtk_widget_set_tooltip_text gtk_widget_set_tooltip_window +gtk_widget_set_visible +gtk_widget_set_window gtk_widget_shape_combine_mask gtk_widget_input_shape_combine_mask gtk_widget_show @@ -5080,6 +5152,12 @@ gtk_widget_unmap gtk_widget_unparent gtk_widget_unrealize gtk_widget_get_window +gtk_widget_set_has_window +gtk_widget_get_has_window +gtk_widget_set_can_default +gtk_widget_get_can_default +gtk_widget_set_can_focus +gtk_widget_get_can_focus #endif #endif diff --git a/gtk/gtkaboutdialog.c b/gtk/gtkaboutdialog.c index 91d30fe344..af7f2cd734 100644 --- a/gtk/gtkaboutdialog.c +++ b/gtk/gtkaboutdialog.c @@ -57,6 +57,67 @@ #include "gtkalias.h" + +/** + * SECTION:gtkaboutdialog + * @Short_description: Display information about an application + * @Title: GtkAboutDialog + * @See_also:#GTK_STOCK_ABOUT + * + * The #GtkAboutDialog offers a simple way to display information about + * a program like its logo, name, copyright, website and license. It is + * also possible to give credits to the authors, documenters, translators + * and artists who have worked on the program. An about dialog is typically + * opened when the user selects the <literal>About</literal> option from + * the <literal>Help</literal> menu. All parts of the dialog are optional. + * + * About dialog often contain links and email addresses. #GtkAboutDialog + * supports this by offering global hooks, which are called when the user + * clicks on a link or email address, see gtk_about_dialog_set_email_hook() + * and gtk_about_dialog_set_url_hook(). Email addresses in the + * authors, documenters and artists properties are recognized by looking for + * <literal><user@<!-- -->host></literal>, URLs are + * recognized by looking for <literal>http://url</literal>, with + * <literal>url</literal> extending to the next space, tab or line break. + * + * <para id="gtk-about-dialog-hook-setup"> + * Since 2.18 #GtkAboutDialog provides default website and email hooks that + * use gtk_show_uri(). + * </para> + * + * If you want provide your own hooks overriding the default ones, it is + * important to do so before setting the website and email URL properties, + * like this: + * <informalexample><programlisting> + * gtk_about_dialog_set_url_hook (GTK_ABOUT_DIALOG (dialog), launch_url, NULL, NULL); + * gtk_about_dialog_set_website (GTK_ABOUT_DIALOG (dialog), app_url); + * </programlisting></informalexample> + * To disable the default hooks, you can pass %NULL as the hook func. Then, + * the #GtkAboutDialog widget will not display the website or the + * email addresses as clickable. + * + * To make constructing a #GtkAboutDialog as convenient as possible, you can + * use the function gtk_show_about_dialog() which constructs and shows a dialog + * and keeps it around so that it can be shown again. + * + * Note that GTK+ sets a default title of <literal>_("About %s")</literal> + * on the dialog window (where %s is replaced by the name of the + * application, but in order to ensure proper translation of the title, + * applications should set the title property explicitly when constructing + * a #GtkAboutDialog, as shown in the following example: + * <informalexample><programlisting> + * gtk_show_about_dialog (NULL, + * "program-name", "ExampleCode", + * "logo", example_logo, + * "title" _("About ExampleCode"), + * NULL); + * </programlisting></informalexample> + * Note that prior to GTK+ 2.12, the #GtkAboutDialog:program-name property + * was called "name". This was changed to avoid the conflict with the + * #GtkWidget:name property. + */ + + static GdkColor default_link_color = { 0, 0, 0, 0xeeee }; static GdkColor default_visited_link_color = { 0, 0x5555, 0x1a1a, 0x8b8b }; @@ -131,8 +192,6 @@ static void gtk_about_dialog_set_property (GObject static void gtk_about_dialog_show (GtkWidget *widge); static void update_name_version (GtkAboutDialog *about); static GtkIconSet * icon_set_new_from_pixbufs (GList *pixbufs); -static void activate_url (GtkWidget *widget, - gpointer data); static void follow_if_link (GtkAboutDialog *about, GtkTextView *text_view, GtkTextIter *iter); @@ -1695,30 +1754,6 @@ gtk_about_dialog_set_logo_icon_name (GtkAboutDialog *about, } static void -activate_url (GtkWidget *widget, - gpointer data) -{ - GtkAboutDialog *about = GTK_ABOUT_DIALOG (data); - const gchar *url = gtk_link_button_get_uri (GTK_LINK_BUTTON (widget)); - GtkAboutDialogActivateLinkFunc url_hook; - gpointer url_hook_data; - - if (activate_url_hook_set) - { - url_hook = activate_url_hook; - url_hook_data = activate_url_hook_data; - } - else - { - url_hook = default_url_hook; - url_hook_data = NULL; - } - - if (url_hook) - url_hook (about, url, url_hook_data); -} - -static void follow_if_link (GtkAboutDialog *about, GtkTextView *text_view, GtkTextIter *iter) diff --git a/gtk/gtkaboutdialog.h b/gtk/gtkaboutdialog.h index 02f2fbec03..efb50b1cf0 100644 --- a/gtk/gtkaboutdialog.h +++ b/gtk/gtkaboutdialog.h @@ -43,6 +43,12 @@ G_BEGIN_DECLS typedef struct _GtkAboutDialog GtkAboutDialog; typedef struct _GtkAboutDialogClass GtkAboutDialogClass; +/** + * GtkAboutDialog: + * + * The <structname>GtkAboutDialog</structname> struct contains + * only private fields and should not be directly accessed. + */ struct _GtkAboutDialog { GtkDialog parent_instance; @@ -118,6 +124,17 @@ G_CONST_RETURN gchar *gtk_about_dialog_get_logo_icon_name (GtkAboutDialog void gtk_about_dialog_set_logo_icon_name (GtkAboutDialog *about, const gchar *icon_name); +/** + * GtkAboutDialogActivateLinkFunc: + * @about: the #GtkAboutDialog in which the link was activated + * @link_: the URL or email address to which the activated link points + * @data: user data that was passed when the function was registered + * with gtk_about_dialog_set_email_hook() or + * gtk_about_dialog_set_url_hook() + * + * The type of a function which is called when a URL or email + * link is activated. + */ typedef void (* GtkAboutDialogActivateLinkFunc) (GtkAboutDialog *about, const gchar *link_, gpointer data); diff --git a/gtk/gtkaccelgroup.c b/gtk/gtkaccelgroup.c index b26fd63a05..29e34eb3f3 100644 --- a/gtk/gtkaccelgroup.c +++ b/gtk/gtkaccelgroup.c @@ -37,6 +37,31 @@ #include "gtkmarshalers.h" #include "gtkalias.h" +/** + * SECTION:gtkaccelgroup + * @Short_description: Groups of global keyboard accelerators for an entire GtkWindow + * @Title: Accelerator Groups + * @See_also:gtk_window_add_accel_group(), gtk_accel_map_change_entry(), + * gtk_item_factory_new(), gtk_label_new_with_mnemonic() + * + * A #GtkAccelGroup represents a group of keyboard accelerators, + * typically attached to a toplevel #GtkWindow (with + * gtk_window_add_accel_group()). Usually you won't need to create a + * #GtkAccelGroup directly; instead, when using #GtkItemFactory, GTK+ + * automatically sets up the accelerators for your menus in the item + * factory's #GtkAccelGroup. + * + * + * Note that <firstterm>accelerators</firstterm> are different from + * <firstterm>mnemonics</firstterm>. Accelerators are shortcuts for + * activating a menu item; they appear alongside the menu item they're a + * shortcut for. For example "Ctrl+Q" might appear alongside the "Quit" + * menu item. Mnemonics are shortcuts for GUI elements such as text + * entries or buttons; they appear as underlined characters. See + * gtk_label_new_with_mnemonic(). Menu items can have both accelerators + * and mnemonics, of course. + */ + /* --- prototypes --- */ static void gtk_accel_group_finalize (GObject *object); @@ -665,11 +690,14 @@ gtk_accel_group_connect_by_path (GtkAccelGroup *accel_group, /** * gtk_accel_group_disconnect: * @accel_group: the accelerator group to remove an accelerator from - * @closure: the closure to remove from this accelerator group + * @closure: the closure to remove from this accelerator group, or %NULL + * to remove all closures * @returns: %TRUE if the closure was found and got disconnected * * Removes an accelerator previously installed through * gtk_accel_group_connect(). + * + * Since 2.20 @closure can be %NULL. */ gboolean gtk_accel_group_disconnect (GtkAccelGroup *accel_group, @@ -680,7 +708,7 @@ gtk_accel_group_disconnect (GtkAccelGroup *accel_group, g_return_val_if_fail (GTK_IS_ACCEL_GROUP (accel_group), FALSE); for (i = 0; i < accel_group->n_accels; i++) - if (accel_group->priv_accels[i].closure == closure) + if (accel_group->priv_accels[i].closure == closure || !closure) { g_object_ref (accel_group); quick_accel_remove (accel_group, i); diff --git a/gtk/gtkaccelgroup.h b/gtk/gtkaccelgroup.h index 80780dba5a..b1e874491e 100644 --- a/gtk/gtkaccelgroup.h +++ b/gtk/gtkaccelgroup.h @@ -66,10 +66,23 @@ typedef gboolean (*GtkAccelGroupActivate) (GtkAccelGroup *accel_group, guint keyval, GdkModifierType modifier); +/** + * GtkAccelGroupFindFunc: + * @key: + * @closure: + * @data: + * + * Since: 2.2 + */ typedef gboolean (*GtkAccelGroupFindFunc) (GtkAccelKey *key, GClosure *closure, gpointer data); +/** + * GtkAccelGroup: + * + * An object representing and maintaining a group of accelerators. + */ struct _GtkAccelGroup { GObject parent; @@ -180,7 +193,20 @@ struct _GtkAccelGroupEntry #ifndef GTK_DISABLE_DEPRECATED +/** + * gtk_accel_group_ref: + * + * Deprecated equivalent of g_object_ref(). + * + * Returns: the accel group that was passed in + */ #define gtk_accel_group_ref g_object_ref + +/** + * gtk_accel_group_unref: + * + * Deprecated equivalent of g_object_unref(). + */ #define gtk_accel_group_unref g_object_unref #endif /* GTK_DISABLE_DEPRECATED */ diff --git a/gtk/gtkaccessible.c b/gtk/gtkaccessible.c index 1a781e7a58..e3d6ea3c14 100644 --- a/gtk/gtkaccessible.c +++ b/gtk/gtkaccessible.c @@ -25,6 +25,13 @@ #include "gtkaccessible.h" #include "gtkalias.h" +/** + * SECTION:gtkaccessible + * @Short_description: Accessibility support for widgets + * @Title: GtkAccessible + */ + + static void gtk_accessible_real_connect_widget_destroyed (GtkAccessible *accessible); G_DEFINE_TYPE (GtkAccessible, gtk_accessible, ATK_TYPE_OBJECT) diff --git a/gtk/gtkactivatable.c b/gtk/gtkactivatable.c index 65c166b87e..4f92e524ea 100644 --- a/gtk/gtkactivatable.c +++ b/gtk/gtkactivatable.c @@ -20,6 +20,7 @@ /** * SECTION:gtkactivatable * @Short_Description: An interface for activatable widgets + * @Title: GtkActivatable * * Activatable widgets can be connected to a #GtkAction and reflects * the state of its action. A #GtkActivatable can also provide feedback diff --git a/gtk/gtkassistant.c b/gtk/gtkassistant.c index b7ed75641d..a2e2f7ae3a 100644 --- a/gtk/gtkassistant.c +++ b/gtk/gtkassistant.c @@ -483,6 +483,7 @@ set_assistant_buttons_state (GtkAssistant *assistant) case GTK_ASSISTANT_PAGE_INTRO: gtk_widget_set_sensitive (assistant->cancel, TRUE); gtk_widget_set_sensitive (assistant->forward, priv->current_page->complete); + gtk_widget_grab_default (assistant->forward); gtk_widget_show (assistant->cancel); gtk_widget_show (assistant->forward); gtk_widget_hide (assistant->back); @@ -494,6 +495,7 @@ set_assistant_buttons_state (GtkAssistant *assistant) gtk_widget_set_sensitive (assistant->cancel, TRUE); gtk_widget_set_sensitive (assistant->back, TRUE); gtk_widget_set_sensitive (assistant->apply, priv->current_page->complete); + gtk_widget_grab_default (assistant->apply); gtk_widget_show (assistant->cancel); gtk_widget_show (assistant->back); gtk_widget_show (assistant->apply); @@ -505,6 +507,7 @@ set_assistant_buttons_state (GtkAssistant *assistant) gtk_widget_set_sensitive (assistant->cancel, TRUE); gtk_widget_set_sensitive (assistant->back, TRUE); gtk_widget_set_sensitive (assistant->forward, priv->current_page->complete); + gtk_widget_grab_default (assistant->forward); gtk_widget_show (assistant->cancel); gtk_widget_show (assistant->back); gtk_widget_show (assistant->forward); @@ -514,6 +517,7 @@ set_assistant_buttons_state (GtkAssistant *assistant) break; case GTK_ASSISTANT_PAGE_SUMMARY: gtk_widget_set_sensitive (assistant->close, TRUE); + gtk_widget_grab_default (assistant->close); gtk_widget_show (assistant->close); gtk_widget_hide (assistant->cancel); gtk_widget_hide (assistant->back); @@ -525,6 +529,7 @@ set_assistant_buttons_state (GtkAssistant *assistant) gtk_widget_set_sensitive (assistant->cancel, priv->current_page->complete); gtk_widget_set_sensitive (assistant->back, priv->current_page->complete); gtk_widget_set_sensitive (assistant->forward, priv->current_page->complete); + gtk_widget_grab_default (assistant->forward); gtk_widget_show (assistant->cancel); gtk_widget_show (assistant->back); gtk_widget_show (assistant->forward); @@ -640,10 +645,10 @@ on_assistant_apply (GtkWidget *widget, { gboolean success; - success = compute_next_step (assistant); - g_signal_emit (assistant, signals [APPLY], 0); + success = compute_next_step (assistant); + /* if the assistant hasn't switched to another page, just emit * the CLOSE signal, it't the last page in the assistant flow */ @@ -752,6 +757,9 @@ gtk_assistant_init (GtkAssistant *assistant) assistant->back = gtk_button_new_from_stock (GTK_STOCK_GO_BACK); assistant->cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL); assistant->last = gtk_button_new_from_stock (GTK_STOCK_GOTO_LAST); + GTK_WIDGET_SET_FLAGS (assistant->close, GTK_CAN_DEFAULT); + GTK_WIDGET_SET_FLAGS (assistant->apply, GTK_CAN_DEFAULT); + GTK_WIDGET_SET_FLAGS (assistant->forward, GTK_CAN_DEFAULT); priv->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); gtk_size_group_add_widget (priv->size_group, assistant->close); @@ -1222,6 +1230,7 @@ gtk_assistant_map (GtkWidget *widget) GtkAssistant *assistant = GTK_ASSISTANT (widget); GtkAssistantPrivate *priv = assistant->priv; GList *page_node; + GtkAssistantPage *page; GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED); @@ -1233,7 +1242,8 @@ gtk_assistant_map (GtkWidget *widget) gtk_widget_map (priv->sidebar_image); /* if there's no default page, pick the first one */ - if (!priv->current_page && priv->pages) + page = NULL; + if (!priv->current_page) { page_node = priv->pages; @@ -1241,22 +1251,13 @@ gtk_assistant_map (GtkWidget *widget) page_node = page_node->next; if (page_node) - priv->current_page = page_node->data; + page = page_node->data; } - if (priv->current_page && - GTK_WIDGET_VISIBLE (priv->current_page->page) && - !GTK_WIDGET_MAPPED (priv->current_page->page)) - { - set_assistant_buttons_state ((GtkAssistant *) widget); - set_assistant_header_image ((GtkAssistant*) widget); - set_assistant_sidebar_image ((GtkAssistant*) widget); - - g_signal_emit (widget, signals [PREPARE], 0, priv->current_page->page); - gtk_widget_set_child_visible (priv->current_page->page, TRUE); - gtk_widget_map (priv->current_page->page); - gtk_widget_map (priv->current_page->title); - } + if (page && + GTK_WIDGET_VISIBLE (page->page) && + !GTK_WIDGET_MAPPED (page->page)) + set_current_page (assistant, page); GTK_WIDGET_CLASS (gtk_assistant_parent_class)->map (widget); } diff --git a/gtk/gtkbuilderparser.c b/gtk/gtkbuilderparser.c index f6f12a5bf6..b4f6855136 100644 --- a/gtk/gtkbuilderparser.c +++ b/gtk/gtkbuilderparser.c @@ -380,7 +380,12 @@ parse_object (GMarkupParseContext *context, data->inside_requested_object = TRUE; } else - return; + { + g_free (object_class); + g_free (object_id); + g_free (constructor); + return; + } } object_info = g_slice_new0 (ObjectInfo); @@ -404,7 +409,8 @@ parse_object (GMarkupParseContext *context, return; } - g_hash_table_insert (data->object_ids, object_id, GINT_TO_POINTER (line)); + + g_hash_table_insert (data->object_ids, g_strdup (object_id), GINT_TO_POINTER (line)); } static void @@ -1128,7 +1134,8 @@ _gtk_builder_parser_parse_buffer (GtkBuilder *builder, data->builder = builder; data->filename = filename; data->domain = g_strdup (domain); - data->object_ids = g_hash_table_new (g_str_hash, g_str_equal); + data->object_ids = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify)g_free, NULL); data->requested_objects = NULL; if (requested_objs) diff --git a/gtk/gtkbutton.h b/gtk/gtkbutton.h index 1243e2abb5..7a67ddcf91 100644 --- a/gtk/gtkbutton.h +++ b/gtk/gtkbutton.h @@ -93,11 +93,15 @@ GtkWidget* gtk_button_new (void); GtkWidget* gtk_button_new_with_label (const gchar *label); GtkWidget* gtk_button_new_from_stock (const gchar *stock_id); GtkWidget* gtk_button_new_with_mnemonic (const gchar *label); +#ifndef GTK_DISABLE_DEPRECATED void gtk_button_pressed (GtkButton *button); void gtk_button_released (GtkButton *button); +#endif void gtk_button_clicked (GtkButton *button); +#ifndef GTK_DISABLE_DEPRECATED void gtk_button_enter (GtkButton *button); void gtk_button_leave (GtkButton *button); +#endif void gtk_button_set_relief (GtkButton *button, GtkReliefStyle newstyle); diff --git a/gtk/gtkcelleditable.c b/gtk/gtkcelleditable.c index 933622410e..38457cae9f 100644 --- a/gtk/gtkcelleditable.c +++ b/gtk/gtkcelleditable.c @@ -21,10 +21,11 @@ #include "config.h" #include "gtkcelleditable.h" #include "gtkmarshalers.h" +#include "gtkprivate.h" #include "gtkintl.h" #include "gtkalias.h" -static void gtk_cell_editable_base_init (gpointer g_class); +static void gtk_cell_editable_base_init (GtkCellEditableIface *iface); GType gtk_cell_editable_get_type (void) @@ -35,12 +36,12 @@ gtk_cell_editable_get_type (void) { const GTypeInfo cell_editable_info = { - sizeof (GtkCellEditableIface), /* class_size */ - gtk_cell_editable_base_init, /* base_init */ - NULL, /* base_finalize */ + sizeof (GtkCellEditableIface), /* class_size */ + (GBaseInitFunc) gtk_cell_editable_base_init, /* base_init */ + NULL, /* base_finalize */ NULL, - NULL, /* class_finalize */ - NULL, /* class_data */ + NULL, /* class_finalize */ + NULL, /* class_data */ 0, 0, NULL @@ -57,13 +58,27 @@ gtk_cell_editable_get_type (void) } static void -gtk_cell_editable_base_init (gpointer g_class) +gtk_cell_editable_base_init (GtkCellEditableIface *iface) { static gboolean initialized = FALSE; if (! initialized) { /** + * GtkCellEditable:editing-canceled: + * + * Indicates whether editing on the cell has been canceled. + * + * Since: 2.20 + **/ + g_object_interface_install_property (iface, + g_param_spec_boolean ("editing-canceled", + P_("Editing Canceled"), + P_("Indicates that editing has been canceled"), + FALSE, + GTK_PARAM_READABLE)); + + /** * GtkCellEditable::editing-done: * @cell_editable: the object on which the signal was emitted * diff --git a/gtk/gtkcellrenderer.c b/gtk/gtkcellrenderer.c index c41a7f857b..e4af9a1303 100644 --- a/gtk/gtkcellrenderer.c +++ b/gtk/gtkcellrenderer.c @@ -684,7 +684,7 @@ gtk_cell_renderer_start_editing (GtkCellRenderer *cell, * @cell: A #GtkCellRenderer * @width: the width of the cell renderer, or -1 * @height: the height of the cell renderer, or -1 - * + * * Sets the renderer size to be explicit, independent of the properties set. **/ void @@ -718,9 +718,9 @@ gtk_cell_renderer_set_fixed_size (GtkCellRenderer *cell, /** * gtk_cell_renderer_get_fixed_size: * @cell: A #GtkCellRenderer - * @width: location to fill in with the fixed width of the widget, or %NULL - * @height: location to fill in with the fixed height of the widget, or %NULL - * + * @width: location to fill in with the fixed width of the cell, or %NULL + * @height: location to fill in with the fixed height of the cell, or %NULL + * * Fills in @width and @height with the appropriate size of @cell. **/ void @@ -731,9 +731,212 @@ gtk_cell_renderer_get_fixed_size (GtkCellRenderer *cell, g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); if (width) - (* width) = cell->width; + *width = cell->width; if (height) - (* height) = cell->height; + *height = cell->height; +} + +/** + * gtk_cell_renderer_set_alignment: + * @cell: A #GtkCellRenderer + * @xalign: the x alignment of the cell renderer + * @yalign: the y alignment of the cell renderer + * + * Sets the renderer's alignment within its available space. + * + * Since: 2.18 + **/ +void +gtk_cell_renderer_set_alignment (GtkCellRenderer *cell, + gfloat xalign, + gfloat yalign) +{ + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + g_return_if_fail (xalign >= 0.0 && xalign <= 1.0); + g_return_if_fail (yalign >= 0.0 && yalign <= 1.0); + + if ((xalign != cell->xalign) || (yalign != cell->yalign)) + { + g_object_freeze_notify (G_OBJECT (cell)); + + if (xalign != cell->xalign) + { + cell->xalign = xalign; + g_object_notify (G_OBJECT (cell), "xalign"); + } + + if (yalign != cell->yalign) + { + cell->yalign = yalign; + g_object_notify (G_OBJECT (cell), "yalign"); + } + + g_object_thaw_notify (G_OBJECT (cell)); + } +} + +/** + * gtk_cell_renderer_get_alignment: + * @cell: A #GtkCellRenderer + * @xalign: location to fill in with the x alignment of the cell, or %NULL + * @yalign: location to fill in with the y alignment of the cell, or %NULL + * + * Fills in @xalign and @yalign with the appropriate values of @cell. + * + * Since: 2.18 + **/ +void +gtk_cell_renderer_get_alignment (GtkCellRenderer *cell, + gfloat *xalign, + gfloat *yalign) +{ + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + if (xalign) + *xalign = cell->xalign; + if (yalign) + *yalign = cell->yalign; +} + +/** + * gtk_cell_renderer_set_padding: + * @cell: A #GtkCellRenderer + * @xpad: the x padding of the cell renderer + * @ypad: the y padding of the cell renderer + * + * Sets the renderer's padding. + * + * Since: 2.18 + **/ +void +gtk_cell_renderer_set_padding (GtkCellRenderer *cell, + gint xpad, + gint ypad) +{ + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + g_return_if_fail (xpad >= 0 && xpad >= 0); + + if ((xpad != cell->xpad) || (ypad != cell->ypad)) + { + g_object_freeze_notify (G_OBJECT (cell)); + + if (xpad != cell->xpad) + { + cell->xpad = xpad; + g_object_notify (G_OBJECT (cell), "xpad"); + } + + if (ypad != cell->ypad) + { + cell->ypad = ypad; + g_object_notify (G_OBJECT (cell), "ypad"); + } + + g_object_thaw_notify (G_OBJECT (cell)); + } +} + +/** + * gtk_cell_renderer_get_padding: + * @cell: A #GtkCellRenderer + * @xpad: location to fill in with the x padding of the cell, or %NULL + * @ypad: location to fill in with the y padding of the cell, or %NULL + * + * Fills in @xpad and @ypad with the appropriate values of @cell. + * + * Since: 2.18 + **/ +void +gtk_cell_renderer_get_padding (GtkCellRenderer *cell, + gint *xpad, + gint *ypad) +{ + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + if (xpad) + *xpad = cell->xpad; + if (ypad) + *ypad = cell->ypad; +} + +/** + * gtk_cell_renderer_set_visible: + * @cell: A #GtkCellRenderer + * @visible: the visibility of the cell + * + * Sets the cell renderer's visibility. + * + * Since: 2.18 + **/ +void +gtk_cell_renderer_set_visible (GtkCellRenderer *cell, + gboolean visible) +{ + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + if (cell->visible != visible) + { + cell->visible = visible ? TRUE : FALSE; + g_object_notify (G_OBJECT (cell), "visible"); + } +} + +/** + * gtk_cell_renderer_get_visible: + * @cell: A #GtkCellRenderer + * + * Returns the cell renderer's visibility. + * + * Returns: %TRUE if the cell renderer is visible + * + * Since: 2.18 + */ +gboolean +gtk_cell_renderer_get_visible (GtkCellRenderer *cell) +{ + g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell), FALSE); + + return cell->visible; +} + +/** + * gtk_cell_renderer_set_sensitive: + * @cell: A #GtkCellRenderer + * @sensitive: the sensitivity of the cell + * + * Sets the cell renderer's sensitivity. + * + * Since: 2.18 + **/ +void +gtk_cell_renderer_set_sensitive (GtkCellRenderer *cell, + gboolean sensitive) +{ + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + if (cell->sensitive != sensitive) + { + cell->sensitive = sensitive ? TRUE : FALSE; + g_object_notify (G_OBJECT (cell), "sensitive"); + } +} + +/** + * gtk_cell_renderer_get_sensitive: + * @cell: A #GtkCellRenderer + * + * Returns the cell renderer's sensitivity. + * + * Returns: %TRUE if the cell renderer is sensitive + * + * Since: 2.18 + */ +gboolean +gtk_cell_renderer_get_sensitive (GtkCellRenderer *cell) +{ + g_return_val_if_fail (GTK_IS_CELL_RENDERER (cell), FALSE); + + return cell->sensitive; } /** diff --git a/gtk/gtkcellrenderer.h b/gtk/gtkcellrenderer.h index 8b54292692..aece202ae9 100644 --- a/gtk/gtkcellrenderer.h +++ b/gtk/gtkcellrenderer.h @@ -152,6 +152,7 @@ GtkCellEditable *gtk_cell_renderer_start_editing (GtkCellRenderer *cell, const GdkRectangle *background_area, const GdkRectangle *cell_area, GtkCellRendererState flags); + void gtk_cell_renderer_set_fixed_size (GtkCellRenderer *cell, gint width, gint height); @@ -159,6 +160,28 @@ void gtk_cell_renderer_get_fixed_size (GtkCellRenderer *cell, gint *width, gint *height); +void gtk_cell_renderer_set_alignment (GtkCellRenderer *cell, + gfloat xalign, + gfloat yalign); +void gtk_cell_renderer_get_alignment (GtkCellRenderer *cell, + gfloat *xalign, + gfloat *yalign); + +void gtk_cell_renderer_set_padding (GtkCellRenderer *cell, + gint xpad, + gint ypad); +void gtk_cell_renderer_get_padding (GtkCellRenderer *cell, + gint *xpad, + gint *ypad); + +void gtk_cell_renderer_set_visible (GtkCellRenderer *cell, + gboolean visible); +gboolean gtk_cell_renderer_get_visible (GtkCellRenderer *cell); + +void gtk_cell_renderer_set_sensitive (GtkCellRenderer *cell, + gboolean sensitive); +gboolean gtk_cell_renderer_get_sensitive (GtkCellRenderer *cell); + /* For use by cell renderer implementations only */ #ifndef GTK_DISABLE_DEPRECATED void gtk_cell_renderer_editing_canceled (GtkCellRenderer *cell); diff --git a/gtk/gtkcellrendereraccel.c b/gtk/gtkcellrendereraccel.c index f3e229688b..dedbfdae64 100644 --- a/gtk/gtkcellrendereraccel.c +++ b/gtk/gtkcellrendereraccel.c @@ -24,6 +24,7 @@ #include "gtkcellrendereraccel.h" #include "gtklabel.h" #include "gtkeventbox.h" +#include "gtkmain.h" #include "gtkprivate.h" #include "gdk/gdkkeysyms.h" #include "gtkalias.h" @@ -470,6 +471,7 @@ grab_key_callback (GtkWidget *widget, edited = TRUE; out: + gtk_grab_remove (accel->grab_widget); gdk_display_keyboard_ungrab (display, event->time); gdk_display_pointer_ungrab (display, event->time); @@ -497,6 +499,7 @@ ungrab_stuff (GtkWidget *widget, { GdkDisplay *display = gtk_widget_get_display (widget); + gtk_grab_remove (accel->grab_widget); gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME); gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME); @@ -606,6 +609,8 @@ gtk_cell_renderer_accel_start_editing (GtkCellRenderer *cell, gtk_widget_show_all (accel->edit_widget); + gtk_grab_add (accel->grab_widget); + g_signal_connect (G_OBJECT (accel->edit_widget), "unrealize", G_CALLBACK (ungrab_stuff), accel); diff --git a/gtk/gtkcellrendererspin.c b/gtk/gtkcellrendererspin.c index 92b66926ad..ebd08e275c 100644 --- a/gtk/gtkcellrendererspin.c +++ b/gtk/gtkcellrendererspin.c @@ -271,6 +271,21 @@ gtk_cell_renderer_spin_key_press_event (GtkWidget *widget, return FALSE; } +static gboolean +gtk_cell_renderer_spin_button_press_event (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) +{ + /* Block 2BUTTON and 3BUTTON here, so that they won't be eaten + * by tree view. + */ + if (event->type == GDK_2BUTTON_PRESS + || event->type == GDK_3BUTTON_PRESS) + return TRUE; + + return FALSE; +} + static GtkCellEditable * gtk_cell_renderer_spin_start_editing (GtkCellRenderer *cell, GdkEvent *event, @@ -296,6 +311,10 @@ gtk_cell_renderer_spin_start_editing (GtkCellRenderer *cell, spin = gtk_spin_button_new (priv->adjustment, priv->climb_rate, priv->digits); + g_signal_connect (spin, "button-press-event", + G_CALLBACK (gtk_cell_renderer_spin_button_press_event), + NULL); + if (cell_text->text) gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), g_ascii_strtod (cell_text->text, NULL)); diff --git a/gtk/gtkcellrendererspinner.c b/gtk/gtkcellrendererspinner.c new file mode 100644 index 0000000000..41bf6cc117 --- /dev/null +++ b/gtk/gtkcellrendererspinner.c @@ -0,0 +1,389 @@ +/* GTK - The GIMP Toolkit + * + * Copyright (C) 2009 Matthias Clasen <mclasen@redhat.com> + * Copyright (C) 2008 Richard Hughes <richard@hughsie.com> + * Copyright (C) 2009 Bastien Nocera <hadess@hadess.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 2007. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "config.h" + +#include "gtkcellrendererspinner.h" +#include "gtkiconfactory.h" +#include "gtkicontheme.h" +#include "gtkintl.h" +#include "gtkalias.h" + + +/** + * SECTION:gtkcellrendererspinner + * @Short_description: Renders a spinning animation in a cell + * @Title: GtkCellRendererSpinner + * @See_also: #GtkSpinner, #GtkCellRendererProgress + * + * GtkCellRendererSpinner renders a spinning animation in a cell, very + * similar to #GtkSpinner. It can often be used as an alternative + * to a #GtkCellRendererProgress for displaying indefinite activity, + * instead of actual progress. + * + * To start the animation in a cell, set the #GtkCellRendererSpinner:active + * property to %TRUE and increment the #GtkCellRendererSpinner:pulse property + * at regular intervals. The usual way to set the cell renderer properties + * for each cell is to bind them to columns in your tree model using e.g. + * gtk_tree_view_column_add_attribute(). + */ + + +enum { + PROP_0, + PROP_ACTIVE, + PROP_PULSE, + PROP_SIZE +}; + +struct _GtkCellRendererSpinnerPrivate +{ + gboolean active; + guint pulse; + GtkIconSize icon_size, old_icon_size; + gint size; +}; + +#define GTK_CELL_RENDERER_SPINNER_GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((object), \ + GTK_TYPE_CELL_RENDERER_SPINNER, \ + GtkCellRendererSpinnerPrivate)) + +static void gtk_cell_renderer_spinner_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void gtk_cell_renderer_spinner_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_cell_renderer_spinner_get_size (GtkCellRenderer *cell, + GtkWidget *widget, + GdkRectangle *cell_area, + gint *x_offset, + gint *y_offset, + gint *width, + gint *height); +static void gtk_cell_renderer_spinner_render (GtkCellRenderer *cell, + GdkWindow *window, + GtkWidget *widget, + GdkRectangle *background_area, + GdkRectangle *cell_area, + GdkRectangle *expose_area, + guint flags); + +G_DEFINE_TYPE (GtkCellRendererSpinner, gtk_cell_renderer_spinner, GTK_TYPE_CELL_RENDERER) + +static void +gtk_cell_renderer_spinner_class_init (GtkCellRendererSpinnerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass); + + object_class->get_property = gtk_cell_renderer_spinner_get_property; + object_class->set_property = gtk_cell_renderer_spinner_set_property; + + cell_class->get_size = gtk_cell_renderer_spinner_get_size; + cell_class->render = gtk_cell_renderer_spinner_render; + + /* GtkCellRendererSpinner:active: + * + * Whether the spinner is active (ie. shown) in the cell + * + * Since 2.20 + */ + g_object_class_install_property (object_class, + PROP_ACTIVE, + g_param_spec_boolean ("active", + P_("Active"), + P_("Whether the spinner is active (ie. shown) in the cell"), + FALSE, + G_PARAM_READWRITE)); + /** + * GtkCellRendererSpinner:pulse: + * + * Pulse of the spinner. Increment this value to draw the next frame of the + * spinner animation. Usually, you would update this value in a timeout. + * + * The #GtkSpinner widget draws one full cycle of the animation per second by default. + * You can learn about the number of frames used by the theme + * by looking at the #GtkSpinner:num-steps style property and the duration + * of the cycle by looking at #GtkSpinner:cycle-duration. + * + * Since 2.20 + */ + g_object_class_install_property (object_class, + PROP_PULSE, + g_param_spec_uint ("pulse", + P_("Pulse"), + P_("Pulse of the spinner"), + 0, G_MAXUINT, 0, + G_PARAM_READWRITE)); + /** + * GtkCellRendererSpinner:size: + * + * The #GtkIconSize value that specifies the size of the rendered spinner. + * + * Since 2.20 + */ + g_object_class_install_property (object_class, + PROP_SIZE, + g_param_spec_enum ("size", + P_("Size"), + P_("The GtkIconSize value that specifies the size of the rendered spinner"), + GTK_TYPE_ICON_SIZE, GTK_ICON_SIZE_MENU, + G_PARAM_READWRITE)); + + + g_type_class_add_private (object_class, sizeof (GtkCellRendererSpinnerPrivate)); +} + +static void +gtk_cell_renderer_spinner_init (GtkCellRendererSpinner *cell) +{ + cell->priv = GTK_CELL_RENDERER_SPINNER_GET_PRIVATE (cell); + cell->priv->pulse = 0; + cell->priv->old_icon_size = GTK_ICON_SIZE_INVALID; + cell->priv->icon_size = GTK_ICON_SIZE_MENU; +} + +/** + * gtk_cell_renderer_spinner_new + * + * Returns a new cell renderer which will show a spinner to indicate + * activity. + * + * Return value: a new #GtkCellRenderer + * + * Since: 2.20 + */ +GtkCellRenderer * +gtk_cell_renderer_spinner_new (void) +{ + return g_object_new (GTK_TYPE_CELL_RENDERER_SPINNER, NULL); +} + +static void +gtk_cell_renderer_spinner_update_size (GtkCellRendererSpinner *cell, + GtkWidget *widget) +{ + GtkCellRendererSpinnerPrivate *priv = cell->priv; + GdkScreen *screen; + GtkIconTheme *icon_theme; + GtkSettings *settings; + + if (cell->priv->old_icon_size == cell->priv->icon_size) + return; + + screen = gtk_widget_get_screen (GTK_WIDGET (widget)); + icon_theme = gtk_icon_theme_get_for_screen (screen); + settings = gtk_settings_get_for_screen (screen); + + if (!gtk_icon_size_lookup_for_settings (settings, priv->icon_size, &priv->size, NULL)) + { + g_warning ("Invalid icon size %u\n", priv->icon_size); + priv->size = 24; + } +} + +static void +gtk_cell_renderer_spinner_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + GtkCellRendererSpinner *cell = GTK_CELL_RENDERER_SPINNER (object); + GtkCellRendererSpinnerPrivate *priv = cell->priv; + + switch (param_id) + { + case PROP_ACTIVE: + g_value_set_boolean (value, priv->active); + break; + case PROP_PULSE: + g_value_set_uint (value, priv->pulse); + break; + case PROP_SIZE: + g_value_set_enum (value, priv->icon_size); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + } +} + +static void +gtk_cell_renderer_spinner_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkCellRendererSpinner *cell = GTK_CELL_RENDERER_SPINNER (object); + GtkCellRendererSpinnerPrivate *priv = cell->priv; + + switch (param_id) + { + case PROP_ACTIVE: + priv->active = g_value_get_boolean (value); + break; + case PROP_PULSE: + priv->pulse = g_value_get_uint (value); + break; + case PROP_SIZE: + priv->old_icon_size = priv->icon_size; + priv->icon_size = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + } +} + +static void +gtk_cell_renderer_spinner_get_size (GtkCellRenderer *cellr, + GtkWidget *widget, + GdkRectangle *cell_area, + gint *x_offset, + gint *y_offset, + gint *width, + gint *height) +{ + GtkCellRendererSpinner *cell = GTK_CELL_RENDERER_SPINNER (cellr); + GtkCellRendererSpinnerPrivate *priv = cell->priv; + gdouble align; + gint w, h; + gint xpad, ypad; + gfloat xalign, yalign; + gboolean rtl; + + rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL; + + gtk_cell_renderer_spinner_update_size (cell, widget); + + g_object_get (cellr, + "xpad", &xpad, + "ypad", &ypad, + "xalign", &xalign, + "yalign", &yalign, + NULL); + w = h = priv->size; + + if (cell_area) + { + if (x_offset) + { + align = rtl ? 1.0 - xalign : xalign; + *x_offset = align * (cell_area->width - w); + *x_offset = MAX (*x_offset, 0); + } + if (y_offset) + { + align = rtl ? 1.0 - yalign : yalign; + *y_offset = align * (cell_area->height - h); + *y_offset = MAX (*y_offset, 0); + } + } + else + { + if (x_offset) + *x_offset = 0; + if (y_offset) + *y_offset = 0; + } + + if (width) + *width = w; + if (height) + *height = h; +} + +static void +gtk_cell_renderer_spinner_render (GtkCellRenderer *cellr, + GdkWindow *window, + GtkWidget *widget, + GdkRectangle *background_area, + GdkRectangle *cell_area, + GdkRectangle *expose_area, + guint flags) +{ + GtkCellRendererSpinner *cell = GTK_CELL_RENDERER_SPINNER (cellr); + GtkCellRendererSpinnerPrivate *priv = cell->priv; + GtkStateType state; + GdkRectangle pix_rect; + GdkRectangle draw_rect; + gint xpad, ypad; + + if (!priv->active) + return; + + gtk_cell_renderer_spinner_get_size (cellr, widget, cell_area, + &pix_rect.x, &pix_rect.y, + &pix_rect.width, &pix_rect.height); + + g_object_get (cellr, + "xpad", &xpad, + "ypad", &ypad, + NULL); + pix_rect.x += cell_area->x + xpad; + pix_rect.y += cell_area->y + ypad; + pix_rect.width -= xpad * 2; + pix_rect.height -= ypad * 2; + + if (!gdk_rectangle_intersect (cell_area, &pix_rect, &draw_rect) || + !gdk_rectangle_intersect (expose_area, &pix_rect, &draw_rect)) + { + return; + } + + state = GTK_STATE_NORMAL; + if (GTK_WIDGET_STATE (widget) == GTK_STATE_INSENSITIVE || !cellr->sensitive) + { + state = GTK_STATE_INSENSITIVE; + } + else + { + if ((flags & GTK_CELL_RENDERER_SELECTED) != 0) + { + if (GTK_WIDGET_HAS_FOCUS (widget)) + state = GTK_STATE_SELECTED; + else + state = GTK_STATE_ACTIVE; + } + else + state = GTK_STATE_PRELIGHT; + } + + gtk_paint_spinner (widget->style, + window, + state, + priv->pulse, + draw_rect.x, draw_rect.y, + draw_rect.width, draw_rect.height); +} + +#define __GTK_CELL_RENDERER_SPINNER_C__ +#include "gtkaliasdef.c" diff --git a/gtk/gtkcellrendererspinner.h b/gtk/gtkcellrendererspinner.h new file mode 100644 index 0000000000..45bcfcab6d --- /dev/null +++ b/gtk/gtkcellrendererspinner.h @@ -0,0 +1,67 @@ +/* GTK - The GIMP Toolkit + * + * Copyright (C) 2009 Matthias Clasen <mclasen@redhat.com> + * Copyright (C) 2008 Richard Hughes <richard@hughsie.com> + * Copyright (C) 2009 Bastien Nocera <hadess@hadess.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only <gtk/gtk.h> can be included directly." +#endif + +#ifndef __GTK_CELL_RENDERER_SPINNER_H__ +#define __GTK_CELL_RENDERER_SPINNER_H__ + +#include <gtk/gtkcellrenderer.h> + +G_BEGIN_DECLS + +#define GTK_TYPE_CELL_RENDERER_SPINNER (gtk_cell_renderer_spinner_get_type ()) +#define GTK_CELL_RENDERER_SPINNER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER_SPINNER, GtkCellRendererSpinner)) +#define GTK_CELL_RENDERER_SPINNER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_RENDERER_SPINNER, GtkCellRendererSpinnerClass)) +#define GTK_IS_CELL_RENDERER_SPINNER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_SPINNER)) +#define GTK_IS_CELL_RENDERER_SPINNER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_RENDERER_SPINNER)) +#define GTK_CELL_RENDERER_SPINNER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_RENDERER_SPINNER, GtkCellRendererSpinnerClass)) + +typedef struct _GtkCellRendererSpinner GtkCellRendererSpinner; +typedef struct _GtkCellRendererSpinnerClass GtkCellRendererSpinnerClass; +typedef struct _GtkCellRendererSpinnerPrivate GtkCellRendererSpinnerPrivate; + +struct _GtkCellRendererSpinner +{ + GtkCellRenderer parent; + GtkCellRendererSpinnerPrivate *priv; +}; + +struct _GtkCellRendererSpinnerClass +{ + GtkCellRendererClass parent_class; + + /* Padding for future expansion */ + void (*_gtk_reserved1) (void); + void (*_gtk_reserved2) (void); + void (*_gtk_reserved3) (void); + void (*_gtk_reserved4) (void); +}; + +GType gtk_cell_renderer_spinner_get_type (void) G_GNUC_CONST; +GtkCellRenderer *gtk_cell_renderer_spinner_new (void); + +G_END_DECLS + +#endif /* __GTK_CELL_RENDERER_SPINNER_H__ */ diff --git a/gtk/gtkcellrenderertoggle.c b/gtk/gtkcellrenderertoggle.c index 5cc826ca9b..d097bb0847 100644 --- a/gtk/gtkcellrenderertoggle.c +++ b/gtk/gtkcellrenderertoggle.c @@ -349,7 +349,7 @@ gtk_cell_renderer_toggle_render (GtkCellRenderer *cell, else shadow = celltoggle->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT; - if (!cell->sensitive) + if (GTK_WIDGET_STATE (widget) == GTK_STATE_INSENSITIVE || !cell->sensitive) { state = GTK_STATE_INSENSITIVE; } @@ -482,5 +482,46 @@ gtk_cell_renderer_toggle_set_active (GtkCellRendererToggle *toggle, g_object_set (toggle, "active", setting ? TRUE : FALSE, NULL); } +/** + * gtk_cell_renderer_toggle_get_activatable: + * @toggle: a #GtkCellRendererToggle + * + * Returns whether the cell renderer is activatable. See + * gtk_cell_renderer_toggle_set_activatable(). + * + * Return value: %TRUE if the cell renderer is activatable. + * + * Since: 2.18 + **/ +gboolean +gtk_cell_renderer_toggle_get_activatable (GtkCellRendererToggle *toggle) +{ + g_return_val_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle), FALSE); + + return toggle->activatable; +} + +/** + * gtk_cell_renderer_toggle_set_activatable: + * @toggle: a #GtkCellRendererToggle. + * @setting: the value to set. + * + * Makes the cell renderer activatable. + * + * Since: 2.18 + **/ +void +gtk_cell_renderer_toggle_set_activatable (GtkCellRendererToggle *toggle, + gboolean setting) +{ + g_return_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle)); + + if (toggle->activatable != setting) + { + toggle->activatable = setting ? TRUE : FALSE; + g_object_notify (G_OBJECT (toggle), "activatable"); + } +} + #define __GTK_CELL_RENDERER_TOGGLE_C__ #include "gtkaliasdef.c" diff --git a/gtk/gtkcellrenderertoggle.h b/gtk/gtkcellrenderertoggle.h index c9b85a191e..cf6a3c2f3d 100644 --- a/gtk/gtkcellrenderertoggle.h +++ b/gtk/gtkcellrenderertoggle.h @@ -64,16 +64,20 @@ struct _GtkCellRendererToggleClass void (*_gtk_reserved4) (void); }; -GType gtk_cell_renderer_toggle_get_type (void) G_GNUC_CONST; -GtkCellRenderer *gtk_cell_renderer_toggle_new (void); +GType gtk_cell_renderer_toggle_get_type (void) G_GNUC_CONST; +GtkCellRenderer *gtk_cell_renderer_toggle_new (void); -gboolean gtk_cell_renderer_toggle_get_radio (GtkCellRendererToggle *toggle); -void gtk_cell_renderer_toggle_set_radio (GtkCellRendererToggle *toggle, - gboolean radio); +gboolean gtk_cell_renderer_toggle_get_radio (GtkCellRendererToggle *toggle); +void gtk_cell_renderer_toggle_set_radio (GtkCellRendererToggle *toggle, + gboolean radio); -gboolean gtk_cell_renderer_toggle_get_active (GtkCellRendererToggle *toggle); -void gtk_cell_renderer_toggle_set_active (GtkCellRendererToggle *toggle, - gboolean setting); +gboolean gtk_cell_renderer_toggle_get_active (GtkCellRendererToggle *toggle); +void gtk_cell_renderer_toggle_set_active (GtkCellRendererToggle *toggle, + gboolean setting); + +gboolean gtk_cell_renderer_toggle_get_activatable (GtkCellRendererToggle *toggle); +void gtk_cell_renderer_toggle_set_activatable (GtkCellRendererToggle *toggle, + gboolean setting); G_END_DECLS diff --git a/gtk/gtkcellview.c b/gtk/gtkcellview.c index 3fa0ad9013..5f8fe39574 100644 --- a/gtk/gtkcellview.c +++ b/gtk/gtkcellview.c @@ -438,6 +438,8 @@ gtk_cell_view_expose (GtkWidget *widget, if (GTK_WIDGET_STATE (widget) == GTK_STATE_PRELIGHT) state = GTK_CELL_RENDERER_PRELIT; + else if (GTK_WIDGET_STATE (widget) == GTK_STATE_INSENSITIVE) + state = GTK_CELL_RENDERER_INSENSITIVE; else state = 0; diff --git a/gtk/gtkclist.c b/gtk/gtkclist.c index 74302d1edb..2d0f6bc32f 100644 --- a/gtk/gtkclist.c +++ b/gtk/gtkclist.c @@ -1401,9 +1401,9 @@ gtk_clist_column_title_passive (GtkCList *clist, clist->column[column].button_passive = TRUE; if (button->button_down) - gtk_button_released (button); + g_signal_emit_by_name (button, "released"); if (button->in_button) - gtk_button_leave (button); + g_signal_emit_by_name (button, "leave"); gtk_signal_connect (GTK_OBJECT (clist->column[column].button), "event", G_CALLBACK (column_title_passive_func), @@ -5925,8 +5925,14 @@ draw_rows (GtkCList *clist, } if (!area) - gdk_window_clear_area (clist->clist_window, 0, - ROW_TOP_YPIXEL (clist, i), 0, 0); + { + int w, h, y; + gdk_drawable_get_size (GDK_DRAWABLE (clist->clist_window), &w, &h); + y = ROW_TOP_YPIXEL (clist, i); + gdk_window_clear_area (clist->clist_window, + 0, y, + w, h - y); + } } static void diff --git a/gtk/gtkcombo.c b/gtk/gtkcombo.c index b1f7c29972..73c9990c8f 100644 --- a/gtk/gtkcombo.c +++ b/gtk/gtkcombo.c @@ -582,7 +582,7 @@ gtk_combo_popdown_list (GtkCombo *combo) if (GTK_BUTTON (combo->button)->in_button) { GTK_BUTTON (combo->button)->in_button = FALSE; - gtk_button_released (GTK_BUTTON (combo->button)); + g_signal_emit_by_name (combo->button, "released"); } if (GTK_WIDGET_HAS_GRAB (combo->popwin)) @@ -666,7 +666,7 @@ gtk_combo_popup_button_press (GtkWidget *button, popup_grab_on_window (combo->popwin->window, gtk_get_current_event_time ()); - gtk_button_pressed (GTK_BUTTON (button)); + g_signal_emit_by_name (button, "depressed"); gtk_grab_add (combo->popwin); diff --git a/gtk/gtkcombobox.c b/gtk/gtkcombobox.c index bda2a92f22..ebe90d9fa2 100644 --- a/gtk/gtkcombobox.c +++ b/gtk/gtkcombobox.c @@ -204,7 +204,8 @@ enum { PROP_HAS_FRAME, PROP_FOCUS_ON_CLICK, PROP_POPUP_SHOWN, - PROP_BUTTON_SENSITIVITY + PROP_BUTTON_SENSITIVITY, + PROP_EDITING_CANCELED }; static guint combo_box_signals[LAST_SIGNAL] = {0,}; @@ -379,6 +380,8 @@ static gboolean gtk_combo_box_menu_button_press (GtkWidget *widget, gpointer user_data); static void gtk_combo_box_menu_item_activate (GtkWidget *item, gpointer user_data); + +static void gtk_combo_box_update_sensitivity (GtkComboBox *combo_box); static void gtk_combo_box_menu_row_inserted (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, @@ -647,6 +650,10 @@ gtk_combo_box_class_init (GtkComboBoxClass *klass) GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END); /* properties */ + g_object_class_override_property (object_class, + PROP_EDITING_CANCELED, + "editing-canceled"); + /** * GtkComboBox:model: * @@ -958,58 +965,58 @@ gtk_combo_box_set_property (GObject *object, switch (prop_id) { - case PROP_MODEL: - gtk_combo_box_set_model (combo_box, g_value_get_object (value)); - break; + case PROP_MODEL: + gtk_combo_box_set_model (combo_box, g_value_get_object (value)); + break; - case PROP_WRAP_WIDTH: - gtk_combo_box_set_wrap_width (combo_box, g_value_get_int (value)); - break; + case PROP_WRAP_WIDTH: + gtk_combo_box_set_wrap_width (combo_box, g_value_get_int (value)); + break; - case PROP_ROW_SPAN_COLUMN: - gtk_combo_box_set_row_span_column (combo_box, g_value_get_int (value)); - break; + case PROP_ROW_SPAN_COLUMN: + gtk_combo_box_set_row_span_column (combo_box, g_value_get_int (value)); + break; - case PROP_COLUMN_SPAN_COLUMN: - gtk_combo_box_set_column_span_column (combo_box, g_value_get_int (value)); - break; + case PROP_COLUMN_SPAN_COLUMN: + gtk_combo_box_set_column_span_column (combo_box, g_value_get_int (value)); + break; - case PROP_ACTIVE: - gtk_combo_box_set_active (combo_box, g_value_get_int (value)); - break; + case PROP_ACTIVE: + gtk_combo_box_set_active (combo_box, g_value_get_int (value)); + break; - case PROP_ADD_TEAROFFS: - gtk_combo_box_set_add_tearoffs (combo_box, g_value_get_boolean (value)); - break; + case PROP_ADD_TEAROFFS: + gtk_combo_box_set_add_tearoffs (combo_box, g_value_get_boolean (value)); + break; - case PROP_HAS_FRAME: - combo_box->priv->has_frame = g_value_get_boolean (value); - break; + case PROP_HAS_FRAME: + combo_box->priv->has_frame = g_value_get_boolean (value); + break; - case PROP_FOCUS_ON_CLICK: - gtk_combo_box_set_focus_on_click (combo_box, - g_value_get_boolean (value)); - break; + case PROP_FOCUS_ON_CLICK: + gtk_combo_box_set_focus_on_click (combo_box, + g_value_get_boolean (value)); + break; - case PROP_TEAROFF_TITLE: - gtk_combo_box_set_title (combo_box, g_value_get_string (value)); - break; + case PROP_TEAROFF_TITLE: + gtk_combo_box_set_title (combo_box, g_value_get_string (value)); + break; - case PROP_POPUP_SHOWN: - if (g_value_get_boolean (value)) - gtk_combo_box_popup (combo_box); - else - gtk_combo_box_popdown (combo_box); - break; + case PROP_POPUP_SHOWN: + if (g_value_get_boolean (value)) + gtk_combo_box_popup (combo_box); + else + gtk_combo_box_popdown (combo_box); + break; - case PROP_BUTTON_SENSITIVITY: - gtk_combo_box_set_button_sensitivity (combo_box, - g_value_get_enum (value)); - break; + case PROP_BUTTON_SENSITIVITY: + gtk_combo_box_set_button_sensitivity (combo_box, + g_value_get_enum (value)); + break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; } } @@ -1020,6 +1027,7 @@ gtk_combo_box_get_property (GObject *object, GParamSpec *pspec) { GtkComboBox *combo_box = GTK_COMBO_BOX (object); + GtkComboBoxPrivate *priv = GTK_COMBO_BOX_GET_PRIVATE (combo_box); switch (prop_id) { @@ -1067,6 +1075,10 @@ gtk_combo_box_get_property (GObject *object, g_value_set_enum (value, combo_box->priv->button_sensitivity); break; + case PROP_EDITING_CANCELED: + g_value_set_boolean (value, priv->editing_canceled); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -2862,6 +2874,7 @@ gtk_combo_box_menu_setup (GtkComboBox *combo_box, gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (priv->column)); gtk_combo_box_update_title (combo_box); + gtk_combo_box_update_sensitivity (combo_box); } static void @@ -3215,6 +3228,11 @@ gtk_combo_box_update_sensitivity (GtkComboBox *combo_box) } gtk_widget_set_sensitive (combo_box->priv->button, sensitive); + + /* In list-mode, we also need to update sensitivity of the event box */ + if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view) + && combo_box->priv->cell_view) + gtk_widget_set_sensitive (combo_box->priv->box, sensitive); } static void @@ -3755,6 +3773,8 @@ gtk_combo_box_list_setup (GtkComboBox *combo_box) combo_box); gtk_widget_show (priv->tree_view); + + gtk_combo_box_update_sensitivity (combo_box); } static void diff --git a/gtk/gtkcomboboxentry.c b/gtk/gtkcomboboxentry.c index 93ae5aea01..c63c08561b 100644 --- a/gtk/gtkcomboboxentry.c +++ b/gtk/gtkcomboboxentry.c @@ -368,9 +368,10 @@ void gtk_combo_box_entry_set_text_column (GtkComboBoxEntry *entry_box, gint text_column) { + GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (entry_box)); + g_return_if_fail (text_column >= 0); - g_return_if_fail (text_column < gtk_tree_model_get_n_columns (gtk_combo_box_get_model (GTK_COMBO_BOX (entry_box)))); - g_return_if_fail (entry_box->priv->text_column == -1); + g_return_if_fail (model == NULL || text_column < gtk_tree_model_get_n_columns (model)); entry_box->priv->text_column = text_column; diff --git a/gtk/gtkcurve.c b/gtk/gtkcurve.c index d996a2d9a0..e49e5ff8c9 100644 --- a/gtk/gtkcurve.c +++ b/gtk/gtkcurve.c @@ -24,6 +24,8 @@ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ +#undef GTK_DISABLE_DEPRECATED + #include "config.h" #include <stdlib.h> #include <string.h> diff --git a/gtk/gtkcurve.h b/gtk/gtkcurve.h index 5edd59db0a..68ee716edb 100644 --- a/gtk/gtkcurve.h +++ b/gtk/gtkcurve.h @@ -24,19 +24,7 @@ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ -/* - * NOTE this widget is considered too specialized/little-used for - * GTK+, and will in the future be moved to some other package. If - * your application needs this widget, feel free to use it, as the - * widget does work and is useful in some applications; it's just not - * of general interest. However, we are not accepting new features for - * the widget, and it will eventually move out of the GTK+ - * distribution. - */ - -#if defined(GTK_DISABLE_SINGLE_INCLUDES) && !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only <gtk/gtk.h> can be included directly." -#endif +#ifndef GTK_DISABLE_DEPRECATED #ifndef __GTK_CURVE_H__ #define __GTK_CURVE_H__ @@ -114,3 +102,5 @@ void gtk_curve_set_curve_type (GtkCurve *curve, GtkCurveType type); G_END_DECLS #endif /* __GTK_CURVE_H__ */ + +#endif /* GTK_DISABLE_DEPRECATED */ diff --git a/gtk/gtkcustompaperunixdialog.c b/gtk/gtkcustompaperunixdialog.c index 972ab1640b..9006eaba23 100644 --- a/gtk/gtkcustompaperunixdialog.c +++ b/gtk/gtkcustompaperunixdialog.c @@ -375,14 +375,11 @@ _gtk_custom_paper_unix_dialog_new (GtkWindow *parent, result = g_object_new (GTK_TYPE_CUSTOM_PAPER_UNIX_DIALOG, "title", title, + "transient-for", parent, + "modal", parent != NULL, + "destroy-with-parent", TRUE, NULL); - if (parent) - { - gtk_window_set_modal (GTK_WINDOW (result), TRUE); - gtk_window_set_transient_for (GTK_WINDOW (result), parent); - } - return result; } diff --git a/gtk/gtkcustompaperunixdialog.h b/gtk/gtkcustompaperunixdialog.h index f1d6ae63b7..0ff3af165d 100644 --- a/gtk/gtkcustompaperunixdialog.h +++ b/gtk/gtkcustompaperunixdialog.h @@ -17,10 +17,6 @@ * Boston, MA 02111-1307, USA. */ -#if defined(GTK_DISABLE_SINGLE_INCLUDES) && !defined (__GTK_UNIX_PRINT_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only <gtk/gtkunixprint.h> can be included directly." -#endif - #ifndef __GTK_CUSTOM_PAPER_UNIX_DIALOG_H__ #define __GTK_CUSTOM_PAPER_UNIX_DIALOG_H__ diff --git a/gtk/gtkdialog.c b/gtk/gtkdialog.c index d463306844..eb25f5224b 100644 --- a/gtk/gtkdialog.c +++ b/gtk/gtkdialog.c @@ -1121,6 +1121,49 @@ _gtk_dialog_set_ignore_separator (GtkDialog *dialog, } /** + * gtk_dialog_get_widget_for_response: + * @dialog: a #GtkDialog + * @response_id: the response ID used by the @dialog widget + * + * Gets the widget button that uses the given response ID in the action area + * of a dialog. + * + * Returns: the @widget button that uses the given @response_id, or %NULL. + * + * Since: 2.20 + */ +GtkWidget* +gtk_dialog_get_widget_for_response (GtkDialog *dialog, + gint response_id) +{ + GList *children; + GList *tmp_list; + + g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL); + + children = gtk_container_get_children (GTK_CONTAINER (dialog->action_area)); + + tmp_list = children; + while (tmp_list != NULL) + { + GtkWidget *widget = tmp_list->data; + ResponseData *rd = get_response_data (widget, FALSE); + + if (rd && rd->response_id == response_id) + { + g_list_free (children); + return widget; + } + + tmp_list = g_list_next (tmp_list); + } + + g_list_free (children); + + return NULL; +} + +/** * gtk_dialog_get_response_for_widget: * @dialog: a #GtkDialog * @widget: a widget in the action area of @dialog diff --git a/gtk/gtkdialog.h b/gtk/gtkdialog.h index 69d2c0049f..18a18961c8 100644 --- a/gtk/gtkdialog.h +++ b/gtk/gtkdialog.h @@ -148,6 +148,8 @@ void gtk_dialog_set_response_sensitive (GtkDialog *dialog, gboolean setting); void gtk_dialog_set_default_response (GtkDialog *dialog, gint response_id); +GtkWidget* gtk_dialog_get_widget_for_response (GtkDialog *dialog, + gint response_id); gint gtk_dialog_get_response_for_widget (GtkDialog *dialog, GtkWidget *widget); diff --git a/gtk/gtkdnd-quartz.c b/gtk/gtkdnd-quartz.c index fd66836360..275ebba0bd 100644 --- a/gtk/gtkdnd-quartz.c +++ b/gtk/gtkdnd-quartz.c @@ -63,6 +63,11 @@ static GtkDragDestInfo *gtk_drag_get_dest_info (GdkDragContext *context, gboolean create); static void gtk_drag_source_site_destroy (gpointer data); +static GtkDragSourceInfo *gtk_drag_get_source_info (GdkDragContext *context, + gboolean create); + +extern GdkDragContext *gdk_quartz_drag_source_context (); /* gdk/quartz/gdkdnd-quartz.c */ + struct _GtkDragSourceSite { GdkModifierType start_button_mask; @@ -89,13 +94,16 @@ struct _GtkDragSourceSite struct _GtkDragSourceInfo { + GtkWidget *source_widget; GtkWidget *widget; GtkTargetList *target_list; /* Targets for drag data */ GdkDragAction possible_actions; /* Actions allowed by source */ GdkDragContext *context; /* drag context */ - + NSEvent *nsevent; /* what started it */ gint hot_x, hot_y; /* Hot spot for drag */ GdkPixbuf *icon_pixbuf; + gboolean success; + gboolean delete; }; struct _GtkDragDestSite @@ -233,19 +241,24 @@ gtk_drag_get_data (GtkWidget *widget, } } - -GtkWidget * -gtk_drag_get_source_widget (GdkDragContext *context) -{ - return NULL; -} - void gtk_drag_finish (GdkDragContext *context, gboolean success, gboolean del, guint32 time) { + GtkDragSourceInfo *info; + GdkDragContext* source_context = gdk_quartz_drag_source_context (); + + if (source_context) + { + info = gtk_drag_get_source_info (source_context, FALSE); + if (info) + { + info->success = success; + info->delete = del; + } + } } static void @@ -307,6 +320,22 @@ gtk_drag_clear_source_info (GdkDragContext *context) g_object_set_qdata (G_OBJECT (context), dest_info_quark, NULL); } +GtkWidget * +gtk_drag_get_source_widget (GdkDragContext *context) +{ + GtkDragSourceInfo *info; + GdkDragContext* real_source_context = gdk_quartz_drag_source_context(); + + if (!real_source_context) + return NULL; + + info = gtk_drag_get_source_info (real_source_context, FALSE); + if (!info) + return NULL; + + return info->source_widget; +} + /************************************************************* * gtk_drag_highlight_expose: * Callback for expose_event for highlighted widgets. @@ -1031,6 +1060,45 @@ gtk_drag_dest_find_target (GtkWidget *widget, return GDK_NONE; } +static gboolean +gtk_drag_begin_idle (gpointer arg) +{ + GdkDragContext* context = (GdkDragContext*) arg; + GtkDragSourceInfo* info = gtk_drag_get_source_info (context, FALSE); + NSWindow *nswindow; + NSPasteboard *pasteboard; + GtkDragSourceOwner *owner; + NSPoint point; + + g_assert (info != NULL); + + pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard]; + owner = [[GtkDragSourceOwner alloc] initWithInfo:info]; + + [pasteboard declareTypes:_gtk_quartz_target_list_to_pasteboard_types (info->target_list) owner:owner]; + + if ((nswindow = get_toplevel_nswindow (info->source_widget)) == NULL) + return FALSE; + + /* Ref the context. It's unreffed when the drag has been aborted */ + g_object_ref (info->context); + + /* FIXME: If the event isn't a mouse event, use the global cursor position instead */ + point = [info->nsevent locationInWindow]; + + [nswindow dragImage:_gtk_quartz_create_image_from_pixbuf (info->icon_pixbuf) + at:point + offset:NSMakeSize(0, 0) + event:info->nsevent + pasteboard:pasteboard + source:nswindow + slideBack:YES]; + + [info->nsevent release]; + + return FALSE; +} + static GdkDragContext * gtk_drag_begin_internal (GtkWidget *widget, GtkDragSourceSite *site, @@ -1042,16 +1110,13 @@ gtk_drag_begin_internal (GtkWidget *widget, GtkDragSourceInfo *info; GdkDragContext *context; NSWindow *nswindow; - NSPasteboard *pasteboard; - GtkDragSourceOwner *owner; - NSEvent *nsevent; - NSPoint point; context = gdk_drag_begin (NULL, NULL); context->is_source = TRUE; info = gtk_drag_get_source_info (context, TRUE); + info->source_widget = g_object_ref (widget); info->widget = g_object_ref (widget); info->target_list = target_list; gtk_target_list_ref (target_list); @@ -1086,13 +1151,13 @@ gtk_drag_begin_internal (GtkWidget *widget, GdkPixbuf *pixbuf; pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 1, 1); - gdk_pixbuf_fill (pixbuf, 0xffffff); - - gtk_drag_set_icon_pixbuf (context, - pixbuf, + gdk_pixbuf_fill (pixbuf, 0xffffff); + + gtk_drag_set_icon_pixbuf (context, + pixbuf, 0, 0); - g_object_unref (pixbuf); + g_object_unref (pixbuf); } break; case GTK_IMAGE_PIXBUF: @@ -1117,31 +1182,17 @@ gtk_drag_begin_internal (GtkWidget *widget, } } - gdk_pointer_ungrab (0); - - pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard]; - owner = [[GtkDragSourceOwner alloc] initWithInfo:info]; - - [pasteboard declareTypes:_gtk_quartz_target_list_to_pasteboard_types (target_list) owner:owner]; - - /* Ref the context. It's unreffed when the drag has been aborted */ - g_object_ref (info->context); - nswindow = get_toplevel_nswindow (widget); + info->nsevent = [nswindow currentEvent]; + [info->nsevent retain]; - /* FIXME: If the event isn't a mouse event, use the global cursor position instead */ - nsevent = [nswindow currentEvent]; - point = [nsevent locationInWindow]; + /* drag will begin in an idle handler to avoid nested run loops */ - [nswindow dragImage:_gtk_quartz_create_image_from_pixbuf (info->icon_pixbuf) - at:point - offset:NSMakeSize(0, 0) - event:nsevent - pasteboard:pasteboard - source:nswindow - slideBack:YES]; + g_idle_add_full (G_PRIORITY_HIGH_IDLE, gtk_drag_begin_idle, context, NULL); - return info->context; + gdk_pointer_ungrab (0); + + return context; } GdkDragContext * @@ -1773,6 +1824,9 @@ gtk_drag_source_info_destroy (GtkDragSourceInfo *info) g_signal_emit_by_name (info->widget, "drag-end", info->context); + if (info->source_widget) + g_object_unref (info->source_widget); + if (info->widget) g_object_unref (info->widget); @@ -1794,6 +1848,10 @@ drag_drop_finished_idle_cb (gpointer data) static void gtk_drag_drop_finished (GtkDragSourceInfo *info) { + if (info->success && info->delete) + g_signal_emit_by_name (info->source_widget, "drag-data-delete", + info->context); + /* Workaround for the fact that the NS API blocks until the drag is * over. This way the context is still valid when returning from * drag_begin, even if it will still be quite useless. See bug #501588. diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c index de10f0d834..9c885ca54d 100644 --- a/gtk/gtkentry.c +++ b/gtk/gtkentry.c @@ -216,7 +216,8 @@ enum { PROP_TOOLTIP_TEXT_SECONDARY, PROP_TOOLTIP_MARKUP_PRIMARY, PROP_TOOLTIP_MARKUP_SECONDARY, - PROP_IM_MODULE + PROP_IM_MODULE, + PROP_EDITING_CANCELED }; static guint signals[LAST_SIGNAL] = { 0 }; @@ -237,9 +238,6 @@ typedef enum */ static void gtk_entry_editable_init (GtkEditableClass *iface); static void gtk_entry_cell_editable_init (GtkCellEditableIface *iface); -static GObject* gtk_entry_constructor (GType type, - guint n_props, - GObjectConstructParam *props); static void gtk_entry_set_property (GObject *object, guint prop_id, const GValue *value, @@ -524,6 +522,8 @@ static void buffer_notify_max_length (GtkEntryBuffer *buffer, GtkEntry *entry); static void buffer_connect_signals (GtkEntry *entry); static void buffer_disconnect_signals (GtkEntry *entry); +static GtkEntryBuffer *get_buffer (GtkEntry *entry); + G_DEFINE_TYPE_WITH_CODE (GtkEntry, gtk_entry, GTK_TYPE_WIDGET, G_IMPLEMENT_INTERFACE (GTK_TYPE_EDITABLE, @@ -565,7 +565,6 @@ gtk_entry_class_init (GtkEntryClass *class) widget_class = (GtkWidgetClass*) class; gtk_object_class = (GtkObjectClass *)class; - gobject_class->constructor = gtk_entry_constructor; gobject_class->dispose = gtk_entry_dispose; gobject_class->finalize = gtk_entry_finalize; gobject_class->set_property = gtk_entry_set_property; @@ -624,6 +623,10 @@ gtk_entry_class_init (GtkEntryClass *class) quark_cursor_hadjustment = g_quark_from_static_string ("gtk-hadjustment"); quark_capslock_feedback = g_quark_from_static_string ("gtk-entry-capslock-feedback"); + g_object_class_override_property (gobject_class, + PROP_EDITING_CANCELED, + "editing-canceled"); + g_object_class_install_property (gobject_class, PROP_BUFFER, g_param_spec_object ("buffer", @@ -1233,7 +1236,7 @@ gtk_entry_class_init (GtkEntryClass *class) GTK_PARAM_READABLE)); /** - * GtkEntry::progress-border: + * GtkEntry:progress-border: * * The border around the progress bar in the entry. * @@ -1247,7 +1250,7 @@ gtk_entry_class_init (GtkEntryClass *class) GTK_PARAM_READABLE)); /** - * GtkEntry::invisible-char: + * GtkEntry:invisible-char: * * The invisible character is used when masking entry contents (in * \"password mode\")"). When it is not explicitly set with the @@ -1258,7 +1261,7 @@ gtk_entry_class_init (GtkEntryClass *class) * This style property allows the theme to prepend a character * to the list of candidates. * - * Since: 2.22 + * Since: 2.18 */ gtk_widget_class_install_style_property (widget_class, g_param_spec_unichar ("invisible-char", @@ -2023,7 +2026,7 @@ gtk_entry_get_property (GObject *object, break; case PROP_MAX_LENGTH: - g_value_set_int (value, gtk_entry_buffer_get_max_length (priv->buffer)); + g_value_set_int (value, gtk_entry_buffer_get_max_length (get_buffer (entry))); break; case PROP_VISIBILITY: @@ -2075,7 +2078,7 @@ gtk_entry_get_property (GObject *object, break; case PROP_TEXT_LENGTH: - g_value_set_uint (value, gtk_entry_buffer_get_length (priv->buffer)); + g_value_set_uint (value, gtk_entry_buffer_get_length (get_buffer (entry))); break; case PROP_INVISIBLE_CHAR_SET: @@ -2198,6 +2201,11 @@ gtk_entry_get_property (GObject *object, gtk_entry_get_icon_tooltip_markup (entry, GTK_ENTRY_ICON_SECONDARY)); break; + case PROP_EDITING_CANCELED: + g_value_set_boolean (value, + entry->editing_canceled); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -2253,34 +2261,10 @@ find_invisible_char (GtkWidget *widget) return '*'; } -static GObject* -gtk_entry_constructor (GType type, - guint n_props, - GObjectConstructParam *props) -{ - GObject *obj = G_OBJECT_CLASS (gtk_entry_parent_class)->constructor (type, n_props, props); - GtkEntryPrivate *priv; - GtkEntryBuffer *buffer; - - if (obj != NULL) - { - priv = GTK_ENTRY_GET_PRIVATE (obj); - if (!priv->buffer) - { - buffer = gtk_entry_buffer_new (NULL, 0); - gtk_entry_set_buffer (GTK_ENTRY (obj), buffer); - g_object_unref (buffer); - } - } - - return obj; -} - static void gtk_entry_init (GtkEntry *entry) { GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); - GtkEntryBuffer *buffer; GTK_WIDGET_SET_FLAGS (entry, GTK_CAN_FOCUS); @@ -2320,13 +2304,6 @@ gtk_entry_init (GtkEntry *entry) g_signal_connect (entry->im_context, "delete-surrounding", G_CALLBACK (gtk_entry_delete_surrounding_cb), entry); - /* need to set a buffer here, so GtkEntry subclasses can do anything - * in their init() functions, just as it used to be before - * GtkEntryBuffer - */ - buffer = gtk_entry_buffer_new (NULL, 0); - gtk_entry_set_buffer (entry, buffer); - g_object_unref (buffer); } static gint @@ -2543,8 +2520,8 @@ gtk_entry_get_display_text (GtkEntry *entry, gint i; priv = GTK_ENTRY_GET_PRIVATE (entry); - text = gtk_entry_buffer_get_text (priv->buffer); - length = gtk_entry_buffer_get_length (priv->buffer); + text = gtk_entry_buffer_get_text (get_buffer (entry)); + length = gtk_entry_buffer_get_length (get_buffer (entry)); if (end_pos < 0) end_pos = length; @@ -2689,9 +2666,6 @@ construct_icon_info (GtkWidget *widget, if (GTK_WIDGET_REALIZED (widget)) realize_icon_info (widget, icon_pos); - if (GTK_WIDGET_MAPPED (widget)) - gdk_window_show_unraised (icon_info->window); - return icon_info; } @@ -3555,6 +3529,10 @@ gtk_entry_leave_notify (GtkWidget *widget, if (icon_info != NULL && event->window == icon_info->window) { + /* a grab means that we may never see the button release */ + if (event->mode == GDK_CROSSING_GRAB || event->mode == GDK_CROSSING_GTK_GRAB) + icon_info->pressed = FALSE; + if (should_prelight (entry, i)) { icon_info->prelight = FALSE; @@ -3997,7 +3975,7 @@ gtk_entry_motion_notify (GtkWidget *widget, if (event->y < 0) tmp_pos = 0; else if (event->y >= height) - tmp_pos = gtk_entry_buffer_get_length (priv->buffer); + tmp_pos = gtk_entry_buffer_get_length (get_buffer (entry)); else tmp_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset); @@ -4314,7 +4292,24 @@ gtk_entry_get_chars (GtkEditable *editable, gint start_pos, gint end_pos) { - return gtk_entry_get_display_text (GTK_ENTRY (editable), start_pos, end_pos); + GtkEntry *entry = GTK_ENTRY (editable); + const gchar *text; + gint text_length; + gint start_index, end_index; + + text = gtk_entry_buffer_get_text (get_buffer (entry)); + text_length = gtk_entry_buffer_get_length (get_buffer (entry)); + + if (end_pos < 0) + end_pos = text_length; + + start_pos = MIN (text_length, start_pos); + end_pos = MIN (text_length, end_pos); + + start_index = g_utf8_offset_to_pointer (text, start_pos) - entry->text; + end_index = g_utf8_offset_to_pointer (text, end_pos) - entry->text; + + return g_strndup (text + start_index, end_index - start_index); } static void @@ -4322,11 +4317,10 @@ gtk_entry_real_set_position (GtkEditable *editable, gint position) { GtkEntry *entry = GTK_ENTRY (editable); - GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); guint length; - length = gtk_entry_buffer_get_length (priv->buffer); + length = gtk_entry_buffer_get_length (get_buffer (entry)); if (position < 0 || position > length) position = length; @@ -4350,10 +4344,9 @@ gtk_entry_set_selection_bounds (GtkEditable *editable, gint end) { GtkEntry *entry = GTK_ENTRY (editable); - GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); guint length; - length = gtk_entry_buffer_get_length (priv->buffer); + length = gtk_entry_buffer_get_length (get_buffer (entry)); if (start < 0) start = length; if (end < 0) @@ -4559,7 +4552,7 @@ gtk_entry_real_delete_text (GtkEditable *editable, * buffer_notify_text(), buffer_notify_length() */ - gtk_entry_buffer_delete_text (GTK_ENTRY_GET_PRIVATE (editable)->buffer, start_pos, end_pos - start_pos); + gtk_entry_buffer_delete_text (get_buffer (GTK_ENTRY (editable)), start_pos, end_pos - start_pos); } /* GtkEntryBuffer signal handlers @@ -4682,23 +4675,21 @@ buffer_notify_max_length (GtkEntryBuffer *buffer, static void buffer_connect_signals (GtkEntry *entry) { - GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); - g_signal_connect (priv->buffer, "inserted-text", G_CALLBACK (buffer_inserted_text), entry); - g_signal_connect (priv->buffer, "deleted-text", G_CALLBACK (buffer_deleted_text), entry); - g_signal_connect (priv->buffer, "notify::text", G_CALLBACK (buffer_notify_text), entry); - g_signal_connect (priv->buffer, "notify::length", G_CALLBACK (buffer_notify_length), entry); - g_signal_connect (priv->buffer, "notify::max-length", G_CALLBACK (buffer_notify_max_length), entry); + g_signal_connect (get_buffer (entry), "inserted-text", G_CALLBACK (buffer_inserted_text), entry); + g_signal_connect (get_buffer (entry), "deleted-text", G_CALLBACK (buffer_deleted_text), entry); + g_signal_connect (get_buffer (entry), "notify::text", G_CALLBACK (buffer_notify_text), entry); + g_signal_connect (get_buffer (entry), "notify::length", G_CALLBACK (buffer_notify_length), entry); + g_signal_connect (get_buffer (entry), "notify::max-length", G_CALLBACK (buffer_notify_max_length), entry); } static void buffer_disconnect_signals (GtkEntry *entry) { - GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); - g_signal_handlers_disconnect_by_func (priv->buffer, buffer_inserted_text, entry); - g_signal_handlers_disconnect_by_func (priv->buffer, buffer_deleted_text, entry); - g_signal_handlers_disconnect_by_func (priv->buffer, buffer_notify_text, entry); - g_signal_handlers_disconnect_by_func (priv->buffer, buffer_notify_length, entry); - g_signal_handlers_disconnect_by_func (priv->buffer, buffer_notify_max_length, entry); + g_signal_handlers_disconnect_by_func (get_buffer (entry), buffer_inserted_text, entry); + g_signal_handlers_disconnect_by_func (get_buffer (entry), buffer_deleted_text, entry); + g_signal_handlers_disconnect_by_func (get_buffer (entry), buffer_notify_text, entry); + g_signal_handlers_disconnect_by_func (get_buffer (entry), buffer_notify_length, entry); + g_signal_handlers_disconnect_by_func (get_buffer (entry), buffer_notify_max_length, entry); } /* Compute the X position for an offset that corresponds to the "more important @@ -4772,7 +4763,7 @@ gtk_entry_move_cursor (GtkEntry *entry, case GTK_MOVEMENT_PARAGRAPH_ENDS: case GTK_MOVEMENT_BUFFER_ENDS: priv = GTK_ENTRY_GET_PRIVATE (entry); - new_pos = count < 0 ? 0 : gtk_entry_buffer_get_length (priv->buffer); + new_pos = count < 0 ? 0 : gtk_entry_buffer_get_length (get_buffer (entry)); break; case GTK_MOVEMENT_DISPLAY_LINES: case GTK_MOVEMENT_PARAGRAPHS: @@ -4830,7 +4821,7 @@ gtk_entry_move_cursor (GtkEntry *entry, case GTK_MOVEMENT_PARAGRAPH_ENDS: case GTK_MOVEMENT_BUFFER_ENDS: priv = GTK_ENTRY_GET_PRIVATE (entry); - new_pos = count < 0 ? 0 : gtk_entry_buffer_get_length (priv->buffer); + new_pos = count < 0 ? 0 : gtk_entry_buffer_get_length (get_buffer (entry)); if (entry->current_pos == new_pos) gtk_widget_error_bell (GTK_WIDGET (entry)); break; @@ -4872,10 +4863,9 @@ gtk_entry_delete_from_cursor (GtkEntry *entry, gint count) { GtkEditable *editable = GTK_EDITABLE (entry); - GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); gint start_pos = entry->current_pos; gint end_pos = entry->current_pos; - gint old_n_bytes = gtk_entry_buffer_get_bytes (priv->buffer); + gint old_n_bytes = gtk_entry_buffer_get_bytes (get_buffer (entry)); _gtk_entry_reset_im_context (entry); @@ -4941,7 +4931,7 @@ gtk_entry_delete_from_cursor (GtkEntry *entry, break; } - if (gtk_entry_buffer_get_bytes (priv->buffer) == old_n_bytes) + if (gtk_entry_buffer_get_bytes (get_buffer (entry)) == old_n_bytes) gtk_widget_error_bell (GTK_WIDGET (entry)); gtk_entry_pend_cursor_blink (entry); @@ -5826,7 +5816,6 @@ gtk_entry_get_cursor_locations (GtkEntry *entry, gint *weak_x) { DisplayMode mode = gtk_entry_get_display_mode (entry); - GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); /* Nothing to display at all, so no cursor is relevant */ if (mode == DISPLAY_BLANK) @@ -5858,7 +5847,7 @@ gtk_entry_get_cursor_locations (GtkEntry *entry, index += entry->preedit_length; else { - gint preedit_len_chars = g_utf8_strlen (text, -1) - gtk_entry_buffer_get_length (priv->buffer); + gint preedit_len_chars = g_utf8_strlen (text, -1) - gtk_entry_buffer_get_length (get_buffer (entry)); index += preedit_len_chars * g_unichar_to_utf8 (entry->invisible_char, NULL); } } @@ -6062,11 +6051,10 @@ gtk_entry_move_logically (GtkEntry *entry, gint start, gint count) { - GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); gint new_pos = start; guint length; - length = gtk_entry_buffer_get_length (priv->buffer); + length = gtk_entry_buffer_get_length (get_buffer (entry)); /* Prevent any leak of information */ if (gtk_entry_get_display_mode (entry) != DISPLAY_NORMAL) @@ -6109,11 +6097,10 @@ gtk_entry_move_forward_word (GtkEntry *entry, gint start, gboolean allow_whitespace) { - GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); gint new_pos = start; guint length; - length = gtk_entry_buffer_get_length (priv->buffer); + length = gtk_entry_buffer_get_length (get_buffer (entry)); /* Prevent any leak of information */ if (gtk_entry_get_display_mode (entry) != DISPLAY_NORMAL) @@ -6257,7 +6244,7 @@ paste_received (GtkClipboard *clipboard, length = truncate_multiline (text); /* only complete if the selection is at the end */ - popup_completion = (gtk_entry_buffer_get_length (priv->buffer) == + popup_completion = (gtk_entry_buffer_get_length (get_buffer (entry)) == MAX (entry->current_pos, entry->selection_bound)); if (completion) @@ -6587,11 +6574,28 @@ gtk_entry_new_with_max_length (gint max) max = CLAMP (max, 0, GTK_ENTRY_BUFFER_MAX_SIZE); entry = g_object_new (GTK_TYPE_ENTRY, NULL); - gtk_entry_buffer_set_max_length (GTK_ENTRY_GET_PRIVATE (entry)->buffer, max); + gtk_entry_buffer_set_max_length (get_buffer (entry), max); return GTK_WIDGET (entry); } + +static GtkEntryBuffer* +get_buffer (GtkEntry *entry) +{ + GtkEntryPrivate *priv = GTK_ENTRY_GET_PRIVATE (entry); + + if (priv->buffer == NULL) + { + GtkEntryBuffer *buffer; + buffer = gtk_entry_buffer_new (NULL, 0); + gtk_entry_set_buffer (entry, buffer); + g_object_unref (buffer); + } + + return priv->buffer; +} + /** * gtk_entry_get_buffer: * @entry: a #GtkEntry @@ -6607,7 +6611,8 @@ GtkEntryBuffer* gtk_entry_get_buffer (GtkEntry *entry) { g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL); - return GTK_ENTRY_GET_PRIVATE (entry)->buffer; + + return get_buffer (entry); } /** @@ -6686,6 +6691,7 @@ void gtk_entry_set_text (GtkEntry *entry, const gchar *text) { + gint tmp_pos; GtkEntryCompletion *completion; GtkEntryPrivate *priv; @@ -6696,7 +6702,7 @@ gtk_entry_set_text (GtkEntry *entry, /* Actually setting the text will affect the cursor and selection; * if the contents don't actually change, this will look odd to the user. */ - if (strcmp (gtk_entry_buffer_get_text (priv->buffer), text) == 0) + if (strcmp (gtk_entry_buffer_get_text (get_buffer (entry)), text) == 0) return; completion = gtk_entry_get_completion (entry); @@ -6705,7 +6711,9 @@ gtk_entry_set_text (GtkEntry *entry, begin_change (entry); g_object_freeze_notify (G_OBJECT (entry)); - gtk_entry_buffer_set_text (priv->buffer, text, -1); + gtk_editable_delete_text (GTK_EDITABLE (entry), 0, -1); + tmp_pos = 0; + gtk_editable_insert_text (GTK_EDITABLE (entry), text, strlen (text), &tmp_pos); g_object_thaw_notify (G_OBJECT (entry)); end_change (entry); @@ -6733,7 +6741,7 @@ gtk_entry_append_text (GtkEntry *entry, g_return_if_fail (text != NULL); priv = GTK_ENTRY_GET_PRIVATE (entry); - tmp_pos = gtk_entry_buffer_get_length (priv->buffer); + tmp_pos = gtk_entry_buffer_get_length (get_buffer (entry)); gtk_editable_insert_text (GTK_EDITABLE (entry), text, -1, &tmp_pos); } @@ -7005,7 +7013,7 @@ G_CONST_RETURN gchar* gtk_entry_get_text (GtkEntry *entry) { g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL); - return gtk_entry_buffer_get_text (GTK_ENTRY_GET_PRIVATE (entry)->buffer); + return gtk_entry_buffer_get_text (get_buffer (entry)); } /** @@ -7052,7 +7060,7 @@ gtk_entry_set_max_length (GtkEntry *entry, gint max) { g_return_if_fail (GTK_IS_ENTRY (entry)); - gtk_entry_buffer_set_max_length (GTK_ENTRY_GET_PRIVATE (entry)->buffer, max); + gtk_entry_buffer_set_max_length (get_buffer (entry), max); } /** @@ -7075,7 +7083,7 @@ gint gtk_entry_get_max_length (GtkEntry *entry) { g_return_val_if_fail (GTK_IS_ENTRY (entry), 0); - return gtk_entry_buffer_get_max_length (GTK_ENTRY_GET_PRIVATE (entry)->buffer); + return gtk_entry_buffer_get_max_length (get_buffer (entry)); } /** @@ -7100,7 +7108,7 @@ guint16 gtk_entry_get_text_length (GtkEntry *entry) { g_return_val_if_fail (GTK_IS_ENTRY (entry), 0); - return gtk_entry_buffer_get_length (GTK_ENTRY_GET_PRIVATE (entry)->buffer); + return gtk_entry_buffer_get_length (get_buffer (entry)); } /** @@ -7535,6 +7543,9 @@ gtk_entry_set_icon_from_pixbuf (GtkEntry *entry, g_object_notify (G_OBJECT (entry), "secondary-icon-pixbuf"); g_object_notify (G_OBJECT (entry), "secondary-icon-storage-type"); } + + if (GTK_WIDGET_MAPPED (entry)) + gdk_window_show_unraised (icon_info->window); } gtk_entry_ensure_pixbuf (entry, icon_pos); @@ -7599,6 +7610,9 @@ gtk_entry_set_icon_from_stock (GtkEntry *entry, g_object_notify (G_OBJECT (entry), "secondary-icon-stock"); g_object_notify (G_OBJECT (entry), "secondary-icon-storage-type"); } + + if (GTK_WIDGET_MAPPED (entry)) + gdk_window_show_unraised (icon_info->window); } gtk_entry_ensure_pixbuf (entry, icon_pos); @@ -7666,6 +7680,9 @@ gtk_entry_set_icon_from_icon_name (GtkEntry *entry, g_object_notify (G_OBJECT (entry), "secondary-icon-name"); g_object_notify (G_OBJECT (entry), "secondary-icon-storage-type"); } + + if (GTK_WIDGET_MAPPED (entry)) + gdk_window_show_unraised (icon_info->window); } gtk_entry_ensure_pixbuf (entry, icon_pos); @@ -7730,6 +7747,9 @@ gtk_entry_set_icon_from_gicon (GtkEntry *entry, g_object_notify (G_OBJECT (entry), "secondary-icon-gicon"); g_object_notify (G_OBJECT (entry), "secondary-icon-storage-type"); } + + if (GTK_WIDGET_MAPPED (entry)) + gdk_window_show_unraised (icon_info->window); } gtk_entry_ensure_pixbuf (entry, icon_pos); @@ -7973,9 +7993,14 @@ gtk_entry_set_icon_sensitive (GtkEntry *entry, { icon_info->insensitive = !sensitive; + icon_info->pressed = FALSE; + icon_info->prelight = FALSE; + if (GTK_WIDGET_REALIZED (GTK_WIDGET (entry))) update_cursors (GTK_WIDGET (entry)); + gtk_widget_queue_draw (GTK_WIDGET (entry)); + g_object_notify (G_OBJECT (entry), icon_pos == GTK_ENTRY_ICON_PRIMARY ? "primary-icon-sensitive" : "secondary-icon-sensitive"); } @@ -8099,6 +8124,8 @@ gtk_entry_get_icon_at_pos (GtkEntry *entry, * #GtkWidget::drag-begin signal to set a different icon. Note that you * have to use g_signal_connect_after() to ensure that your signal handler * gets executed after the default handler. + * + * Since: 2.16 */ void gtk_entry_set_icon_drag_source (GtkEntry *entry, @@ -8138,6 +8165,8 @@ gtk_entry_set_icon_drag_source (GtkEntry *entry, * * Returns: index of the icon which is the source of the current * DND operation, or -1. + * + * Since: 2.16 */ gint gtk_entry_get_current_icon_drag_source (GtkEntry *entry) @@ -9374,6 +9403,9 @@ keypress_completion_out: event->keyval == GDK_KP_Enter || event->keyval == GDK_Return) { + GtkTreeIter iter; + GtkTreeModel *model = NULL; + GtkTreeSelection *sel; gboolean retval = TRUE; _gtk_entry_reset_im_context (GTK_ENTRY (widget)); @@ -9381,9 +9413,6 @@ keypress_completion_out: if (completion->priv->current_selected < matches) { - GtkTreeIter iter; - GtkTreeModel *model = NULL; - GtkTreeSelection *sel; gboolean entry_set; sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view)); @@ -9415,15 +9444,18 @@ keypress_completion_out: } else if (completion->priv->current_selected - matches >= 0) { - GtkTreePath *path; - - _gtk_entry_reset_im_context (GTK_ENTRY (widget)); - - path = gtk_tree_path_new_from_indices (completion->priv->current_selected - matches, -1); + sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->action_view)); + if (gtk_tree_selection_get_selected (sel, &model, &iter)) + { + GtkTreePath *path; - g_signal_emit_by_name (completion, "action-activated", - gtk_tree_path_get_indices (path)[0]); - gtk_tree_path_free (path); + path = gtk_tree_path_new_from_indices (completion->priv->current_selected - matches, -1); + g_signal_emit_by_name (completion, "action-activated", + gtk_tree_path_get_indices (path)[0]); + gtk_tree_path_free (path); + } + else + retval = FALSE; } g_free (completion->priv->completion_prefix); @@ -9494,7 +9526,7 @@ accept_completion_callback (GtkEntry *entry) if (completion->priv->has_completion) gtk_editable_set_position (GTK_EDITABLE (entry), - gtk_entry_buffer_get_length (GTK_ENTRY_GET_PRIVATE (entry)->buffer)); + gtk_entry_buffer_get_length (get_buffer (entry))); return FALSE; } diff --git a/gtk/gtkentrybuffer.c b/gtk/gtkentrybuffer.c index 865332714f..57ce3b325a 100644 --- a/gtk/gtkentrybuffer.c +++ b/gtk/gtkentrybuffer.c @@ -70,7 +70,7 @@ static guint signals[LAST_SIGNAL] = { 0 }; struct _GtkEntryBufferPrivate { - guint max_length; + gint max_length; /* Only valid if this class is not derived */ gchar *normal_text; @@ -288,7 +288,7 @@ gtk_entry_buffer_set_property (GObject *obj, gtk_entry_buffer_set_text (buffer, g_value_get_string (value), -1); break; case PROP_MAX_LENGTH: - gtk_entry_buffer_set_max_length (buffer, g_value_get_uint (value)); + gtk_entry_buffer_set_max_length (buffer, g_value_get_int (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); @@ -313,7 +313,7 @@ gtk_entry_buffer_get_property (GObject *obj, g_value_set_uint (value, gtk_entry_buffer_get_length (buffer)); break; case PROP_MAX_LENGTH: - g_value_set_uint (value, gtk_entry_buffer_get_max_length (buffer)); + g_value_set_int (value, gtk_entry_buffer_get_max_length (buffer)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); @@ -347,10 +347,13 @@ gtk_entry_buffer_class_init (GtkEntryBufferClass *klass) * * Since: 2.18 */ - g_object_class_install_property (gobject_class, PROP_TEXT, - g_param_spec_string ("text", P_("Text"), - P_("The contents of the buffer"), - "", GTK_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, + PROP_TEXT, + g_param_spec_string ("text", + P_("Text"), + P_("The contents of the buffer"), + "", + GTK_PARAM_READWRITE)); /** * GtkEntryBuffer:length: @@ -359,10 +362,13 @@ gtk_entry_buffer_class_init (GtkEntryBufferClass *klass) * * Since: 2.18 */ - g_object_class_install_property (gobject_class, PROP_LENGTH, - g_param_spec_uint ("length", P_("Text length"), - P_("Length of the text currently in the buffer"), - 0, GTK_ENTRY_BUFFER_MAX_SIZE, 0, GTK_PARAM_READABLE)); + g_object_class_install_property (gobject_class, + PROP_LENGTH, + g_param_spec_uint ("length", + P_("Text length"), + P_("Length of the text currently in the buffer"), + 0, GTK_ENTRY_BUFFER_MAX_SIZE, 0, + GTK_PARAM_READABLE)); /** * GtkEntryBuffer:max-length: @@ -371,10 +377,13 @@ gtk_entry_buffer_class_init (GtkEntryBufferClass *klass) * * Since: 2.18 */ - g_object_class_install_property (gobject_class, PROP_MAX_LENGTH, - g_param_spec_uint ("max-length", P_("Maximum length"), - P_("Maximum number of characters for this entry. Zero if no maximum"), - 0, GTK_ENTRY_BUFFER_MAX_SIZE, 0, GTK_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, + PROP_MAX_LENGTH, + g_param_spec_int ("max-length", + P_("Maximum length"), + P_("Maximum number of characters for this entry. Zero if no maximum"), + 0, GTK_ENTRY_BUFFER_MAX_SIZE, 0, + GTK_PARAM_READWRITE)); /** * GtkEntry::inserted-text: @@ -511,7 +520,7 @@ gtk_entry_buffer_get_bytes (GtkEntryBuffer *buffer) * * Since: 2.18 **/ -const gchar* +G_CONST_RETURN gchar* gtk_entry_buffer_get_text (GtkEntryBuffer *buffer) { GtkEntryBufferClass *klass; @@ -568,12 +577,11 @@ gtk_entry_buffer_set_text (GtkEntryBuffer *buffer, **/ void gtk_entry_buffer_set_max_length (GtkEntryBuffer *buffer, - guint max_length) + gint max_length) { g_return_if_fail (GTK_IS_ENTRY_BUFFER (buffer)); - if (max_length > GTK_ENTRY_BUFFER_MAX_SIZE) - max_length = GTK_ENTRY_BUFFER_MAX_SIZE; + max_length = CLAMP (max_length, 0, GTK_ENTRY_BUFFER_MAX_SIZE); if (max_length > 0 && gtk_entry_buffer_get_length (buffer) > max_length) gtk_entry_buffer_delete_text (buffer, max_length, -1); @@ -593,8 +601,8 @@ gtk_entry_buffer_set_max_length (GtkEntryBuffer *buffer, * in #GtkEntryBuffer, or 0 if there is no maximum. * * Since: 2.18 - **/ -guint + */ +gint gtk_entry_buffer_get_max_length (GtkEntryBuffer *buffer) { g_return_val_if_fail (GTK_IS_ENTRY_BUFFER (buffer), 0); diff --git a/gtk/gtkentrybuffer.h b/gtk/gtkentrybuffer.h index 275aaa1b3b..20556cb9e8 100644 --- a/gtk/gtkentrybuffer.h +++ b/gtk/gtkentrybuffer.h @@ -99,16 +99,16 @@ gsize gtk_entry_buffer_get_bytes (GtkEntryBuffe guint gtk_entry_buffer_get_length (GtkEntryBuffer *buffer); -const gchar* gtk_entry_buffer_get_text (GtkEntryBuffer *buffer); +G_CONST_RETURN gchar* gtk_entry_buffer_get_text (GtkEntryBuffer *buffer); void gtk_entry_buffer_set_text (GtkEntryBuffer *buffer, const gchar *chars, gint n_chars); void gtk_entry_buffer_set_max_length (GtkEntryBuffer *buffer, - guint max_length); + gint max_length); -guint gtk_entry_buffer_get_max_length (GtkEntryBuffer *buffer); +gint gtk_entry_buffer_get_max_length (GtkEntryBuffer *buffer); guint gtk_entry_buffer_insert_text (GtkEntryBuffer *buffer, guint position, diff --git a/gtk/gtkfilechooser.c b/gtk/gtkfilechooser.c index f113bea1f0..6c7de57225 100644 --- a/gtk/gtkfilechooser.c +++ b/gtk/gtkfilechooser.c @@ -269,6 +269,22 @@ gtk_file_chooser_class_init (gpointer g_iface) "if necessary."), FALSE, GTK_PARAM_READWRITE)); + + /** + * GtkFileChooser:create-folders: + * + * Whether a file chooser not in %GTK_FILE_CHOOSER_ACTION_OPEN mode + * will offer the user to create new folders. + * + * Since: 2.18 + */ + g_object_interface_install_property (g_iface, + g_param_spec_boolean ("create-folders", + P_("Allow folders creation"), + P_("Whether a file chooser not in open mode " + "will offer the user to create new folders."), + TRUE, + GTK_PARAM_READWRITE)); } /** @@ -424,6 +440,49 @@ gtk_file_chooser_get_select_multiple (GtkFileChooser *chooser) } /** + * gtk_file_chooser_set_create_folders: + * @chooser: a #GtkFileChooser + * @create_folders: %TRUE if the New Folder button should be displayed + * + * Sets whether file choser will offer to create new folders. + * This is only relevant if the action is not set to be + * GTK_FILE_CHOOSER_ACTION_OPEN. + * + * Since: 2.18 + **/ +void +gtk_file_chooser_set_create_folders (GtkFileChooser *chooser, + gboolean create_folders) +{ + g_return_if_fail (GTK_IS_FILE_CHOOSER (chooser)); + + g_object_set (chooser, "create-folders", create_folders, NULL); +} + +/** + * gtk_file_chooser_get_create_folders: + * @chooser: a #GtkFileChooser + * + * Gets whether file choser will offer to create new folders. + * See gtk_file_chooser_set_create_folders(). + * + * Return value: %TRUE if the New Folder button should be displayed. + * + * Since: 2.18 + **/ +gboolean +gtk_file_chooser_get_create_folders (GtkFileChooser *chooser) +{ + gboolean create_folders; + + g_return_val_if_fail (GTK_IS_FILE_CHOOSER (chooser), FALSE); + + g_object_get (chooser, "create-folders", &create_folders, NULL); + + return create_folders; +} + +/** * gtk_file_chooser_get_filename: * @chooser: a #GtkFileChooser * @@ -1180,7 +1239,8 @@ gtk_file_chooser_set_file (GtkFileChooser *chooser, * If the file chooser is in folder mode, this function returns the selected * folder. * - * Returns: a selected #GFile + * Returns: a selected #GFile. You own the returned file; use + * g_object_unref() to release it. * * Since: 2.14 **/ diff --git a/gtk/gtkfilechooser.h b/gtk/gtkfilechooser.h index b7983a9660..0772690bd2 100644 --- a/gtk/gtkfilechooser.h +++ b/gtk/gtkfilechooser.h @@ -85,6 +85,10 @@ void gtk_file_chooser_set_do_overwrite_confirmation (GtkFileChoo gboolean do_overwrite_confirmation); gboolean gtk_file_chooser_get_do_overwrite_confirmation (GtkFileChooser *chooser); +void gtk_file_chooser_set_create_folders (GtkFileChooser *chooser, + gboolean create_folders); +gboolean gtk_file_chooser_get_create_folders (GtkFileChooser *chooser); + /* Suggested name for the Save-type actions */ void gtk_file_chooser_set_current_name (GtkFileChooser *chooser, diff --git a/gtk/gtkfilechooserbutton.c b/gtk/gtkfilechooserbutton.c index 3b95adeeda..70b0b4f268 100644 --- a/gtk/gtkfilechooserbutton.c +++ b/gtk/gtkfilechooserbutton.c @@ -810,6 +810,7 @@ gtk_file_chooser_button_set_property (GObject *object, case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET: case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN: case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION: + case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS: g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value); break; @@ -2301,6 +2302,12 @@ update_label_and_image (GtkFileChooserButton *button) label_text = NULL; pixbuf = NULL; + if (priv->update_button_cancellable) + { + g_cancellable_cancel (priv->update_button_cancellable); + priv->update_button_cancellable = NULL; + } + if (files && files->data) { GFile *file; @@ -2332,12 +2339,6 @@ update_label_and_image (GtkFileChooserButton *button) goto out; } - if (priv->update_button_cancellable) - { - g_cancellable_cancel (priv->update_button_cancellable); - priv->update_button_cancellable = NULL; - } - if (g_file_is_native (file)) { priv->update_button_cancellable = diff --git a/gtk/gtkfilechooserdefault.c b/gtk/gtkfilechooserdefault.c index 388129ae5e..1dcd09f447 100644 --- a/gtk/gtkfilechooserdefault.c +++ b/gtk/gtkfilechooserdefault.c @@ -191,30 +191,37 @@ typedef enum { SHORTCUT_TYPE_RECENT } ShortcutType; -/* Column numbers for the search model. - * Keep this in sync with search_setup_model() - */ +#define MODEL_ATTRIBUTES "standard::name,standard::type,standard::display-name," \ + "standard::is-hidden,standard::is-backup,standard::size," \ + "standard::content-type,time::modified" enum { - SEARCH_MODEL_COL_FILE, - SEARCH_MODEL_COL_DISPLAY_NAME, - SEARCH_MODEL_COL_COLLATION_KEY, - SEARCH_MODEL_COL_MTIME, - SEARCH_MODEL_COL_SIZE, - SEARCH_MODEL_COL_CANCELLABLE, - SEARCH_MODEL_COL_PIXBUF, - SEARCH_MODEL_COL_MIME_TYPE, - SEARCH_MODEL_COL_IS_FOLDER, - SEARCH_MODEL_COL_NUM_COLUMNS + /* the first 3 must be these due to settings caching sort column */ + MODEL_COL_NAME, + MODEL_COL_SIZE, + MODEL_COL_MTIME, + MODEL_COL_FILE, + MODEL_COL_NAME_COLLATED, + MODEL_COL_IS_FOLDER, + MODEL_COL_PIXBUF, + MODEL_COL_SIZE_TEXT, + MODEL_COL_MTIME_TEXT, + MODEL_COL_ELLIPSIZE, + MODEL_COL_NUM_COLUMNS }; -enum { - RECENT_MODEL_COL_FILE, - RECENT_MODEL_COL_DISPLAY_NAME, - RECENT_MODEL_COL_INFO, - RECENT_MODEL_COL_IS_FOLDER, - RECENT_MODEL_COL_CANCELLABLE, - RECENT_MODEL_COL_NUM_COLUMNS -}; +/* This list of types is passed to _gtk_file_system_model_new*() */ +#define MODEL_COLUMN_TYPES \ + MODEL_COL_NUM_COLUMNS, \ + G_TYPE_STRING, /* MODEL_COL_NAME */ \ + G_TYPE_INT64, /* MODEL_COL_SIZE */ \ + G_TYPE_LONG, /* MODEL_COL_MTIME */ \ + G_TYPE_FILE, /* MODEL_COL_FILE */ \ + G_TYPE_STRING, /* MODEL_COL_NAME_COLLATED */ \ + G_TYPE_BOOLEAN, /* MODEL_COL_IS_FOLDER */ \ + GDK_TYPE_PIXBUF, /* MODEL_COL_PIXBUF */ \ + G_TYPE_STRING, /* MODEL_COL_SIZE_TEXT */ \ + G_TYPE_STRING, /* MODEL_COL_MTIME_TEXT */ \ + PANGO_TYPE_ELLIPSIZE_MODE /* MODEL_COL_ELLIPSIZE */ /* Identifiers for target types */ enum { @@ -376,11 +383,6 @@ static void list_row_activated (GtkTreeView *tree_view, GtkTreeViewColumn *column, GtkFileChooserDefault *impl); -static void select_func (GtkFileSystemModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer user_data); - static void path_bar_clicked (GtkPathBar *path_bar, GFile *file, GFile *child, @@ -394,29 +396,7 @@ static void remove_bookmark_button_clicked_cb (GtkButton *button, static void save_folder_combo_changed_cb (GtkComboBox *combo, GtkFileChooserDefault *impl); -static void list_icon_data_func (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gpointer data); -static void list_name_data_func (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gpointer data); -static void list_size_data_func (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gpointer data); -static void list_mtime_data_func (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gpointer data); - -static GFileInfo *get_list_file_info (GtkFileChooserDefault *impl, - GtkTreeIter *iter); +static void update_cell_renderer_attributes (GtkFileChooserDefault *impl); static void load_remove_timer (GtkFileChooserDefault *impl); static void browse_files_center_selected_row (GtkFileChooserDefault *impl); @@ -427,8 +407,6 @@ static void location_switch_to_path_bar (GtkFileChooserDefault *impl); static void search_stop_searching (GtkFileChooserDefault *impl, gboolean remove_query); -static void search_clear_model_row (GtkTreeModel *model, - GtkTreeIter *iter); static void search_clear_model (GtkFileChooserDefault *impl, gboolean remove_from_treeview); static gboolean search_should_respond (GtkFileChooserDefault *impl); @@ -437,9 +415,6 @@ static GSList *search_get_selected_files (GtkFileChooserDefault *impl); static void search_entry_activate_cb (GtkEntry *entry, gpointer data); static void settings_load (GtkFileChooserDefault *impl); -static void search_get_valid_child_iter (GtkFileChooserDefault *impl, - GtkTreeIter *child_iter, - GtkTreeIter *iter); static void recent_stop_loading (GtkFileChooserDefault *impl); static void recent_clear_model (GtkFileChooserDefault *impl, @@ -447,9 +422,6 @@ static void recent_clear_model (GtkFileChooserDefault *impl, static gboolean recent_should_respond (GtkFileChooserDefault *impl); static void recent_switch_to_browse_mode (GtkFileChooserDefault *impl); static GSList * recent_get_selected_files (GtkFileChooserDefault *impl); -static void recent_get_valid_child_iter (GtkFileChooserDefault *impl, - GtkTreeIter *child_iter, - GtkTreeIter *iter); static void set_file_system_backend (GtkFileChooserDefault *impl); static void unset_file_system_backend (GtkFileChooserDefault *impl); @@ -484,56 +456,6 @@ static GtkTreeModel *shortcuts_pane_model_filter_new (GtkFileChooserDefault *imp GtkTreeModel *child_model, GtkTreePath *root); - -typedef struct { - GtkTreeModelSort parent; - - GtkFileChooserDefault *impl; -} RecentModelSort; - -typedef struct { - GtkTreeModelSortClass parent_class; -} RecentModelSortClass; - -#define RECENT_MODEL_SORT_TYPE (_recent_model_sort_get_type ()) -#define RECENT_MODEL_SORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), RECENT_MODEL_SORT_TYPE, RecentModelSort)) - -static void recent_model_sort_drag_source_iface_init (GtkTreeDragSourceIface *iface); - -G_DEFINE_TYPE_WITH_CODE (RecentModelSort, - _recent_model_sort, - GTK_TYPE_TREE_MODEL_SORT, - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, - recent_model_sort_drag_source_iface_init)); - -static GtkTreeModel *recent_model_sort_new (GtkFileChooserDefault *impl, - GtkTreeModel *child_model); - - -typedef struct { - GtkTreeModelSort parent; - - GtkFileChooserDefault *impl; -} SearchModelSort; - -typedef struct { - GtkTreeModelSortClass parent_class; -} SearchModelSortClass; - -#define SEARCH_MODEL_SORT_TYPE (_search_model_sort_get_type ()) -#define SEARCH_MODEL_SORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SEARCH_MODEL_SORT_TYPE, SearchModelSort)) - -static void search_model_sort_drag_source_iface_init (GtkTreeDragSourceIface *iface); - -G_DEFINE_TYPE_WITH_CODE (SearchModelSort, - _search_model_sort, - GTK_TYPE_TREE_MODEL_SORT, - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, - search_model_sort_drag_source_iface_init)); - -static GtkTreeModel *search_model_sort_new (GtkFileChooserDefault *impl, - GtkTreeModel *child_model); - G_DEFINE_TYPE_WITH_CODE (GtkFileChooserDefault, _gtk_file_chooser_default, GTK_TYPE_VBOX, @@ -798,9 +720,10 @@ _gtk_file_chooser_default_init (GtkFileChooserDefault *impl) impl->pending_select_files = NULL; impl->location_mode = LOCATION_MODE_PATH_BAR; impl->operation_mode = OPERATION_MODE_BROWSE; - impl->sort_column = FILE_LIST_COL_NAME; + impl->sort_column = MODEL_COL_NAME; impl->sort_order = GTK_SORT_ASCENDING; impl->recent_manager = gtk_recent_manager_get_default (); + impl->create_folders = TRUE; gtk_box_set_spacing (GTK_BOX (impl), 12); @@ -894,14 +817,11 @@ store_selection_foreach (GtkTreeModel *model, gpointer data) { GtkFileChooserDefault *impl; - GtkTreeIter child_iter; GFile *file; impl = GTK_FILE_CHOOSER_DEFAULT (data); - gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, iter); - - file = _gtk_file_system_model_get_file (impl->browse_files_model, &child_iter); + file = _gtk_file_system_model_get_file (GTK_FILE_SYSTEM_MODEL (model), iter); pending_select_files_add (impl, file); } @@ -965,9 +885,6 @@ gtk_file_chooser_default_finalize (GObject *object) if (impl->browse_files_model) g_object_unref (impl->browse_files_model); - if (impl->sort_model) - g_object_unref (impl->sort_model); - search_clear_model (impl, FALSE); recent_clear_model (impl, FALSE); @@ -2637,36 +2554,17 @@ add_bookmark_foreach_cb (GtkTreeModel *model, gpointer data) { GtkFileChooserDefault *impl; - GtkFileSystemModel *fs_model; - GtkTreeIter child_iter; GFile *file; impl = (GtkFileChooserDefault *) data; - switch (impl->operation_mode) - { - case OPERATION_MODE_BROWSE: - fs_model = impl->browse_files_model; - gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, iter); - file = _gtk_file_system_model_get_file (fs_model, &child_iter); - break; - - case OPERATION_MODE_SEARCH: - search_get_valid_child_iter (impl, &child_iter, iter); - gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter, - SEARCH_MODEL_COL_FILE, &file, - -1); - break; - - case OPERATION_MODE_RECENT: - recent_get_valid_child_iter (impl, &child_iter, iter); - gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter, - RECENT_MODEL_COL_FILE, &file, - -1); - break; - } + gtk_tree_model_get (model, iter, + MODEL_COL_FILE, &file, + -1); shortcuts_add_bookmark_from_file (impl, file, -1); + + g_object_unref (file); } /* Adds a bookmark from the currently selected item in the file list */ @@ -2770,35 +2668,21 @@ selection_check_foreach_cb (GtkTreeModel *model, gpointer data) { struct selection_check_closure *closure; - GtkTreeIter child_iter; - GFileInfo *info; gboolean is_folder; + GFile *file; - closure = data; - closure->num_selected++; + gtk_tree_model_get (model, iter, + MODEL_COL_FILE, &file, + MODEL_COL_IS_FOLDER, &is_folder, + -1); - switch (closure->impl->operation_mode) - { - case OPERATION_MODE_BROWSE: - gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter); - info = _gtk_file_system_model_get_info (closure->impl->browse_files_model, &child_iter); - is_folder = info ? (_gtk_file_info_consider_as_directory (info)) : FALSE; - break; + if (file == NULL) + return; - case OPERATION_MODE_SEARCH: - search_get_valid_child_iter (closure->impl, &child_iter, iter); - gtk_tree_model_get (GTK_TREE_MODEL (closure->impl->search_model), &child_iter, - SEARCH_MODEL_COL_IS_FOLDER, &is_folder, - -1); - break; + g_object_unref (file); - case OPERATION_MODE_RECENT: - recent_get_valid_child_iter (closure->impl, &child_iter, iter); - gtk_tree_model_get (GTK_TREE_MODEL (closure->impl->recent_model), &child_iter, - RECENT_MODEL_COL_IS_FOLDER, &is_folder, - -1); - break; - } + closure = data; + closure->num_selected++; closure->all_folders = closure->all_folders && is_folder; closure->all_files = closure->all_files && !is_folder; @@ -2847,32 +2731,18 @@ get_selected_file_foreach_cb (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) { - struct get_selected_file_closure *closure; - GtkTreeIter child_iter; + struct get_selected_file_closure *closure = data; - closure = data; - - switch (closure->impl->operation_mode) + if (closure->file) { - case OPERATION_MODE_BROWSE: - gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter); - closure->file = _gtk_file_system_model_get_file (closure->impl->browse_files_model, &child_iter); - break; - - case OPERATION_MODE_SEARCH: - search_get_valid_child_iter (closure->impl, &child_iter, iter); - gtk_tree_model_get (GTK_TREE_MODEL (closure->impl->search_model), &child_iter, - SEARCH_MODEL_COL_FILE, &closure->file, - -1); - break; - - case OPERATION_MODE_RECENT: - recent_get_valid_child_iter (closure->impl, &child_iter, iter); - gtk_tree_model_get (GTK_TREE_MODEL (closure->impl->recent_model), &child_iter, - RECENT_MODEL_COL_FILE, &closure->file, - -1); - break; + /* Just in case this function gets run more than once with a multiple selection; we only care about one file */ + g_object_unref (closure->file); + closure->file = NULL; } + + gtk_tree_model_get (model, iter, + MODEL_COL_FILE, &closure->file, /* this will give us a reffed file */ + -1); } /* Returns a selected path from the file list */ @@ -2905,40 +2775,18 @@ update_tooltip (GtkTreeModel *model, gpointer data) { UpdateTooltipData *udata = data; - GtkTreeIter child_iter; - GFileInfo *info; if (udata->tip == NULL) { - const gchar *display_name; - - switch (udata->impl->operation_mode) - { - case OPERATION_MODE_BROWSE: - gtk_tree_model_sort_convert_iter_to_child_iter (udata->impl->sort_model, - &child_iter, - iter); - info = _gtk_file_system_model_get_info (udata->impl->browse_files_model, &child_iter); - display_name = g_file_info_get_display_name (info); - break; - - case OPERATION_MODE_SEARCH: - search_get_valid_child_iter (udata->impl, &child_iter, iter); - gtk_tree_model_get (GTK_TREE_MODEL (udata->impl->search_model), &child_iter, - SEARCH_MODEL_COL_DISPLAY_NAME, &display_name, - -1); - break; + gchar *display_name; - case OPERATION_MODE_RECENT: - recent_get_valid_child_iter (udata->impl, &child_iter, iter); - gtk_tree_model_get (GTK_TREE_MODEL (udata->impl->recent_model), &child_iter, - RECENT_MODEL_COL_DISPLAY_NAME, &display_name, - -1); - break; - } + gtk_tree_model_get (model, iter, + MODEL_COL_NAME, &display_name, + -1); udata->tip = g_strdup_printf (_("Add the folder '%s' to the bookmarks"), display_name); + g_free (display_name); } } @@ -2964,7 +2812,9 @@ bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl) GFile *file; file = get_selected_file (impl); - active = all_folders && (shortcut_find_position (impl, file) == -1); + active = file && all_folders && (shortcut_find_position (impl, file) == -1); + if (file) + g_object_unref (file); } else active = all_folders; @@ -3007,24 +2857,27 @@ bookmarks_check_remove_sensitivity (GtkFileChooserDefault *impl) GtkTreeIter iter; gboolean removable = FALSE; gchar *name = NULL; + gchar *tip; if (shortcuts_get_selected (impl, &iter)) - gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, - SHORTCUTS_COL_REMOVABLE, &removable, - SHORTCUTS_COL_NAME, &name, - -1); - - gtk_widget_set_sensitive (impl->browse_shortcuts_remove_button, removable); - - if (removable) { - gchar *tip; + gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, + SHORTCUTS_COL_REMOVABLE, &removable, + SHORTCUTS_COL_NAME, &name, + -1); + gtk_widget_set_sensitive (impl->browse_shortcuts_remove_button, removable); + + if (removable) + tip = g_strdup_printf (_("Remove the bookmark '%s'"), name); + else + tip = g_strdup_printf (_("Bookmark '%s' cannot be removed"), name); - tip = g_strdup_printf (_("Remove the bookmark '%s'"), name); gtk_widget_set_tooltip_text (impl->browse_shortcuts_remove_button, tip); g_free (tip); } - + else + gtk_widget_set_tooltip_text (impl->browse_shortcuts_remove_button, + _("Remove the selected bookmark")); g_free (name); } @@ -3649,26 +3502,34 @@ shortcuts_row_separator_func (GtkTreeModel *model, return shortcut_type == SHORTCUT_TYPE_SEPARATOR; } -/* Since GtkTreeView has a keybinding attached to '/', we need to catch - * keypresses before the TreeView gets them. - */ static gboolean -tree_view_keybinding_cb (GtkWidget *tree_view, - GdkEventKey *event, - GtkFileChooserDefault *impl) +shortcuts_key_press_event_after_cb (GtkWidget *tree_view, + GdkEventKey *event, + GtkFileChooserDefault *impl) { - if ((event->keyval == GDK_slash - || event->keyval == GDK_KP_Divide -#ifdef G_OS_UNIX - || event->keyval == GDK_asciitilde -#endif - ) && ! (event->state & (~GDK_SHIFT_MASK & gtk_accelerator_get_default_mod_mask ()))) + GtkWidget *entry; + + /* don't screw up focus switching with Tab */ + if (event->keyval == GDK_Tab + || event->keyval == GDK_KP_Tab + || event->keyval == GDK_ISO_Left_Tab + || event->length < 1) + return FALSE; + + if (impl->location_entry) + entry = impl->location_entry; + else if (impl->search_entry) + entry = impl->search_entry; + else + entry = NULL; + + if (entry) { - location_popup_handler (impl, event->string); - return TRUE; + gtk_widget_grab_focus (entry); + return gtk_widget_event (entry, (GdkEvent *) event); } - - return FALSE; + else + return FALSE; } /* Callback used when the file list's popup menu is detached */ @@ -3883,11 +3744,28 @@ shortcuts_list_create (GtkFileChooserDefault *impl) /* Tree */ impl->browse_shortcuts_tree_view = gtk_tree_view_new (); + gtk_tree_view_set_enable_search (GTK_TREE_VIEW (impl->browse_shortcuts_tree_view), FALSE); #ifdef PROFILE_FILE_CHOOSER g_object_set_data (G_OBJECT (impl->browse_shortcuts_tree_view), "fmq-name", "shortcuts"); #endif - g_signal_connect (impl->browse_shortcuts_tree_view, "key-press-event", - G_CALLBACK (tree_view_keybinding_cb), impl); + + /* Connect "after" to key-press-event on the shortcuts pane. We want this action to be possible: + * + * 1. user brings up a SAVE dialog + * 2. user clicks on a shortcut in the shortcuts pane + * 3. user starts typing a filename + * + * Normally, the user's typing would be ignored, as the shortcuts treeview doesn't + * support interactive search. However, we'd rather focus the location entry + * so that the user can type *there*. + * + * To preserve keyboard navigation in the shortcuts pane, we don't focus the + * filename entry if one clicks on a shortcut; rather, we focus the entry only + * if the user starts typing while the focus is in the shortcuts pane. + */ + g_signal_connect_after (impl->browse_shortcuts_tree_view, "key-press-event", + G_CALLBACK (shortcuts_key_press_event_after_cb), impl); + g_signal_connect (impl->browse_shortcuts_tree_view, "popup-menu", G_CALLBACK (shortcuts_popup_menu_cb), impl); g_signal_connect (impl->browse_shortcuts_tree_view, "button-press-event", @@ -4030,14 +3908,28 @@ shortcuts_pane_create (GtkFileChooserDefault *impl, return vbox; } +static gboolean +key_is_left_or_right (GdkEventKey *event) +{ + guint modifiers; + + modifiers = gtk_accelerator_get_default_mod_mask (); + + return ((event->keyval == GDK_Right + || event->keyval == GDK_KP_Right + || event->keyval == GDK_Left + || event->keyval == GDK_KP_Left) + && (event->state & modifiers) == 0); +} + /* Handles key press events on the file list, so that we can trap Enter to * activate the default button on our own. Also, checks to see if '/' has been * pressed. See comment by tree_view_keybinding_cb() for more details. */ static gboolean -trap_activate_cb (GtkWidget *widget, - GdkEventKey *event, - gpointer data) +browse_files_key_press_event_cb (GtkWidget *widget, + GdkEventKey *event, + gpointer data) { GtkFileChooserDefault *impl; int modifiers; @@ -4057,6 +3949,12 @@ trap_activate_cb (GtkWidget *widget, return TRUE; } + if (key_is_left_or_right (event)) + { + gtk_widget_grab_focus (impl->browse_shortcuts_tree_view); + return TRUE; + } + if ((event->keyval == GDK_Return || event->keyval == GDK_ISO_Enter || event->keyval == GDK_KP_Enter @@ -4122,9 +4020,8 @@ show_size_column_toggled_cb (GtkCheckMenuItem *item, { impl->show_size_column = gtk_check_menu_item_get_active (item); - if (impl->list_size_column) - gtk_tree_view_column_set_visible (impl->list_size_column, - impl->show_size_column); + gtk_tree_view_column_set_visible (impl->list_size_column, + impl->show_size_column); } /* Shows an error dialog about not being able to select a dragged file */ @@ -4461,64 +4358,13 @@ typedef struct { gint model_column; } ColumnMap; -/* Sigh. Each operation mode has different sort column IDs. This table - * translates between them. - */ -static const ColumnMap column_map[] = { - { OPERATION_MODE_BROWSE, FILE_LIST_COL_NAME, FILE_LIST_COL_NAME }, - { OPERATION_MODE_BROWSE, FILE_LIST_COL_SIZE, FILE_LIST_COL_SIZE }, - { OPERATION_MODE_BROWSE, FILE_LIST_COL_MTIME, FILE_LIST_COL_MTIME }, - - { OPERATION_MODE_SEARCH, FILE_LIST_COL_NAME, SEARCH_MODEL_COL_FILE }, - { OPERATION_MODE_SEARCH, FILE_LIST_COL_SIZE, SEARCH_MODEL_COL_SIZE }, - { OPERATION_MODE_SEARCH, FILE_LIST_COL_MTIME, SEARCH_MODEL_COL_MTIME }, - - { OPERATION_MODE_RECENT, FILE_LIST_COL_NAME, RECENT_MODEL_COL_FILE }, - { OPERATION_MODE_RECENT, FILE_LIST_COL_SIZE, 0 }, - { OPERATION_MODE_RECENT, FILE_LIST_COL_MTIME, RECENT_MODEL_COL_INFO } -}; - -static gint -general_column_to_model_column (GtkFileChooserDefault *impl, gint general_column) -{ - int i; - - for (i = 0; i < G_N_ELEMENTS (column_map); i++) - if (column_map[i].operation_mode == impl->operation_mode - && column_map[i].general_column == general_column) - return column_map[i].model_column; - - g_assert_not_reached (); - return 0; -} - -static gint -model_column_to_general_column (GtkFileChooserDefault *impl, gint model_column) -{ - int i; - - for (i = 0; i < G_N_ELEMENTS (column_map); i++) - if (column_map[i].operation_mode == impl->operation_mode - && column_map[i].model_column == model_column) - return column_map[i].general_column; - - g_assert_not_reached (); - return 0; -} - /* Sets the sort column IDs for the file list based on the operation mode */ static void file_list_set_sort_column_ids (GtkFileChooserDefault *impl) { - int name_id, mtime_id, size_id; - - name_id = general_column_to_model_column (impl, FILE_LIST_COL_NAME); - mtime_id = general_column_to_model_column (impl, FILE_LIST_COL_MTIME); - size_id = general_column_to_model_column (impl, FILE_LIST_COL_SIZE); - - gtk_tree_view_column_set_sort_column_id (impl->list_name_column, name_id); - gtk_tree_view_column_set_sort_column_id (impl->list_mtime_column, mtime_id); - gtk_tree_view_column_set_sort_column_id (impl->list_size_column, size_id); + gtk_tree_view_column_set_sort_column_id (impl->list_name_column, MODEL_COL_NAME); + gtk_tree_view_column_set_sort_column_id (impl->list_mtime_column, MODEL_COL_MTIME); + gtk_tree_view_column_set_sort_column_id (impl->list_size_column, MODEL_COL_SIZE); } static gboolean @@ -4530,8 +4376,9 @@ file_list_query_tooltip_cb (GtkWidget *widget, gpointer user_data) { GtkFileChooserDefault *impl = user_data; - GtkTreeIter iter, child_iter; - GtkTreePath *path = NULL; + GtkTreeModel *model; + GtkTreePath *path; + GtkTreeIter iter; GFile *file; gchar *filename; @@ -4539,48 +4386,17 @@ file_list_query_tooltip_cb (GtkWidget *widget, return FALSE; - gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (impl->browse_files_tree_view), - &x, &y, - keyboard_tip, - NULL, &path, NULL); - - if (!path) + if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (impl->browse_files_tree_view), + &x, &y, + keyboard_tip, + &model, &path, &iter)) return FALSE; + + gtk_tree_model_get (model, &iter, + MODEL_COL_FILE, &file, + -1); - switch (impl->operation_mode) - { - case OPERATION_MODE_SEARCH: - if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->search_model_sort), &iter, path)) - { - gtk_tree_path_free (path); - return FALSE; - } - - search_get_valid_child_iter (impl, &child_iter, &iter); - gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter, - SEARCH_MODEL_COL_FILE, &file, - -1); - break; - - case OPERATION_MODE_RECENT: - if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->recent_model_sort), &iter, path)) - { - gtk_tree_path_free (path); - return FALSE; - } - - recent_get_valid_child_iter (impl, &child_iter, &iter); - gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter, - RECENT_MODEL_COL_FILE, &file, - -1); - break; - - case OPERATION_MODE_BROWSE: - g_assert_not_reached (); - return FALSE; - } - - if (!file) + if (file == NULL) { gtk_tree_path_free (path); return FALSE; @@ -4593,11 +4409,20 @@ file_list_query_tooltip_cb (GtkWidget *widget, path); g_free (filename); + g_object_unref (file); gtk_tree_path_free (path); return TRUE; } +static void +set_icon_cell_renderer_fixed_size (GtkFileChooserDefault *impl, GtkCellRenderer *renderer) +{ + gtk_cell_renderer_set_fixed_size (renderer, + renderer->xpad * 2 + impl->icon_size, + renderer->ypad * 2 + impl->icon_size); +} + /* Creates the widgets for the file list */ static GtkWidget * create_file_list (GtkFileChooserDefault *impl) @@ -4635,7 +4460,7 @@ create_file_list (GtkFileChooserDefault *impl) g_signal_connect (impl->browse_files_tree_view, "row-activated", G_CALLBACK (list_row_activated), impl); g_signal_connect (impl->browse_files_tree_view, "key-press-event", - G_CALLBACK (trap_activate_cb), impl); + G_CALLBACK (browse_files_key_press_event_cb), impl); g_signal_connect (impl->browse_files_tree_view, "popup-menu", G_CALLBACK (list_popup_menu_cb), impl); g_signal_connect (impl->browse_files_tree_view, "button-press-event", @@ -4665,6 +4490,8 @@ create_file_list (GtkFileChooserDefault *impl) g_signal_connect (selection, "changed", G_CALLBACK (list_selection_changed), impl); + /* Keep the column order in sync with update_cell_renderer_attributes() */ + /* Filename column */ impl->list_name_column = gtk_tree_view_column_new (); @@ -4673,9 +4500,9 @@ create_file_list (GtkFileChooserDefault *impl) gtk_tree_view_column_set_title (impl->list_name_column, _("Name")); renderer = gtk_cell_renderer_pixbuf_new (); + /* We set a fixed size so that we get an empty slot even if no icons are loaded yet */ + set_icon_cell_renderer_fixed_size (impl, renderer); gtk_tree_view_column_pack_start (impl->list_name_column, renderer, FALSE); - gtk_tree_view_column_set_cell_data_func (impl->list_name_column, renderer, - list_icon_data_func, impl, NULL); impl->list_name_renderer = gtk_cell_renderer_text_new (); g_object_set (impl->list_name_renderer, @@ -4686,8 +4513,6 @@ create_file_list (GtkFileChooserDefault *impl) g_signal_connect (impl->list_name_renderer, "editing-canceled", G_CALLBACK (renderer_editing_canceled_cb), impl); gtk_tree_view_column_pack_start (impl->list_name_column, impl->list_name_renderer, TRUE); - gtk_tree_view_column_set_cell_data_func (impl->list_name_column, impl->list_name_renderer, - list_name_data_func, impl, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), impl->list_name_column); @@ -4702,8 +4527,6 @@ create_file_list (GtkFileChooserDefault *impl) "alignment", PANGO_ALIGN_RIGHT, NULL); gtk_tree_view_column_pack_start (column, renderer, TRUE); /* bug: it doesn't expand */ - gtk_tree_view_column_set_cell_data_func (column, renderer, - list_size_data_func, impl, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column); impl->list_size_column = column; @@ -4715,12 +4538,11 @@ create_file_list (GtkFileChooserDefault *impl) renderer = gtk_cell_renderer_text_new (); gtk_tree_view_column_pack_start (column, renderer, TRUE); - gtk_tree_view_column_set_cell_data_func (column, renderer, - list_mtime_data_func, impl, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (impl->browse_files_tree_view), column); impl->list_mtime_column = column; file_list_set_sort_column_ids (impl); + update_cell_renderer_attributes (impl); gtk_widget_show_all (swin); @@ -4813,6 +4635,39 @@ save_folder_combo_changed_cb (GtkComboBox *combo, } } +static void +save_folder_update_tooltip (GtkComboBox *combo, + GtkFileChooserDefault *impl) +{ + GtkTreeIter iter; + gchar *tooltip; + + tooltip = NULL; + + if (gtk_combo_box_get_active_iter (combo, &iter)) + { + GtkTreeIter child_iter; + gpointer col_data; + ShortcutType shortcut_type; + + gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (impl->shortcuts_combo_filter_model), + &child_iter, + &iter); + gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &child_iter, + SHORTCUTS_COL_DATA, &col_data, + SHORTCUTS_COL_TYPE, &shortcut_type, + -1); + + if (shortcut_type == SHORTCUT_TYPE_FILE) + tooltip = g_file_get_parse_name (G_FILE (col_data)); + } + + gtk_widget_set_tooltip_text (GTK_WIDGET (combo), tooltip); + gtk_widget_set_has_tooltip (GTK_WIDGET (combo), + gtk_widget_get_sensitive (GTK_WIDGET (combo))); + g_free (tooltip); +} + /* Filter function used to filter out the Search item and its separator. * Used for the "Save in folder" combo box, so that these items do not appear in it. */ @@ -4904,6 +4759,8 @@ save_folder_combo_create (GtkFileChooserDefault *impl) g_signal_connect (combo, "changed", G_CALLBACK (save_folder_combo_changed_cb), impl); + g_signal_connect (combo, "changed", + G_CALLBACK (save_folder_update_tooltip), impl); return combo; } @@ -5512,12 +5369,14 @@ update_appearance (GtkFileChooserDefault *impl) { gtk_widget_set_sensitive (impl->save_folder_label, FALSE); gtk_widget_set_sensitive (impl->save_folder_combo, FALSE); + gtk_widget_set_has_tooltip (impl->save_folder_combo, FALSE); gtk_widget_show (impl->browse_widgets); } else { gtk_widget_set_sensitive (impl->save_folder_label, TRUE); gtk_widget_set_sensitive (impl->save_folder_combo, TRUE); + gtk_widget_set_has_tooltip (impl->save_folder_combo, TRUE); gtk_widget_hide (impl->browse_widgets); } @@ -5540,7 +5399,7 @@ update_appearance (GtkFileChooserDefault *impl) if (impl->location_entry) _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->action); - if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN) + if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN || !impl->create_folders) gtk_widget_hide (impl->browse_new_folder_button); else gtk_widget_show (impl->browse_new_folder_button); @@ -5582,6 +5441,7 @@ gtk_file_chooser_default_set_property (GObject *object, set_select_multiple (impl, FALSE, TRUE); } impl->action = action; + update_cell_renderer_attributes (impl); update_appearance (impl); settings_load (impl); } @@ -5655,6 +5515,14 @@ gtk_file_chooser_default_set_property (GObject *object, } break; + case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS: + { + gboolean create_folders = g_value_get_boolean (value); + impl->create_folders = create_folders; + update_appearance (impl); + } + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -5711,6 +5579,10 @@ gtk_file_chooser_default_get_property (GObject *object, g_value_set_boolean (value, impl->do_overwrite_confirmation); break; + case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS: + g_value_set_boolean (value, impl->create_folders); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -5793,12 +5665,6 @@ gtk_file_chooser_default_dispose (GObject *object) impl->update_current_folder_cancellable = NULL; } - if (impl->show_and_select_files_cancellable) - { - g_cancellable_cancel (impl->show_and_select_files_cancellable); - impl->show_and_select_files_cancellable = NULL; - } - if (impl->should_respond_get_info_cancellable) { g_cancellable_cancel (impl->should_respond_get_info_cancellable); @@ -5889,6 +5755,8 @@ change_icon_theme (GtkFileChooserDefault *impl) { GtkSettings *settings; gint width, height; + GtkCellRenderer *renderer; + GList *cells; profile_start ("start", NULL); @@ -5900,6 +5768,14 @@ change_icon_theme (GtkFileChooserDefault *impl) impl->icon_size = FALLBACK_ICON_SIZE; shortcuts_reload_icons (impl); + /* the first cell in the first column is the icon column, and we have a fixed size there */ + cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT ( + gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_files_tree_view), 0))); + renderer = GTK_CELL_RENDERER (cells->data); + set_icon_cell_renderer_fixed_size (impl, renderer); + g_list_free (cells); + if (impl->browse_files_model) + _gtk_file_system_model_clear_cache (impl->browse_files_model, MODEL_COL_PIXBUF); gtk_widget_queue_resize (impl->browse_files_tree_view); profile_end ("end", NULL); @@ -6006,79 +5882,19 @@ gtk_file_chooser_default_size_allocate (GtkWidget *widget, GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->size_allocate (widget, allocation); } -static gboolean -get_is_file_filtered (GtkFileChooserDefault *impl, - GFile *file, - GFileInfo *file_info) -{ - GtkFileFilterInfo filter_info; - GtkFileFilterFlags needed; - gboolean result; - - if (!impl->current_filter) - return FALSE; - - filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE; - - needed = gtk_file_filter_get_needed (impl->current_filter); - - filter_info.display_name = g_file_info_get_display_name (file_info); - filter_info.mime_type = g_content_type_get_mime_type (g_file_info_get_content_type (file_info)); - - if (needed & GTK_FILE_FILTER_FILENAME) - { - filter_info.filename = g_file_get_path (file); - if (filter_info.filename) - filter_info.contains |= GTK_FILE_FILTER_FILENAME; - } - else - filter_info.filename = NULL; - - if (needed & GTK_FILE_FILTER_URI) - { - filter_info.uri = g_file_get_uri (file); - if (filter_info.uri) - filter_info.contains |= GTK_FILE_FILTER_URI; - } - else - filter_info.uri = NULL; - - result = gtk_file_filter_filter (impl->current_filter, &filter_info); - - g_free ((gchar *)filter_info.filename); - g_free ((gchar *)filter_info.uri); - g_free ((gchar *)filter_info.mime_type); - - return !result; -} - static void set_sort_column (GtkFileChooserDefault *impl) { GtkTreeSortable *sortable; - switch (impl->operation_mode) - { - case OPERATION_MODE_BROWSE: - sortable = GTK_TREE_SORTABLE (impl->sort_model); - break; - - case OPERATION_MODE_SEARCH: - sortable = GTK_TREE_SORTABLE (impl->search_model_sort); - break; - - case OPERATION_MODE_RECENT: - sortable = GTK_TREE_SORTABLE (impl->recent_model_sort); - break; - - default: - g_assert_not_reached (); - return; - } + sortable = GTK_TREE_SORTABLE (gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view))); + /* can happen when we're still populating the model */ + if (sortable == NULL) + return; gtk_tree_sortable_set_sort_column_id (sortable, - general_column_to_model_column (impl, impl->sort_column), - impl->sort_order); + impl->sort_column, + impl->sort_order); } static void @@ -6112,8 +5928,7 @@ settings_load (GtkFileChooserDefault *impl) gtk_expander_set_expanded (GTK_EXPANDER (impl->save_expander), expand_folders); impl->show_size_column = show_size_column; - if (impl->list_size_column) - gtk_tree_view_column_set_visible (impl->list_size_column, show_size_column); + gtk_tree_view_column_set_visible (impl->list_size_column, show_size_column); impl->sort_column = sort_column; impl->sort_order = sort_order; @@ -6254,62 +6069,20 @@ gtk_file_chooser_default_unmap (GtkWidget *widget) impl->reload_state = RELOAD_WAS_UNMAPPED; } -static gboolean -list_model_filter_func (GtkFileSystemModel *model, - GFile *file, - GFileInfo *file_info, - gpointer user_data) -{ - GtkFileChooserDefault *impl = user_data; - - if (!impl->current_filter) - return TRUE; - - if (_gtk_file_info_consider_as_directory (file_info)) - return TRUE; - - return !get_is_file_filtered (impl, file, file_info); -} - static void install_list_model_filter (GtkFileChooserDefault *impl) { - GtkFileSystemModelFilter filter; - gpointer data; - - g_assert (impl->browse_files_model != NULL); - - if (impl->current_filter) - { - filter = list_model_filter_func; - data = impl; - } - else - { - filter = NULL; - data = NULL; - } - _gtk_file_system_model_set_filter (impl->browse_files_model, - filter, - data); + impl->current_filter); } #define COMPARE_DIRECTORIES \ GtkFileChooserDefault *impl = user_data; \ - GFileInfo *info_a = _gtk_file_system_model_get_info (impl->browse_files_model, a); \ - GFileInfo *info_b = _gtk_file_system_model_get_info (impl->browse_files_model, b); \ + GtkFileSystemModel *fs_model = GTK_FILE_SYSTEM_MODEL (model); \ gboolean dir_a, dir_b; \ \ - if (info_a) \ - dir_a = _gtk_file_info_consider_as_directory (info_a); \ - else \ - return impl->list_sort_ascending ? -1 : 1; \ - \ - if (info_b) \ - dir_b = _gtk_file_info_consider_as_directory (info_b); \ - else \ - return impl->list_sort_ascending ? 1 : -1; \ + dir_a = g_value_get_boolean (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_IS_FOLDER)); \ + dir_b = g_value_get_boolean (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_IS_FOLDER)); \ \ if (dir_a != dir_b) \ return impl->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first */ @@ -6324,15 +6097,20 @@ name_sort_func (GtkTreeModel *model, COMPARE_DIRECTORIES; else { - gchar *key_a, *key_b; + const char *key_a, *key_b; gint result; - key_a = g_utf8_collate_key_for_filename (g_file_info_get_display_name (info_a), -1); - key_b = g_utf8_collate_key_for_filename (g_file_info_get_display_name (info_b), -1); - result = strcmp (key_a, key_b); + key_a = g_value_get_string (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_NAME_COLLATED)); + key_b = g_value_get_string (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_NAME_COLLATED)); - g_free (key_a); - g_free (key_b); + if (key_a && key_b) + result = strcmp (key_a, key_b); + else if (key_a) + result = 1; + else if (key_b) + result = -1; + else + result = 0; return result; } @@ -6348,8 +6126,10 @@ size_sort_func (GtkTreeModel *model, COMPARE_DIRECTORIES; else { - goffset size_a = g_file_info_get_size (info_a); - goffset size_b = g_file_info_get_size (info_b); + gint64 size_a, size_b; + + size_a = g_value_get_int64 (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_SIZE)); + size_b = g_value_get_int64 (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_SIZE)); return size_a > size_b ? -1 : (size_a == size_b ? 0 : 1); } @@ -6365,12 +6145,12 @@ mtime_sort_func (GtkTreeModel *model, COMPARE_DIRECTORIES; else { - GTimeVal ta, tb; + glong ta, tb; - g_file_info_get_modification_time (info_a, &ta); - g_file_info_get_modification_time (info_b, &tb); + ta = g_value_get_long (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_MTIME)); + tb = g_value_get_long (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_MTIME)); - return ta.tv_sec > tb.tv_sec ? -1 : (ta.tv_sec == tb.tv_sec ? 0 : 1); + return ta > tb ? -1 : (ta == tb ? 0 : 1); } } @@ -6387,7 +6167,7 @@ list_sort_column_changed_cb (GtkTreeSortable *sortable, if (gtk_tree_sortable_get_sort_column_id (sortable, &sort_column_id, &sort_type)) { impl->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING); - impl->sort_column = model_column_to_general_column (impl, sort_column_id); + impl->sort_column = sort_column_id; impl->sort_order = sort_type; } } @@ -6425,28 +6205,16 @@ load_set_model (GtkFileChooserDefault *impl) profile_start ("start", NULL); g_assert (impl->browse_files_model != NULL); - g_assert (impl->sort_model == NULL); - - profile_msg (" gtk_tree_model_sort_new_with_model start", NULL); - impl->sort_model = (GtkTreeModelSort *)gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (impl->browse_files_model)); - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_NAME, name_sort_func, impl, NULL); - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_SIZE, size_sort_func, impl, NULL); - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->sort_model), FILE_LIST_COL_MTIME, mtime_sort_func, impl, NULL); - gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->sort_model), NULL, NULL, NULL); - set_sort_column (impl); - impl->list_sort_ascending = TRUE; - profile_msg (" gtk_tree_model_sort_new_with_model end", NULL); - - g_signal_connect (impl->sort_model, "sort-column-changed", - G_CALLBACK (list_sort_column_changed_cb), impl); profile_msg (" gtk_tree_view_set_model start", NULL); gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), - GTK_TREE_MODEL (impl->sort_model)); + GTK_TREE_MODEL (impl->browse_files_model)); gtk_tree_view_columns_autosize (GTK_TREE_VIEW (impl->browse_files_tree_view)); gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->browse_files_tree_view), - GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME); + MODEL_COL_NAME); + set_sort_column (impl); profile_msg (" gtk_tree_view_set_model end", NULL); + impl->list_sort_ascending = TRUE; profile_end ("end", NULL); } @@ -6511,11 +6279,12 @@ browse_files_select_first_row (GtkFileChooserDefault *impl) GtkTreeIter dummy_iter; GtkTreeModel *tree_model; - if (!impl->sort_model) + tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view)); + + if (!tree_model) return; path = gtk_tree_path_new_from_indices (0, -1); - tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view)); /* If the list is empty, do nothing. */ if (gtk_tree_model_get_iter (tree_model, &dummy_iter, path)) @@ -6562,144 +6331,58 @@ browse_files_center_selected_row (GtkFileChooserDefault *impl) gtk_tree_selection_selected_foreach (selection, center_selected_row_foreach_cb, &closure); } -struct ShowAndSelectPathsData -{ - GtkFileChooserDefault *impl; - GSList *files; -}; - -static void -show_and_select_files_finished_loading (GtkFolder *folder, - gpointer user_data) +static gboolean +show_and_select_files (GtkFileChooserDefault *impl, + GSList *files) { - gboolean have_hidden; - gboolean have_filtered; - GSList *l; - struct ShowAndSelectPathsData *data = user_data; - - have_hidden = FALSE; - have_filtered = FALSE; - - for (l = data->files; l; l = l->next) - { - GFile *file; - GFileInfo *info; - - file = l->data; - - info = _gtk_folder_get_info (folder, file); - if (info) - { - if (!have_hidden) - have_hidden = g_file_info_get_is_hidden (info) - || g_file_info_get_is_backup (info); - - if (!have_filtered) - have_filtered = (! _gtk_file_info_consider_as_directory (info)) && - get_is_file_filtered (data->impl, file, info); - - g_object_unref (info); - - if (have_hidden && have_filtered) - break; /* we now have all the information we need */ - } - } - - g_signal_handlers_disconnect_by_func (folder, - show_and_select_files_finished_loading, - user_data); - - if (have_hidden) - g_object_set (data->impl, "show-hidden", TRUE, NULL); + GtkTreeSelection *selection; + GtkFileSystemModel *fsmodel; + gboolean can_have_hidden, can_have_filtered, selected_a_file; + GSList *walk; - if (have_filtered) - set_current_filter (data->impl, NULL); + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view)); + fsmodel = GTK_FILE_SYSTEM_MODEL (gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view))); + can_have_hidden = !impl->show_hidden; + can_have_filtered = impl->current_filter != NULL; + selected_a_file = FALSE; - for (l = data->files; l; l = l->next) + for (walk = files; walk && (can_have_hidden || can_have_filtered); walk = walk->next) { - GFile *file; - - file = l->data; - _gtk_file_system_model_path_do (data->impl->browse_files_model, file, - select_func, data->impl); - } - - browse_files_center_selected_row (data->impl); - - g_object_unref (data->impl); - g_slist_foreach (data->files, (GFunc) g_object_unref, NULL); - g_slist_free (data->files); - g_free (data); -} - -static void -show_and_select_files_get_folder_cb (GCancellable *cancellable, - GtkFolder *folder, - const GError *error, - gpointer user_data) -{ - gboolean cancelled = g_cancellable_is_cancelled (cancellable); - struct ShowAndSelectPathsData *data = user_data; - - if (data->impl->show_and_select_files_cancellable != cancellable) - goto out; - - data->impl->show_and_select_files_cancellable = NULL; - - if (cancelled || error) - goto out; - - g_object_unref (cancellable); - - if (_gtk_folder_is_finished_loading (folder)) - show_and_select_files_finished_loading (folder, user_data); - else - g_signal_connect (folder, "finished-loading", - G_CALLBACK (show_and_select_files_finished_loading), - user_data); - - return; - -out: - g_object_unref (data->impl); - g_slist_foreach (data->files, (GFunc) g_object_unref, NULL); - g_slist_free (data->files); - g_free (data); + GFile *file = walk->data; + GtkTreeIter iter; - g_object_unref (cancellable); -} + if (!_gtk_file_system_model_get_iter_for_file (fsmodel, &iter, file)) + continue; -static gboolean -show_and_select_files (GtkFileChooserDefault *impl, - GFile *parent_file, - GSList *files, - GError **error) -{ - struct ShowAndSelectPathsData *info; + if (!_gtk_file_system_model_iter_is_visible (fsmodel, &iter)) + { + GFileInfo *info = _gtk_file_system_model_get_info (fsmodel, &iter); - profile_start ("start", NULL); + if (can_have_hidden && + (g_file_info_get_is_hidden (info) || + g_file_info_get_is_backup (info))) + { + g_object_set (impl, "show-hidden", TRUE, NULL); + can_have_hidden = FALSE; + } - if (!files) - { - profile_end ("end", NULL); - return TRUE; + if (can_have_filtered) + { + set_current_filter (impl, NULL); + can_have_filtered = FALSE; + } + } + + if (_gtk_file_system_model_iter_is_visible (fsmodel, &iter)) + { + gtk_tree_selection_select_iter (selection, &iter); + selected_a_file = TRUE; + } } - info = g_new (struct ShowAndSelectPathsData, 1); - info->impl = g_object_ref (impl); - info->files = g_slist_copy (files); - g_slist_foreach (info->files, (GFunc) g_object_ref, NULL); - - if (impl->show_and_select_files_cancellable) - g_cancellable_cancel (impl->show_and_select_files_cancellable); - - impl->show_and_select_files_cancellable = - _gtk_file_system_get_folder (impl->file_system, parent_file, - "standard::is-hidden,standard::is-backup,standard::type,standard::name,standard::content-type", - show_and_select_files_get_folder_cb, info); + browse_files_center_selected_row (impl); - profile_end ("end", NULL); - return TRUE; + return selected_a_file; } /* Processes the pending operation when a folder is finished loading */ @@ -6708,12 +6391,10 @@ pending_select_files_process (GtkFileChooserDefault *impl) { g_assert (impl->load_state == LOAD_FINISHED); g_assert (impl->browse_files_model != NULL); - g_assert (impl->sort_model != NULL); if (impl->pending_select_files) { - /* NULL GError */ - show_and_select_files (impl, impl->current_folder, impl->pending_select_files, NULL); + show_and_select_files (impl, impl->pending_select_files); pending_select_files_free (impl); browse_files_center_selected_row (impl); } @@ -6733,13 +6414,40 @@ pending_select_files_process (GtkFileChooserDefault *impl) g_assert (impl->pending_select_files == NULL); } +static void +show_error_on_reading_current_folder (GtkFileChooserDefault *impl, GError *error) +{ + GFileInfo *info; + char *msg; + + info = g_file_query_info (impl->current_folder, + G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, + G_FILE_QUERY_INFO_NONE, + NULL, + NULL); + if (info) + { + msg = g_strdup_printf (_("Could not read the contents of %s"), g_file_info_get_display_name (info)); + g_object_unref (info); + } + else + msg = g_strdup (_("Could not read the contents of the folder")); + + error_message (impl, msg, error->message); + g_free (msg); +} + /* Callback used when the file system model finishes loading */ static void browse_files_model_finished_loading_cb (GtkFileSystemModel *model, + GError *error, GtkFileChooserDefault *impl) { profile_start ("start", NULL); + if (error) + show_error_on_reading_current_folder (impl, error); + if (impl->load_state == LOAD_PRELOAD) { load_remove_timer (impl); @@ -6782,13 +6490,265 @@ stop_loading_and_clear_list_model (GtkFileChooserDefault *impl) impl->browse_files_model = NULL; } - if (impl->sort_model) + gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL); +} + +static char * +my_g_format_time_for_display (glong secs) +{ + GDate mtime, now; + gint days_diff; + struct tm tm_mtime; + time_t time_mtime, time_now; + const gchar *format; + gchar *locale_format = NULL; + gchar buf[256]; + char *date_str = NULL; +#ifdef G_OS_WIN32 + const char *locale, *dot = NULL; + gint64 codepage = -1; + char charset[20]; +#endif + + time_mtime = secs; + +#ifdef HAVE_LOCALTIME_R + localtime_r ((time_t *) &time_mtime, &tm_mtime); +#else + { + struct tm *ptm = localtime ((time_t *) &time_mtime); + + if (!ptm) + { + g_warning ("ptm != NULL failed"); + + return g_strdup (_("Unknown")); + } + else + memcpy ((void *) &tm_mtime, (void *) ptm, sizeof (struct tm)); + } +#endif /* HAVE_LOCALTIME_R */ + + g_date_set_time_t (&mtime, time_mtime); + time_now = time (NULL); + g_date_set_time_t (&now, time_now); + + days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime); + + /* Translators: %H means "hours" and %M means "minutes" */ + if (days_diff == 0) + format = _("%H:%M"); + else if (days_diff == 1) + format = _("Yesterday at %H:%M"); + else + { + if (days_diff > 1 && days_diff < 7) + format = "%A"; /* Days from last week */ + else + format = "%x"; /* Any other date */ + } + +#ifdef G_OS_WIN32 + /* g_locale_from_utf8() returns a string in the system + * code-page, which is not always the same as that used by the C + * library. For instance when running a GTK+ program with + * LANG=ko on an English version of Windows, the system + * code-page is 1252, but the code-page used by the C library is + * 949. (It's GTK+ itself that sets the C library locale when it + * notices the LANG environment variable. See gtkmain.c The + * Microsoft C library doesn't look at any locale environment + * variables.) We need to pass strftime() a string in the C + * library's code-page. See bug #509885. + */ + locale = setlocale (LC_ALL, NULL); + if (locale != NULL) + dot = strchr (locale, '.'); + if (dot != NULL) { - g_object_unref (impl->sort_model); - impl->sort_model = NULL; + codepage = g_ascii_strtoll (dot+1, NULL, 10); + + /* All codepages should fit in 16 bits AFAIK */ + if (codepage > 0 && codepage < 65536) + { + sprintf (charset, "CP%u", (guint) codepage); + locale_format = g_convert (format, -1, charset, "UTF-8", NULL, NULL, NULL); + } } - - gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL); +#else + locale_format = g_locale_from_utf8 (format, -1, NULL, NULL, NULL); +#endif + if (locale_format != NULL && + strftime (buf, sizeof (buf), locale_format, &tm_mtime) != 0) + { +#ifdef G_OS_WIN32 + /* As above but in opposite direction... */ + if (codepage > 0 && codepage < 65536) + date_str = g_convert (buf, -1, "UTF-8", charset, NULL, NULL, NULL); +#else + date_str = g_locale_to_utf8 (buf, -1, NULL, NULL, NULL); +#endif + } + + if (date_str == NULL) + date_str = g_strdup (_("Unknown")); + + g_free (locale_format); + return date_str; +} + +static void +copy_attribute (GFileInfo *to, GFileInfo *from, const char *attribute) +{ + GFileAttributeType type; + gpointer value; + + if (g_file_info_get_attribute_data (from, attribute, &type, &value, NULL)) + g_file_info_set_attribute (to, attribute, type, value); +} + +static void +file_system_model_got_thumbnail (GObject *object, GAsyncResult *res, gpointer data) +{ + GtkFileSystemModel *model = data; /* might be unreffed if operation was cancelled */ + GFile *file = G_FILE (object); + GFileInfo *queried, *info; + GtkTreeIter iter; + + queried = g_file_query_info_finish (file, res, NULL); + if (queried == NULL) + return; + + /* now we know model is valid */ + + /* file was deleted */ + if (!_gtk_file_system_model_get_iter_for_file (model, &iter, file)) + return; + + info = g_file_info_dup (_gtk_file_system_model_get_info (model, &iter)); + + copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAIL_PATH); + copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED); + copy_attribute (info, queried, G_FILE_ATTRIBUTE_STANDARD_ICON); + + _gtk_file_system_model_update_file (model, file, info, FALSE); + + g_object_unref (info); +} + +static gboolean +file_system_model_set (GtkFileSystemModel *model, + GFile *file, + GFileInfo *info, + int column, + GValue *value, + gpointer data) +{ + GtkFileChooserDefault *impl = data; + + switch (column) + { + case MODEL_COL_FILE: + g_value_set_object (value, file); + break; + case MODEL_COL_NAME: + if (info == NULL) + g_value_set_string (value, DEFAULT_NEW_FOLDER_NAME); + else + g_value_set_string (value, g_file_info_get_display_name (info)); + break; + case MODEL_COL_NAME_COLLATED: + if (info == NULL) + g_value_take_string (value, g_utf8_collate_key_for_filename (DEFAULT_NEW_FOLDER_NAME, -1)); + else + g_value_take_string (value, g_utf8_collate_key_for_filename (g_file_info_get_display_name (info), -1)); + break; + case MODEL_COL_IS_FOLDER: + g_value_set_boolean (value, info == NULL || _gtk_file_info_consider_as_directory (info)); + break; + case MODEL_COL_PIXBUF: + if (info) + { + if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_ICON)) + { + g_value_take_object (value, _gtk_file_info_render_icon (info, GTK_WIDGET (impl), impl->icon_size)); + } + else + { + GtkTreeModel *tree_model; + GtkTreePath *path, *start, *end; + GtkTreeIter iter; + + if (impl->browse_files_tree_view == NULL || + g_file_info_has_attribute (info, "filechooser::queried")) + return FALSE; + + tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view)); + if (tree_model != GTK_TREE_MODEL (model)) + return FALSE; + + if (!_gtk_file_system_model_get_iter_for_file (model, + &iter, + file)) + g_assert_not_reached (); + if (!gtk_tree_view_get_visible_range (GTK_TREE_VIEW (impl->browse_files_tree_view), &start, &end)) + return FALSE; + path = gtk_tree_model_get_path (tree_model, &iter); + if (gtk_tree_path_compare (start, path) != 1 && + gtk_tree_path_compare (path, end) != 1) + { + g_file_info_set_attribute_boolean (info, "filechooser::queried", TRUE); + g_file_query_info_async (file, + G_FILE_ATTRIBUTE_THUMBNAIL_PATH "," + G_FILE_ATTRIBUTE_THUMBNAILING_FAILED "," + G_FILE_ATTRIBUTE_STANDARD_ICON, + G_FILE_QUERY_INFO_NONE, + G_PRIORITY_DEFAULT, + _gtk_file_system_model_get_cancellable (model), + file_system_model_got_thumbnail, + model); + } + gtk_tree_path_free (path); + gtk_tree_path_free (start); + gtk_tree_path_free (end); + return FALSE; + } + } + else + g_value_set_object (value, NULL); + break; + case MODEL_COL_SIZE: + g_value_set_int64 (value, info ? g_file_info_get_size (info) : 0); + break; + case MODEL_COL_SIZE_TEXT: + if (info == NULL || _gtk_file_info_consider_as_directory (info)) + g_value_set_string (value, NULL); + else + g_value_take_string (value, g_format_size_for_display (g_file_info_get_size (info))); + break; + case MODEL_COL_MTIME: + case MODEL_COL_MTIME_TEXT: + { + GTimeVal tv; + if (info == NULL) + break; + g_file_info_get_modification_time (info, &tv); + if (column == MODEL_COL_MTIME) + g_value_set_long (value, tv.tv_sec); + else if (tv.tv_sec == 0) + g_value_set_static_string (value, _("Unknown")); + else + g_value_take_string (value, my_g_format_time_for_display (tv.tv_sec)); + break; + } + case MODEL_COL_ELLIPSIZE: + g_value_set_enum (value, info ? PANGO_ELLIPSIZE_END : PANGO_ELLIPSIZE_NONE); + break; + default: + g_assert_not_reached (); + break; + } + + return TRUE; } /* Gets rid of the old list model and creates a new one for the current folder */ @@ -6805,24 +6765,30 @@ set_list_model (GtkFileChooserDefault *impl, set_busy_cursor (impl, TRUE); gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL); - impl->browse_files_model = _gtk_file_system_model_new (impl->file_system, - impl->current_folder, 0, - "standard,time,thumbnail::*", - error); - if (!impl->browse_files_model) - { - set_busy_cursor (impl, FALSE); - profile_end ("end", NULL); - return FALSE; - } + impl->browse_files_model = + _gtk_file_system_model_new_for_directory (impl->current_folder, + MODEL_ATTRIBUTES, + file_system_model_set, + impl, + MODEL_COLUMN_TYPES); + + _gtk_file_system_model_set_show_hidden (impl->browse_files_model, impl->show_hidden); + + profile_msg (" set sort function", NULL); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), MODEL_COL_NAME, name_sort_func, impl, NULL); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), MODEL_COL_SIZE, size_sort_func, impl, NULL); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), MODEL_COL_MTIME, mtime_sort_func, impl, NULL); + gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (impl->browse_files_model), NULL, NULL, NULL); + set_sort_column (impl); + impl->list_sort_ascending = TRUE; + g_signal_connect (impl->browse_files_model, "sort-column-changed", + G_CALLBACK (list_sort_column_changed_cb), impl); load_setup_timer (impl); /* This changes the state to LOAD_PRELOAD */ g_signal_connect (impl->browse_files_model, "finished-loading", G_CALLBACK (browse_files_model_finished_loading_cb), impl); - _gtk_file_system_model_set_show_hidden (impl->browse_files_model, impl->show_hidden); - install_list_model_filter (impl); profile_end ("end", NULL); @@ -6902,18 +6868,12 @@ update_chooser_entry (GtkFileChooserDefault *impl) } else if (closure.num_selected == 1) { - GtkTreeIter child_iter; - if (impl->operation_mode == OPERATION_MODE_BROWSE) { GFileInfo *info; gboolean change_entry; - gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, - &child_iter, - &closure.first_selected_iter); - - info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter); + info = _gtk_file_system_model_get_info (impl->browse_files_model, &closure.first_selected_iter); /* If the cursor moved to the row of the newly created folder, * retrieving info will return NULL. @@ -7321,22 +7281,6 @@ gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser, _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), name); } -static void -select_func (GtkFileSystemModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer user_data) -{ - GtkFileChooserDefault *impl = user_data; - GtkTreeSelection *selection; - GtkTreeIter sorted_iter; - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view)); - - gtk_tree_model_sort_convert_child_iter_to_iter (impl->sort_model, &sorted_iter, iter); - gtk_tree_selection_select_iter (selection, &sorted_iter); -} - static gboolean gtk_file_chooser_default_select_file (GtkFileChooser *chooser, GFile *file, @@ -7372,7 +7316,7 @@ gtk_file_chooser_default_select_file (GtkFileChooser *chooser, files.data = (gpointer) file; files.next = NULL; - result = show_and_select_files (impl, parent_file, &files, error); + result = show_and_select_files (impl, &files); g_object_unref (parent_file); return result; } @@ -7393,33 +7337,23 @@ gtk_file_chooser_default_select_file (GtkFileChooser *chooser, } static void -unselect_func (GtkFileSystemModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer user_data) -{ - GtkFileChooserDefault *impl = user_data; - GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view); - GtkTreePath *sorted_path; - - sorted_path = gtk_tree_model_sort_convert_child_path_to_path (impl->sort_model, - path); - gtk_tree_selection_unselect_path (gtk_tree_view_get_selection (tree_view), - sorted_path); - gtk_tree_path_free (sorted_path); -} - -static void gtk_file_chooser_default_unselect_file (GtkFileChooser *chooser, GFile *file) { GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser); + GtkTreeView *tree_view = GTK_TREE_VIEW (impl->browse_files_tree_view); + GtkTreeIter iter; if (!impl->browse_files_model) return; - _gtk_file_system_model_path_do (impl->browse_files_model, file, - unselect_func, impl); + if (!_gtk_file_system_model_get_iter_for_file (impl->browse_files_model, + &iter, + file)) + return; + + gtk_tree_selection_unselect_iter (gtk_tree_view_get_selection (tree_view), + &iter); } static gboolean @@ -7430,13 +7364,13 @@ maybe_select (GtkTreeModel *model, { GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (data); GtkTreeSelection *selection; - GFileInfo *info; gboolean is_folder; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view)); - info = get_list_file_info (impl, iter); - is_folder = _gtk_file_info_consider_as_directory (info); + gtk_tree_model_get (model, iter, + MODEL_COL_IS_FOLDER, &is_folder, + -1); if ((is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) || (!is_folder && impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)) @@ -7463,7 +7397,7 @@ gtk_file_chooser_default_select_all (GtkFileChooser *chooser) } if (impl->select_multiple) - gtk_tree_model_foreach (GTK_TREE_MODEL (impl->sort_model), + gtk_tree_model_foreach (GTK_TREE_MODEL (impl->browse_files_model), maybe_select, impl); } @@ -7579,13 +7513,11 @@ get_files_foreach (GtkTreeModel *model, struct get_files_closure *info; GFile *file; GtkFileSystemModel *fs_model; - GtkTreeIter sel_iter; info = data; fs_model = info->impl->browse_files_model; - gtk_tree_model_sort_convert_iter_to_child_iter (info->impl->sort_model, &sel_iter, iter); - file = _gtk_file_system_model_get_file (fs_model, &sel_iter); + file = _gtk_file_system_model_get_file (fs_model, iter); if (!file) return; /* We are on the editable row */ @@ -8131,13 +8063,10 @@ switch_folder_foreach_cb (GtkTreeModel *model, gpointer data) { struct switch_folder_closure *closure; - GtkTreeIter child_iter; closure = data; - gtk_tree_model_sort_convert_iter_to_child_iter (closure->impl->sort_model, &child_iter, iter); - - closure->file = _gtk_file_system_model_get_file (closure->impl->browse_files_model, &child_iter); + closure->file = _gtk_file_system_model_get_file (closure->impl->browse_files_model, iter); closure->num_selected++; } @@ -8172,7 +8101,7 @@ get_selected_file_info_from_file_list (GtkFileChooserDefault *impl, gboolean *had_selection) { GtkTreeSelection *selection; - GtkTreeIter iter, child_iter; + GtkTreeIter iter; GFileInfo *info; g_assert (!impl->select_multiple); @@ -8185,11 +8114,7 @@ get_selected_file_info_from_file_list (GtkFileChooserDefault *impl, *had_selection = TRUE; - gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, - &child_iter, - &iter); - - info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter); + info = _gtk_file_system_model_get_info (impl->browse_files_model, &iter); return info; } @@ -8794,7 +8719,7 @@ search_selected_foreach_get_file_cb (GtkTreeModel *model, list = data; - gtk_tree_model_get (model, iter, SEARCH_MODEL_COL_FILE, &file, -1); + gtk_tree_model_get (model, iter, MODEL_COL_FILE, &file, -1); *list = g_slist_prepend (*list, g_object_ref (file)); } @@ -8828,106 +8753,12 @@ search_should_respond (GtkFileChooserDefault *impl) return (gtk_tree_selection_count_selected_rows (selection) != 0); } -struct SearchHitInsertRequest -{ - GtkFileChooserDefault *impl; - GFile *file; - GtkTreeRowReference *row_ref; -}; - -static void -search_hit_get_info_cb (GCancellable *cancellable, - GFileInfo *info, - const GError *error, - gpointer data) -{ - gboolean cancelled = g_cancellable_is_cancelled (cancellable); - GdkPixbuf *pixbuf = NULL; - GtkTreePath *path; - GtkTreeIter iter; - GCancellable *model_cancellable; - gboolean is_folder = FALSE; - GTimeVal mtime; - guint64 modification_time = 0; - goffset size; - char *mime_type; - char *display_name; - struct SearchHitInsertRequest *request = data; - - if (!request->impl->search_model) - goto out; - - path = gtk_tree_row_reference_get_path (request->row_ref); - if (!path) - goto out; - - gtk_tree_model_get_iter (GTK_TREE_MODEL (request->impl->search_model), - &iter, path); - gtk_tree_path_free (path); - - gtk_tree_model_get (GTK_TREE_MODEL (request->impl->search_model), &iter, - SEARCH_MODEL_COL_CANCELLABLE, &model_cancellable, - -1); - if (cancellable != model_cancellable) - goto out; - - /* set the cancellable to NULL in the model */ - gtk_list_store_set (request->impl->search_model, &iter, - SEARCH_MODEL_COL_CANCELLABLE, NULL, - -1); - - if (cancelled) - goto out; - - if (!info) - { - search_clear_model_row (GTK_TREE_MODEL (request->impl->search_model), &iter); - gtk_list_store_remove (request->impl->search_model, &iter); - goto out; - } - - display_name = g_strdup (g_file_info_get_display_name (info)); - mime_type = g_content_type_get_mime_type (g_file_info_get_content_type (info)); - g_file_info_get_modification_time (info, &mtime); - modification_time = (guint64) mtime.tv_sec; - size = g_file_info_get_size (info); - is_folder = _gtk_file_info_consider_as_directory (info); - pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (request->impl), - request->impl->icon_size); - - gtk_list_store_set (request->impl->search_model, &iter, - SEARCH_MODEL_COL_PIXBUF, pixbuf, - SEARCH_MODEL_COL_DISPLAY_NAME, display_name, - SEARCH_MODEL_COL_MIME_TYPE, mime_type, - SEARCH_MODEL_COL_IS_FOLDER, is_folder, - SEARCH_MODEL_COL_MTIME, modification_time, - SEARCH_MODEL_COL_SIZE, size, - -1); - - if (pixbuf) - g_object_unref (pixbuf); - -out: - g_object_unref (request->impl); - g_object_unref (request->file); - gtk_tree_row_reference_free (request->row_ref); - g_free (request); - - g_object_unref (cancellable); -} - /* Adds one hit from the search engine to the search_model */ static void search_add_hit (GtkFileChooserDefault *impl, gchar *uri) { GFile *file; - char *tmp; - char *collation_key; - GtkTreeIter iter; - GtkTreePath *p; - GCancellable *cancellable; - struct SearchHitInsertRequest *request; file = g_file_new_for_uri (uri); if (!file) @@ -8939,35 +8770,11 @@ search_add_hit (GtkFileChooserDefault *impl, return; } - tmp = g_file_get_parse_name (file); - collation_key = g_utf8_collate_key_for_filename (tmp, -1); - g_free (tmp); - - request = g_new0 (struct SearchHitInsertRequest, 1); - request->impl = g_object_ref (impl); - request->file = g_object_ref (file); - - gtk_list_store_append (impl->search_model, &iter); - p = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->search_model), &iter); - - request->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->search_model), p); - gtk_tree_path_free (p); + _gtk_file_system_model_add_and_query_file (impl->search_model, + file, + MODEL_ATTRIBUTES); - cancellable = _gtk_file_system_get_info (impl->file_system, file, - "standard::type," - "standard::icon," - "standard::content-type," - "standard::display-name," - "time::modified," - "standard::size", - search_hit_get_info_cb, - request); - - gtk_list_store_set (impl->search_model, &iter, - SEARCH_MODEL_COL_FILE, file, - SEARCH_MODEL_COL_COLLATION_KEY, collation_key, - SEARCH_MODEL_COL_CANCELLABLE, cancellable, - -1); + g_object_unref (file); } /* Callback used from GtkSearchEngine when we get new hits */ @@ -8999,7 +8806,7 @@ search_engine_finished_cb (GtkSearchEngine *engine, * but it'll make the search look like blocked. */ gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), - GTK_TREE_MODEL (impl->search_model_filter)); + GTK_TREE_MODEL (impl->search_model)); #endif /* FMQ: if search was empty, say that we got no hits */ @@ -9034,64 +8841,17 @@ search_engine_error_cb (GtkSearchEngine *engine, set_busy_cursor (impl, FALSE); } -static void -search_clear_model_row (GtkTreeModel *model, - GtkTreeIter *iter) -{ - GFile *file; - gchar *display_name; - gchar *collation_key; - GCancellable *cancellable; - gchar *mime_type; - - gtk_tree_model_get (model, iter, - SEARCH_MODEL_COL_FILE, &file, - SEARCH_MODEL_COL_DISPLAY_NAME, &display_name, - SEARCH_MODEL_COL_COLLATION_KEY, &collation_key, - SEARCH_MODEL_COL_CANCELLABLE, &cancellable, - SEARCH_MODEL_COL_MIME_TYPE, &mime_type, - -1); - - if (file) - g_object_unref (file); - - g_free (display_name); - g_free (collation_key); - g_free (mime_type); - - if (cancellable) - g_cancellable_cancel (cancellable); -} - /* Frees the data in the search_model */ static void search_clear_model (GtkFileChooserDefault *impl, gboolean remove_from_treeview) { - GtkTreeModel *model; - GtkTreeIter iter; - if (!impl->search_model) return; - model = GTK_TREE_MODEL (impl->search_model); - - if (gtk_tree_model_get_iter_first (model, &iter)) - do - { - search_clear_model_row (model, &iter); - } - while (gtk_tree_model_iter_next (model, &iter)); - g_object_unref (impl->search_model); impl->search_model = NULL; - g_object_unref (impl->search_model_filter); - impl->search_model_filter = NULL; - - g_object_unref (impl->search_model_sort); - impl->search_model_sort = NULL; - if (remove_from_treeview) gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL); } @@ -9130,7 +8890,7 @@ search_switch_to_browse_mode (GtkFileChooserDefault *impl) impl->search_entry = NULL; gtk_widget_show (impl->browse_path_bar); - if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN) + if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN || !impl->create_folders) gtk_widget_hide (impl->browse_new_folder_button); else gtk_widget_show (impl->browse_new_folder_button); @@ -9150,247 +8910,27 @@ search_switch_to_browse_mode (GtkFileChooserDefault *impl) file_list_set_sort_column_ids (impl); } -/* Sort callback from the path column */ -static gint -search_column_path_sort_func (GtkTreeModel *model, - GtkTreeIter *a, - GtkTreeIter *b, - gpointer user_data) -{ - GtkFileChooserDefault *impl = user_data; - GtkTreeIter child_a, child_b; - const char *collation_key_a, *collation_key_b; - gboolean is_folder_a, is_folder_b; - - gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_a, a); - gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_b, b); - - gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_a, - SEARCH_MODEL_COL_IS_FOLDER, &is_folder_a, - SEARCH_MODEL_COL_COLLATION_KEY, &collation_key_a, - -1); - gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_b, - SEARCH_MODEL_COL_IS_FOLDER, &is_folder_b, - SEARCH_MODEL_COL_COLLATION_KEY, &collation_key_b, - -1); - - if (!collation_key_a) - return 1; - - if (!collation_key_b) - return -1; - - /* always show folders first */ - if (is_folder_a != is_folder_b) - return is_folder_a ? 1 : -1; - - return strcmp (collation_key_a, collation_key_b); -} - -/* Sort callback from the size column */ -static gint -search_column_size_sort_func (GtkTreeModel *model, - GtkTreeIter *a, - GtkTreeIter *b, - gpointer user_data) -{ - GtkFileChooserDefault *impl = user_data; - GtkTreeIter child_a, child_b; - gboolean is_folder_a, is_folder_b; - goffset size_a, size_b; - - gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_a, a); - gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_b, b); - - gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_a, - SEARCH_MODEL_COL_IS_FOLDER, &is_folder_a, - SEARCH_MODEL_COL_SIZE, &size_a, - -1); - gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_b, - SEARCH_MODEL_COL_IS_FOLDER, &is_folder_b, - SEARCH_MODEL_COL_SIZE, &size_b, - -1); - - if (is_folder_a != is_folder_b) - return is_folder_a ? 1 : -1; - - if (size_a < size_b) - return -1; - else if (size_a > size_b) - return 1; - else - return 0; -} - -/* Sort callback from the modification time column */ -static gint -search_column_mtime_sort_func (GtkTreeModel *model, - GtkTreeIter *a, - GtkTreeIter *b, - gpointer user_data) -{ - GtkFileChooserDefault *impl = user_data; - GtkTreeIter child_a, child_b; - gboolean is_folder_a, is_folder_b; - guint64 mtime_a, mtime_b; - - gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_a, a); - gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_b, b); - - gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_a, - SEARCH_MODEL_COL_IS_FOLDER, &is_folder_a, - SEARCH_MODEL_COL_MTIME, &mtime_a, - -1); - gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_b, - SEARCH_MODEL_COL_IS_FOLDER, &is_folder_b, - SEARCH_MODEL_COL_MTIME, &mtime_b, - -1); - - if (is_folder_a != is_folder_b) - return is_folder_a ? 1 : -1; - - if (mtime_a < mtime_b) - return -1; - else if (mtime_a > mtime_b) - return 1; - else - return 0; -} - -static gboolean -search_get_is_filtered (GtkFileChooserDefault *impl, - GFile *file, - const gchar *display_name, - const gchar *mime_type) -{ - GtkFileFilterInfo filter_info; - GtkFileFilterFlags needed; - gboolean result; - - if (!impl->current_filter) - return FALSE; - - filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE; - needed = gtk_file_filter_get_needed (impl->current_filter); - - filter_info.display_name = display_name; - filter_info.mime_type = mime_type; - - if (needed & GTK_FILE_FILTER_FILENAME) - { - filter_info.filename = g_file_get_path (file); - if (filter_info.filename) - filter_info.contains |= GTK_FILE_FILTER_FILENAME; - } - else - filter_info.filename = NULL; - - if (needed & GTK_FILE_FILTER_URI) - { - filter_info.uri = g_file_get_uri (file); - if (filter_info.uri) - filter_info.contains |= GTK_FILE_FILTER_URI; - } - else - filter_info.uri = NULL; - - result = gtk_file_filter_filter (impl->current_filter, &filter_info); - - if (filter_info.filename) - g_free ((gchar *) filter_info.filename); - if (filter_info.uri) - g_free ((gchar *) filter_info.uri); - - return !result; - -} - -/* Visibility function for the recent filter model */ -static gboolean -search_model_visible_func (GtkTreeModel *model, - GtkTreeIter *iter, - gpointer user_data) -{ - GtkFileChooserDefault *impl = user_data; - GFile *file; - gchar *display_name, *mime_type; - gboolean is_folder; - - if (!impl->current_filter) - return TRUE; - - gtk_tree_model_get (model, iter, - SEARCH_MODEL_COL_FILE, &file, - SEARCH_MODEL_COL_IS_FOLDER, &is_folder, - SEARCH_MODEL_COL_DISPLAY_NAME, &display_name, - SEARCH_MODEL_COL_MIME_TYPE, &mime_type, - -1); - - if (!display_name) - return TRUE; - - if (is_folder) - return TRUE; - - return !search_get_is_filtered (impl, file, display_name, mime_type); -} - /* Creates the search_model and puts it in the tree view */ static void search_setup_model (GtkFileChooserDefault *impl) { g_assert (impl->search_model == NULL); - g_assert (impl->search_model_filter == NULL); - g_assert (impl->search_model_sort == NULL); - /* We store these columns in the search model: - * - * SEARCH_MODEL_COL_FILE - a GFile for the hit's URI, stored - * as a pointer not as a G_TYPE_FILE - * SEARCH_MODEL_COL_DISPLAY_NAME - a string with the display name, stored - * as a pointer not as a G_TYPE_STRING - * SEARCH_MODEL_COL_COLLATION_KEY - collation key for the filename, stored - * as a pointer not as a G_TYPE_STRING - * SEARCH_MODEL_COL_MTIME - G_TYPE_UINT64 for the modification time - * SEARCH_MODEL_COL_SIZE - G_TYPE_INT64 for the size - * SEARCH_MODEL_COL_CANCELLABLE - cancellable used when getting the hit's info - * SEARCH_MODEL_COL_PIXBUF - GdkPixbuf for the hit's icon - * SEARCH_MODEL_COL_MIME_TYPE - a string with the hit's MIME type - * SEARCH_MODEL_COL_IS_FOLDER - a boolean flag for folders - * - * Keep this in sync with the enumeration defined near the beginning - * of this file. - */ - impl->search_model = gtk_list_store_new (SEARCH_MODEL_COL_NUM_COLUMNS, - G_TYPE_POINTER, /* file */ - G_TYPE_POINTER, /* display-name */ - G_TYPE_POINTER, /* collation-key */ - G_TYPE_UINT64, /* mtime */ - G_TYPE_INT64, /* size */ - G_TYPE_POINTER, /* cancellable */ - GDK_TYPE_PIXBUF, /* pixbuf */ - G_TYPE_POINTER, /* mime-type */ - G_TYPE_BOOLEAN /*is-folder */); - - impl->search_model_filter = - GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (GTK_TREE_MODEL (impl->search_model), NULL)); - gtk_tree_model_filter_set_visible_func (impl->search_model_filter, - search_model_visible_func, - impl, NULL); - - impl->search_model_sort = - GTK_TREE_MODEL_SORT (search_model_sort_new (impl, GTK_TREE_MODEL (impl->search_model_filter))); - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model_sort), - SEARCH_MODEL_COL_FILE, - search_column_path_sort_func, + impl->search_model = _gtk_file_system_model_new (file_system_model_set, + impl, + MODEL_COLUMN_TYPES); + + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model), + MODEL_COL_NAME, + name_sort_func, impl, NULL); - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model_sort), - SEARCH_MODEL_COL_MTIME, - search_column_mtime_sort_func, + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model), + MODEL_COL_MTIME, + mtime_sort_func, impl, NULL); - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model_sort), - SEARCH_MODEL_COL_SIZE, - search_column_size_sort_func, + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->search_model), + MODEL_COL_SIZE, + size_sort_func, impl, NULL); set_sort_column (impl); @@ -9399,29 +8939,7 @@ search_setup_model (GtkFileChooserDefault *impl) * run */ gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), - GTK_TREE_MODEL (impl->search_model_sort)); -} - -static void -search_get_valid_child_iter (GtkFileChooserDefault *impl, - GtkTreeIter *child_iter, - GtkTreeIter *iter) -{ - GtkTreeIter middle; - - if (!impl->search_model) - return; - - if (!impl->search_model_filter || !impl->search_model_sort) - return; - - /* pass 1: get the iterator in the filter model */ - gtk_tree_model_sort_convert_iter_to_child_iter (impl->search_model_sort, - &middle, iter); - - /* pass 2: get the iterator in the real model */ - gtk_tree_model_filter_convert_iter_to_child_iter (impl->search_model_filter, - child_iter, &middle); + GTK_TREE_MODEL (impl->search_model)); } /* Creates a new query with the specified text and launches it */ @@ -9494,6 +9012,7 @@ search_setup_widgets (GtkFileChooserDefault *impl) { GtkWidget *label; GtkWidget *image; + gchar *tmp; impl->search_hbox = gtk_hbox_new (FALSE, 12); @@ -9506,8 +9025,10 @@ search_setup_widgets (GtkFileChooserDefault *impl) /* Label */ label = gtk_label_new (NULL); - gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("<b>_Search:</b>")); + tmp = g_strdup_printf ("<b>%s</b>", _("Search:")); + gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), tmp); gtk_box_pack_start (GTK_BOX (impl->search_hbox), label, FALSE, FALSE, 0); + g_free (tmp); /* Entry */ @@ -9609,7 +9130,6 @@ search_activate (GtkFileChooserDefault *impl) g_assert (impl->search_hbox == NULL); g_assert (impl->search_entry == NULL); g_assert (impl->search_model == NULL); - g_assert (impl->search_model_filter == NULL); search_setup_widgets (impl); file_list_set_sort_column_ids (impl); @@ -9625,7 +9145,6 @@ recent_clear_model (GtkFileChooserDefault *impl, gboolean remove_from_treeview) { GtkTreeModel *model; - GtkTreeIter iter; if (!impl->recent_model) return; @@ -9635,40 +9154,8 @@ recent_clear_model (GtkFileChooserDefault *impl, if (remove_from_treeview) gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), NULL); - if (gtk_tree_model_get_iter_first (model, &iter)) - { - do - { - GFile *file; - GCancellable *cancellable; - GtkRecentInfo *recent_info; - gchar *display_name; - - gtk_tree_model_get (model, &iter, - RECENT_MODEL_COL_DISPLAY_NAME, &display_name, - RECENT_MODEL_COL_FILE, &file, - RECENT_MODEL_COL_CANCELLABLE, &cancellable, - RECENT_MODEL_COL_INFO, &recent_info, - -1); - - if (cancellable) - g_cancellable_cancel (cancellable); - - g_object_unref (file); - gtk_recent_info_unref (recent_info); - g_free (display_name); - } - while (gtk_tree_model_iter_next (model, &iter)); - } - g_object_unref (impl->recent_model); impl->recent_model = NULL; - - g_object_unref (impl->recent_model_filter); - impl->recent_model_filter = NULL; - - g_object_unref (impl->recent_model_sort); - impl->recent_model_sort = NULL; } /* Stops any ongoing loading of the recent files list; does @@ -9699,7 +9186,7 @@ recent_switch_to_browse_mode (GtkFileChooserDefault *impl) impl->recent_hbox = NULL; gtk_widget_show (impl->browse_path_bar); - if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN) + if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN || !impl->create_folders) gtk_widget_hide (impl->browse_new_folder_button); else gtk_widget_show (impl->browse_new_folder_button); @@ -9720,211 +9207,28 @@ recent_switch_to_browse_mode (GtkFileChooserDefault *impl) file_list_set_sort_column_ids (impl); } -/* Sort callback from the modification time column */ -static gint -recent_column_mtime_sort_func (GtkTreeModel *model, - GtkTreeIter *a, - GtkTreeIter *b, - gpointer user_data) -{ - GtkFileChooserDefault *impl = user_data; - GtkTreeIter child_a, child_b; - GtkRecentInfo *info_a, *info_b; - gboolean is_folder_a, is_folder_b; - - gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_a, a); - gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_b, b); - - gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_a, - RECENT_MODEL_COL_IS_FOLDER, &is_folder_a, - RECENT_MODEL_COL_INFO, &info_a, - -1); - gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_b, - RECENT_MODEL_COL_IS_FOLDER, &is_folder_b, - RECENT_MODEL_COL_INFO, &info_b, - -1); - - if (!info_a) - return 1; - - if (!info_b) - return -1; - - /* folders always go first */ - if (is_folder_a != is_folder_b) - return is_folder_a ? 1 : -1; - - if (gtk_recent_info_get_modified (info_a) < gtk_recent_info_get_modified (info_b)) - return -1; - else if (gtk_recent_info_get_modified (info_a) > gtk_recent_info_get_modified (info_b)) - return 1; - else - return 0; -} - -static gint -recent_column_path_sort_func (GtkTreeModel *model, - GtkTreeIter *a, - GtkTreeIter *b, - gpointer user_data) -{ - GtkFileChooserDefault *impl = user_data; - GtkTreeIter child_a, child_b; - gboolean is_folder_a, is_folder_b; - gchar *name_a, *name_b; - - gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_a, a); - gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_b, b); - - gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_a, - RECENT_MODEL_COL_IS_FOLDER, &is_folder_a, - RECENT_MODEL_COL_DISPLAY_NAME, &name_a, - -1); - gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_b, - RECENT_MODEL_COL_IS_FOLDER, &is_folder_b, - RECENT_MODEL_COL_DISPLAY_NAME, &name_b, - -1); - - if (!name_a) - return 1; - - if (!name_b) - return -1; - - if (is_folder_a != is_folder_b) - return is_folder_a ? 1 : -1; - - return strcmp (name_a, name_b); -} - -static gboolean -recent_get_is_filtered (GtkFileChooserDefault *impl, - GFile *file, - GtkRecentInfo *recent_info) -{ - GtkFileFilterInfo filter_info; - GtkFileFilterFlags needed; - gboolean result; - - if (!impl->current_filter) - return FALSE; - - filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME | GTK_FILE_FILTER_MIME_TYPE; - needed = gtk_file_filter_get_needed (impl->current_filter); - - filter_info.display_name = gtk_recent_info_get_display_name (recent_info); - filter_info.mime_type = gtk_recent_info_get_mime_type (recent_info); - - if (needed & GTK_FILE_FILTER_FILENAME) - { - filter_info.filename = g_file_get_path (file); - if (filter_info.filename) - filter_info.contains |= GTK_FILE_FILTER_FILENAME; - } - else - filter_info.filename = NULL; - - if (needed & GTK_FILE_FILTER_URI) - { - filter_info.uri = g_file_get_uri (file); - if (filter_info.uri) - filter_info.contains |= GTK_FILE_FILTER_URI; - } - else - filter_info.uri = NULL; - - result = gtk_file_filter_filter (impl->current_filter, &filter_info); - - if (filter_info.filename) - g_free ((gchar *) filter_info.filename); - if (filter_info.uri) - g_free ((gchar *) filter_info.uri); - - return !result; -} - -/* Visibility function for the recent filter model */ -static gboolean -recent_model_visible_func (GtkTreeModel *model, - GtkTreeIter *iter, - gpointer user_data) -{ - GtkFileChooserDefault *impl = user_data; - GFile *file; - GtkRecentInfo *recent_info; - gboolean is_folder; - - if (!impl->current_filter) - return TRUE; - - gtk_tree_model_get (model, iter, - RECENT_MODEL_COL_INFO, &recent_info, - RECENT_MODEL_COL_FILE, &file, - RECENT_MODEL_COL_IS_FOLDER, &is_folder, - -1); - - if (!recent_info) - return TRUE; - - if (is_folder) - return TRUE; - - return !recent_get_is_filtered (impl, file, recent_info); -} - static void recent_setup_model (GtkFileChooserDefault *impl) { g_assert (impl->recent_model == NULL); - g_assert (impl->recent_model_filter == NULL); - g_assert (impl->recent_model_sort == NULL); - /* We store these columns in the search model: - * - * RECENT_MODEL_COL_FILE - a pointer to GFile for the hit's URI, - * stored as a pointer and not as a G_TYPE_FILE; - * RECENT_MODEL_COL_DISPLAY_NAME - a string with the display name, - * stored as a pointer and not as a G_TYPE_STRING; - * RECENT_MODEL_COL_INFO - GtkRecentInfo, stored as a pointer and not - * as a GTK_TYPE_RECENT_INFO; - * RECENT_MODEL_COL_IS_FOLDER - boolean flag; - * RECENT_MODEL_COL_CANCELLABLE - GCancellable, stored as a pointer - * and not as a G_TYPE_CANCELLABLE; - * - * Keep this in sync with the enumeration defined near the beginning of - * this file. - */ - impl->recent_model = gtk_list_store_new (RECENT_MODEL_COL_NUM_COLUMNS, - G_TYPE_POINTER, - G_TYPE_POINTER, - G_TYPE_POINTER, - G_TYPE_BOOLEAN, - G_TYPE_POINTER); - - impl->recent_model_filter = - GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (GTK_TREE_MODEL (impl->recent_model), NULL)); - gtk_tree_model_filter_set_visible_func (impl->recent_model_filter, - recent_model_visible_func, - impl, - NULL); - - /* this is the model that will actually be added to - * the browse_files_tree_view widget; remember: we are - * stuffing the real model into a filter model and then - * into a sort model; this means we'll have to translate - * the child iterator *twice* to get from a path or an - * iterator coming from the tree view widget to the - * real data inside the model. - */ - impl->recent_model_sort = - GTK_TREE_MODEL_SORT (recent_model_sort_new (impl, GTK_TREE_MODEL (impl->recent_model_filter))); - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->recent_model_sort), - RECENT_MODEL_COL_FILE, - recent_column_path_sort_func, + impl->recent_model = _gtk_file_system_model_new (file_system_model_set, + impl, + MODEL_COLUMN_TYPES); + + _gtk_file_system_model_set_filter (impl->recent_model, + impl->current_filter); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->recent_model), + MODEL_COL_NAME, + name_sort_func, impl, NULL); - gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->recent_model_sort), - RECENT_MODEL_COL_INFO, - recent_column_mtime_sort_func, + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->recent_model), + MODEL_COL_SIZE, + size_sort_func, + impl, NULL); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (impl->recent_model), + MODEL_COL_MTIME, + mtime_sort_func, impl, NULL); set_sort_column (impl); } @@ -9945,7 +9249,7 @@ recent_idle_cleanup (gpointer data) GtkFileChooserDefault *impl = load_data->impl; gtk_tree_view_set_model (GTK_TREE_VIEW (impl->browse_files_tree_view), - GTK_TREE_MODEL (impl->recent_model_sort)); + GTK_TREE_MODEL (impl->recent_model)); set_busy_cursor (impl, FALSE); @@ -9960,71 +9264,6 @@ recent_idle_cleanup (gpointer data) g_free (load_data); } -struct RecentItemInsertRequest -{ - GtkFileChooserDefault *impl; - GFile *file; - GtkTreeRowReference *row_ref; -}; - -static void -recent_item_get_info_cb (GCancellable *cancellable, - GFileInfo *info, - const GError *error, - gpointer data) -{ - gboolean cancelled = g_cancellable_is_cancelled (cancellable); - GtkTreePath *path; - GtkTreeIter iter; - GCancellable *model_cancellable; - gboolean is_folder = FALSE; - struct RecentItemInsertRequest *request = data; - - if (!request->impl->recent_model) - goto out; - - path = gtk_tree_row_reference_get_path (request->row_ref); - if (!path) - goto out; - - gtk_tree_model_get_iter (GTK_TREE_MODEL (request->impl->recent_model), - &iter, path); - gtk_tree_path_free (path); - - gtk_tree_model_get (GTK_TREE_MODEL (request->impl->recent_model), &iter, - RECENT_MODEL_COL_CANCELLABLE, &model_cancellable, - -1); - if (cancellable != model_cancellable) - goto out; - - gtk_list_store_set (request->impl->recent_model, &iter, - RECENT_MODEL_COL_CANCELLABLE, NULL, - -1); - - if (cancelled) - goto out; - - if (!info) - { - gtk_list_store_remove (request->impl->recent_model, &iter); - goto out; - } - - is_folder = _gtk_file_info_consider_as_directory (info); - - gtk_list_store_set (request->impl->recent_model, &iter, - RECENT_MODEL_COL_IS_FOLDER, is_folder, - -1); - -out: - g_object_unref (request->impl); - g_object_unref (request->file); - gtk_tree_row_reference_free (request->row_ref); - g_free (request); - - g_object_unref (cancellable); -} - static gint recent_sort_mru (gconstpointer a, gconstpointer b) @@ -10056,13 +9295,8 @@ recent_idle_load (gpointer data) { RecentLoadData *load_data = data; GtkFileChooserDefault *impl = load_data->impl; - GtkTreeIter iter; - GtkTreePath *p; - GtkRecentInfo *info; - const gchar *uri, *display_name; + GList *walk; GFile *file; - GCancellable *cancellable; - struct RecentItemInsertRequest *request; if (!impl->recent_manager) return FALSE; @@ -10112,47 +9346,23 @@ recent_idle_load (gpointer data) return TRUE; } - info = g_list_nth_data (load_data->items, load_data->n_loaded_items); - g_assert (info != NULL); - - uri = gtk_recent_info_get_uri (info); - display_name = gtk_recent_info_get_display_name (info); - file = g_file_new_for_uri (uri); - - gtk_list_store_append (impl->recent_model, &iter); - p = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->recent_model), &iter); - - request = g_new0 (struct RecentItemInsertRequest, 1); - request->impl = g_object_ref (impl); - request->file = g_object_ref (file); - request->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (impl->recent_model), p); - gtk_tree_path_free (p); - - cancellable = _gtk_file_system_get_info (impl->file_system, file, - "standard::type", - recent_item_get_info_cb, - request); - - gtk_list_store_set (impl->recent_model, &iter, - RECENT_MODEL_COL_FILE, file, - RECENT_MODEL_COL_DISPLAY_NAME, g_strdup (display_name), - RECENT_MODEL_COL_INFO, gtk_recent_info_ref (info), - RECENT_MODEL_COL_CANCELLABLE, cancellable, - -1); - - load_data->n_loaded_items += 1; - /* finished loading items */ - if (load_data->n_loaded_items == load_data->n_items) + for (walk = load_data->items; walk; walk = walk->next) { - g_list_foreach (load_data->items, (GFunc) gtk_recent_info_unref, NULL); - g_list_free (load_data->items); - load_data->items = NULL; + GtkRecentInfo *info = walk->data; + file = g_file_new_for_uri (gtk_recent_info_get_uri (info)); - return FALSE; + _gtk_file_system_model_add_and_query_file (impl->recent_model, + file, + MODEL_ATTRIBUTES); + gtk_recent_info_unref (walk->data); + g_object_unref (file); } - return TRUE; + g_list_free (load_data->items); + load_data->items = NULL; + + return FALSE; } static void @@ -10192,8 +9402,8 @@ recent_selected_foreach_get_file_cb (GtkTreeModel *model, list = data; - gtk_tree_model_get (model, iter, RECENT_MODEL_COL_FILE, &file, -1); - *list = g_slist_prepend (*list, g_object_ref (file)); + gtk_tree_model_get (model, iter, MODEL_COL_FILE, &file, -1); + *list = g_slist_prepend (*list, file); } /* Constructs a list of the selected paths in recent files mode */ @@ -10232,6 +9442,7 @@ recent_hide_entry (GtkFileChooserDefault *impl) { GtkWidget *label; GtkWidget *image; + gchar *tmp; impl->recent_hbox = gtk_hbox_new (FALSE, 12); @@ -10242,8 +9453,10 @@ recent_hide_entry (GtkFileChooserDefault *impl) /* Label */ label = gtk_label_new (NULL); - gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), _("<b>Recently Used</b>")); + tmp = g_strdup_printf ("<b>%s</b>", _("Recently Used")); + gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), tmp); gtk_box_pack_start (GTK_BOX (impl->recent_hbox), label, FALSE, FALSE, 0); + g_free (tmp); gtk_widget_hide (impl->browse_path_bar); gtk_widget_hide (impl->browse_new_folder_button); @@ -10279,41 +9492,10 @@ recent_activate (GtkFileChooserDefault *impl) recent_hide_entry (impl); - /* hide the file size column if it's visible */ - gtk_tree_view_column_set_visible (impl->list_size_column, FALSE); - file_list_set_sort_column_ids (impl); recent_start_loading (impl); } -/* convert an iterator coming from the model bound to - * browse_files_tree_view to an interator inside the - * real recent_model - */ -static void -recent_get_valid_child_iter (GtkFileChooserDefault *impl, - GtkTreeIter *child_iter, - GtkTreeIter *iter) -{ - GtkTreeIter middle; - - if (!impl->recent_model) - return; - - if (!impl->recent_model_filter || !impl->recent_model_sort) - return; - - /* pass 1: get the iterator in the filter model */ - gtk_tree_model_sort_convert_iter_to_child_iter (impl->recent_model_sort, - &middle, iter); - - /* pass 2: get the iterator in the real model */ - gtk_tree_model_filter_convert_iter_to_child_iter (impl->recent_model_filter, - child_iter, - &middle); -} - - static void set_current_filter (GtkFileChooserDefault *impl, GtkFileFilter *filter) @@ -10343,11 +9525,11 @@ set_current_filter (GtkFileChooserDefault *impl, if (impl->browse_files_model) install_list_model_filter (impl); - if (impl->search_model_filter) - gtk_tree_model_filter_refilter (impl->search_model_filter); + if (impl->search_model) + _gtk_file_system_model_set_filter (impl->search_model, filter); - if (impl->recent_model_filter) - gtk_tree_model_filter_refilter (impl->recent_model_filter); + if (impl->recent_model) + _gtk_file_system_model_set_filter (impl->recent_model, filter); g_object_notify (G_OBJECT (impl), "filter"); } @@ -10368,61 +9550,27 @@ check_preview_change (GtkFileChooserDefault *impl) { GtkTreePath *cursor_path; GFile *new_file; - const char *new_display_name; + char *new_display_name; + GtkTreeModel *model; gtk_tree_view_get_cursor (GTK_TREE_VIEW (impl->browse_files_tree_view), &cursor_path, NULL); - new_file = NULL; - new_display_name = NULL; + model = gtk_tree_view_get_model (GTK_TREE_VIEW (impl->browse_files_tree_view)); if (cursor_path) { - GtkTreeIter child_iter; - - if (impl->operation_mode == OPERATION_MODE_BROWSE) - { - if (impl->sort_model) - { - GtkTreeIter iter; - GFileInfo *new_info; - - gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, cursor_path); - gtk_tree_path_free (cursor_path); - - gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter); - - new_file = _gtk_file_system_model_get_file (impl->browse_files_model, &child_iter); - new_info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter); - if (new_info) - new_display_name = g_file_info_get_display_name (new_info); - } - } - else if (impl->operation_mode == OPERATION_MODE_SEARCH) - { - GtkTreeIter iter; - - gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->search_model_sort), - &iter, cursor_path); - gtk_tree_path_free (cursor_path); - - search_get_valid_child_iter (impl, &child_iter, &iter); - gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter, - SEARCH_MODEL_COL_FILE, &new_file, - SEARCH_MODEL_COL_DISPLAY_NAME, &new_display_name, - -1); - } - else if (impl->operation_mode == OPERATION_MODE_RECENT) - { - GtkTreeIter iter; - - gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->recent_model_sort), - &iter, cursor_path); - gtk_tree_path_free (cursor_path); + GtkTreeIter iter; - recent_get_valid_child_iter (impl, &child_iter, &iter); - gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter, - RECENT_MODEL_COL_FILE, &new_file, - RECENT_MODEL_COL_DISPLAY_NAME, &new_display_name, - -1); - } + gtk_tree_model_get_iter (model, &iter, cursor_path); + gtk_tree_model_get (model, &iter, + MODEL_COL_FILE, &new_file, + MODEL_COL_NAME, &new_display_name, + -1); + + gtk_tree_path_free (cursor_path); + } + else + { + new_file = NULL; + new_display_name = NULL; } if (new_file != impl->preview_file && @@ -10437,13 +9585,14 @@ check_preview_change (GtkFileChooserDefault *impl) if (new_file) { - impl->preview_file = g_object_ref (new_file); - impl->preview_display_name = g_strdup (new_display_name); + impl->preview_file = new_file; + impl->preview_display_name = new_display_name; } else { impl->preview_file = NULL; impl->preview_display_name = NULL; + g_free (new_display_name); } if (impl->use_preview_label && impl->preview_label) @@ -10707,6 +9856,12 @@ shortcuts_key_press_event_cb (GtkWidget *widget, modifiers = gtk_accelerator_get_default_mod_mask (); + if (key_is_left_or_right (event)) + { + gtk_widget_grab_focus (impl->browse_files_tree_view); + return TRUE; + } + if ((event->keyval == GDK_BackSpace || event->keyval == GDK_Delete || event->keyval == GDK_KP_Delete) @@ -10757,56 +9912,16 @@ list_select_func (GtkTreeSelection *selection, if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) { - GtkTreeIter iter, child_iter; - - switch (impl->operation_mode) - { - case OPERATION_MODE_SEARCH: - { - gboolean is_folder; - - if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->search_model_sort), &iter, path)) - return FALSE; - - search_get_valid_child_iter (impl, &child_iter, &iter); - gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter, - SEARCH_MODEL_COL_IS_FOLDER, &is_folder, - -1); - if (!is_folder) - return FALSE; - } - break; - - case OPERATION_MODE_RECENT: - { - gboolean is_folder; - - if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->recent_model_sort), &iter, path)) - return FALSE; - - recent_get_valid_child_iter (impl, &child_iter, &iter); - gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter, - RECENT_MODEL_COL_IS_FOLDER, &is_folder, - -1); - if (!is_folder) - return FALSE; - } - break; - - case OPERATION_MODE_BROWSE: - { - GFileInfo *info; - - if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path)) - return FALSE; + GtkTreeIter iter; + gboolean is_folder; - gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, &child_iter, &iter); - info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter); - if (info && (! _gtk_file_info_consider_as_directory (info))) - return FALSE; - } - break; - } + if (!gtk_tree_model_get_iter (model, &iter, path)) + return FALSE; + gtk_tree_model_get (model, &iter, + MODEL_COL_IS_FOLDER, &is_folder, + -1); + if (!is_folder) + return FALSE; } return TRUE; @@ -10849,103 +9964,33 @@ list_row_activated (GtkTreeView *tree_view, GtkTreeViewColumn *column, GtkFileChooserDefault *impl) { + GFile *file; GtkTreeIter iter; - GtkTreeIter child_iter; - - switch (impl->operation_mode) - { - case OPERATION_MODE_SEARCH: - { - GFile *file; - gboolean is_folder; - - if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->search_model_sort), &iter, path)) - return; - - search_get_valid_child_iter (impl, &child_iter, &iter); - gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter, - SEARCH_MODEL_COL_FILE, &file, - SEARCH_MODEL_COL_IS_FOLDER, &is_folder, - -1); - - if (is_folder) - { - change_folder_and_display_error (impl, file, FALSE); - return; - } + GtkTreeModel *model; + gboolean is_folder; - g_signal_emit_by_name (impl, "file-activated"); - } - break; + model = gtk_tree_view_get_model (tree_view); - case OPERATION_MODE_RECENT: - { - GFile *file; - gboolean is_folder; + if (!gtk_tree_model_get_iter (model, &iter, path)) + return; - if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->recent_model_sort), &iter, path)) - return; - - recent_get_valid_child_iter (impl, &child_iter, &iter); - gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter, - RECENT_MODEL_COL_FILE, &file, - RECENT_MODEL_COL_IS_FOLDER, &is_folder, - -1); - - if (is_folder) - { - change_folder_and_display_error (impl, file, FALSE); - return; - } + gtk_tree_model_get (model, &iter, + MODEL_COL_FILE, &file, + MODEL_COL_IS_FOLDER, &is_folder, + -1); - g_signal_emit_by_name (impl, "file-activated"); - } - break; - - case OPERATION_MODE_BROWSE: - { - GFileInfo *info; + if (is_folder && file) + { + change_folder_and_display_error (impl, file, FALSE); + return; + } - if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->sort_model), &iter, path)) - return; - - gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, - &child_iter, &iter); - info = _gtk_file_system_model_get_info (impl->browse_files_model, - &child_iter); - - if (_gtk_file_info_consider_as_directory (info)) - { - GFile *file, *target_file; - const gchar *target_uri; - - file = _gtk_file_system_model_get_file (impl->browse_files_model, &child_iter); - if (g_file_info_get_file_type (info) == G_FILE_TYPE_MOUNTABLE || - g_file_info_get_file_type (info) == G_FILE_TYPE_SHORTCUT) - { - target_uri = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI); - if (target_uri) - { - target_file = g_file_new_for_uri (target_uri); - if (target_file) - { - g_object_unref (file); - file = target_file; - } - } - } - - - change_folder_and_display_error (impl, file, FALSE); - return; - } + if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN || + impl->action == GTK_FILE_CHOOSER_ACTION_SAVE) + g_signal_emit_by_name (impl, "file-activated"); - if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN || - impl->action == GTK_FILE_CHOOSER_ACTION_SAVE) - g_signal_emit_by_name (impl, "file-activated"); - } - break; - } + if (file) + g_object_unref (file); } static void @@ -10969,472 +10014,70 @@ path_bar_clicked (GtkPathBar *path_bar, g_object_set (impl, "show-hidden", TRUE, NULL); } -static GFileInfo * -get_list_file_info (GtkFileChooserDefault *impl, - GtkTreeIter *iter) -{ - GtkTreeIter child_iter; - - gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, - &child_iter, - iter); - - return _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter); -} - static void -list_icon_data_func (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gpointer data) +update_cell_renderer_attributes (GtkFileChooserDefault *impl) { - GtkFileChooserDefault *impl = data; - GtkTreeIter child_iter; - GdkPixbuf *pixbuf = NULL; - gboolean sensitive = TRUE; - - profile_start ("start", NULL); - - switch (impl->operation_mode) - { - case OPERATION_MODE_SEARCH: - { - GtkTreeIter child_iter; - gboolean is_folder; - - search_get_valid_child_iter (impl, &child_iter, iter); - gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter, - SEARCH_MODEL_COL_PIXBUF, &pixbuf, - SEARCH_MODEL_COL_IS_FOLDER, &is_folder, - -1); - - if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || - impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) - sensitive = is_folder; - } - break; - - case OPERATION_MODE_RECENT: - { - GtkTreeIter child_iter; - GtkRecentInfo *info; - gboolean is_folder; - - recent_get_valid_child_iter (impl, &child_iter, iter); - gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter, - RECENT_MODEL_COL_INFO, &info, - RECENT_MODEL_COL_IS_FOLDER, &is_folder, - -1); - - pixbuf = gtk_recent_info_get_icon (info, impl->icon_size); - - if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || - impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) - sensitive = is_folder; - } - break; - - case OPERATION_MODE_BROWSE: - { - GFileInfo *info; - GFile *file; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GList *walk, *list; + gboolean always_sensitive; - info = get_list_file_info (impl, iter); + always_sensitive = impl->action != GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER && + impl->action != GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER; - gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model, - &child_iter, - iter); - file = _gtk_file_system_model_get_file (impl->browse_files_model, &child_iter); - if (file) - { - if (info) - { - /* FIXME: NULL GError */ - pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (impl), impl->icon_size); - } - } - else - { - /* We are on the editable row */ - pixbuf = NULL; - } - - if (info && - (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || - impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)) - sensitive = _gtk_file_info_consider_as_directory (info); - } - break; - } - - g_object_set (cell, - "pixbuf", pixbuf, - "sensitive", sensitive, - NULL); - - if (pixbuf) - g_object_unref (pixbuf); - - profile_end ("end", NULL); -} + /* Keep the following column numbers in sync with create_file_list() */ -static void -list_name_data_func (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gpointer data) -{ - GtkFileChooserDefault *impl = data; - GFileInfo *info; - gboolean sensitive = TRUE; - - if (impl->operation_mode == OPERATION_MODE_SEARCH) + /* name */ + column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_files_tree_view), 0); + list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column)); + for (walk = list; walk; walk = walk->next) { - GtkTreeIter child_iter; - gchar *display_name; - gboolean is_folder; - - search_get_valid_child_iter (impl, &child_iter, iter); - gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter, - SEARCH_MODEL_COL_DISPLAY_NAME, &display_name, - SEARCH_MODEL_COL_IS_FOLDER, &is_folder, - -1); - - if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || - impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) + renderer = walk->data; + if (GTK_IS_CELL_RENDERER_PIXBUF (renderer)) { - sensitive = is_folder; + gtk_tree_view_column_set_attributes (column, renderer, + "pixbuf", MODEL_COL_PIXBUF, + NULL); } - - g_object_set (cell, - "text", display_name, - "sensitive", sensitive, - "ellipsize", PANGO_ELLIPSIZE_END, - NULL); - - return; - } - - if (impl->operation_mode == OPERATION_MODE_RECENT) - { - GtkTreeIter child_iter; - GtkRecentInfo *recent_info; - gchar *display_name; - gboolean is_folder; - - recent_get_valid_child_iter (impl, &child_iter, iter); - gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter, - RECENT_MODEL_COL_INFO, &recent_info, - RECENT_MODEL_COL_IS_FOLDER, &is_folder, - -1); - - display_name = gtk_recent_info_get_short_name (recent_info); - - if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || - impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) + else { - sensitive = is_folder; + gtk_tree_view_column_set_attributes (column, renderer, + "text", MODEL_COL_NAME, + "ellipsize", MODEL_COL_ELLIPSIZE, + NULL); } - - g_object_set (cell, - "text", display_name, - "sensitive", sensitive, - "ellipsize", PANGO_ELLIPSIZE_END, - NULL); - - g_free (display_name); - - return; - } - - info = get_list_file_info (impl, iter); - sensitive = TRUE; - - if (!info) - { - g_object_set (cell, - "text", DEFAULT_NEW_FOLDER_NAME, - "sensitive", TRUE, - "ellipsize", PANGO_ELLIPSIZE_NONE, - NULL); - - return; - } - - - if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || - impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) - { - sensitive = _gtk_file_info_consider_as_directory (info); - } - - g_object_set (cell, - "text", g_file_info_get_display_name (info), - "sensitive", sensitive, - "ellipsize", PANGO_ELLIPSIZE_END, - NULL); -} - -static void -list_size_data_func (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gpointer data) -{ - GtkFileChooserDefault *impl = data; - GFileInfo *info; - goffset size; - gchar *str; - gboolean sensitive = TRUE; - - if (impl->operation_mode == OPERATION_MODE_RECENT) - return; - - if (impl->operation_mode == OPERATION_MODE_SEARCH) - { - GtkTreeIter child_iter; - gboolean is_folder = FALSE; - - search_get_valid_child_iter (impl, &child_iter, iter); - gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter, - SEARCH_MODEL_COL_SIZE, &size, - SEARCH_MODEL_COL_IS_FOLDER, &is_folder, - -1); - - if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || - impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) - sensitive = is_folder ? TRUE : FALSE; - - if (!is_folder) - str = g_format_size_for_display (size); - else - str = NULL; - - g_object_set (cell, - "text", str, - "sensitive", sensitive, - NULL); - - g_free (str); - - return; - } - - info = get_list_file_info (impl, iter); - - if (!info || _gtk_file_info_consider_as_directory (info)) - { - g_object_set (cell, - "text", NULL, - "sensitive", sensitive, - NULL); - return; - } - - size = g_file_info_get_size (info); - str = g_format_size_for_display (size); - - if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || - impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) - sensitive = FALSE; - - g_object_set (cell, - "text", str, - "sensitive", sensitive, - NULL); - - g_free (str); -} - -/* Tree column data callback for the file list; fetches the mtime of a file */ -static void -list_mtime_data_func (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *tree_model, - GtkTreeIter *iter, - gpointer data) -{ - GtkFileChooserDefault *impl; - GTimeVal timeval = { 0, }; - time_t time_mtime; - gchar *date_str = NULL; - gboolean sensitive = TRUE; -#ifdef G_OS_WIN32 - const char *locale, *dot = NULL; - gint64 codepage = -1; - char charset[20]; -#endif - - impl = data; - - if (impl->operation_mode == OPERATION_MODE_SEARCH) - { - GtkTreeIter child_iter; - guint64 mtime; - gboolean is_folder; - - search_get_valid_child_iter (impl, &child_iter, iter); - gtk_tree_model_get (GTK_TREE_MODEL (impl->search_model), &child_iter, - SEARCH_MODEL_COL_MTIME, &mtime, - SEARCH_MODEL_COL_IS_FOLDER, &is_folder, - -1); - - time_mtime = (time_t) mtime; - - if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || - impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) - sensitive = is_folder; - } - else if (impl->operation_mode == OPERATION_MODE_RECENT) - { - GtkTreeIter child_iter; - GtkRecentInfo *info; - gboolean is_folder; - - recent_get_valid_child_iter (impl, &child_iter, iter); - gtk_tree_model_get (GTK_TREE_MODEL (impl->recent_model), &child_iter, - RECENT_MODEL_COL_INFO, &info, - RECENT_MODEL_COL_IS_FOLDER, &is_folder, - -1); - - if (info) - time_mtime = gtk_recent_info_get_modified (info); + if (always_sensitive) + g_object_set (renderer, "sensitive", TRUE, NULL); else - time_mtime = 0; - - if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || - impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) - sensitive = is_folder; - } + gtk_tree_view_column_add_attribute (column, renderer, "sensitive", MODEL_COL_IS_FOLDER); + } + g_list_free (list); + + /* size */ + column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_files_tree_view), 1); + list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column)); + renderer = list->data; + gtk_tree_view_column_set_attributes (column, renderer, + "text", MODEL_COL_SIZE_TEXT, + NULL); + if (always_sensitive) + g_object_set (renderer, "sensitive", TRUE, NULL); else - { - GFileInfo *info; - - info = get_list_file_info (impl, iter); - if (!info) - { - g_object_set (cell, - "text", "", - "sensitive", TRUE, - NULL); - return; - } - - g_file_info_get_modification_time (info, &timeval); - time_mtime = timeval.tv_sec; - - if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER || - impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER) - sensitive = _gtk_file_info_consider_as_directory (info); - } - - if (G_UNLIKELY (time_mtime == 0)) - date_str = g_strdup (_("Unknown")); + gtk_tree_view_column_add_attribute (column, renderer, "sensitive", MODEL_COL_IS_FOLDER); + g_list_free (list); + + /* mtime */ + column = gtk_tree_view_get_column (GTK_TREE_VIEW (impl->browse_files_tree_view), 2); + list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column)); + renderer = list->data; + gtk_tree_view_column_set_attributes (column, renderer, + "text", MODEL_COL_MTIME_TEXT, + NULL); + if (always_sensitive) + g_object_set (renderer, "sensitive", TRUE, NULL); else - { - GDate mtime, now; - gint days_diff; - struct tm tm_mtime; - time_t time_now; - const gchar *format; - gchar *locale_format = NULL; - gchar buf[256]; - -#ifdef HAVE_LOCALTIME_R - localtime_r ((time_t *) &time_mtime, &tm_mtime); -#else - { - struct tm *ptm = localtime ((time_t *) &timeval.tv_sec); - - if (!ptm) - { - g_warning ("ptm != NULL failed"); - - g_object_set (cell, - "text", _("Unknown"), - "sensitive", sensitive, - NULL); - return; - } - else - memcpy ((void *) &tm_mtime, (void *) ptm, sizeof (struct tm)); - } -#endif /* HAVE_LOCALTIME_R */ - - g_date_set_time_t (&mtime, time_mtime); - time_now = time (NULL); - g_date_set_time_t (&now, time_now); - - days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime); - - /* Translators: %H means "hours" and %M means "minutes" */ - if (days_diff == 0) - format = _("%H:%M"); - else if (days_diff == 1) - format = _("Yesterday at %H:%M"); - else - { - if (days_diff > 1 && days_diff < 7) - format = "%A"; /* Days from last week */ - else - format = "%x"; /* Any other date */ - } - -#ifdef G_OS_WIN32 - /* g_locale_from_utf8() returns a string in the system - * code-page, which is not always the same as that used by the C - * library. For instance when running a GTK+ program with - * LANG=ko on an English version of Windows, the system - * code-page is 1252, but the code-page used by the C library is - * 949. (It's GTK+ itself that sets the C library locale when it - * notices the LANG environment variable. See gtkmain.c The - * Microsoft C library doesn't look at any locale environment - * variables.) We need to pass strftime() a string in the C - * library's code-page. See bug #509885. - */ - locale = setlocale (LC_ALL, NULL); - if (locale != NULL) - dot = strchr (locale, '.'); - if (dot != NULL) - { - codepage = g_ascii_strtoll (dot+1, NULL, 10); - - /* All codepages should fit in 16 bits AFAIK */ - if (codepage > 0 && codepage < 65536) - { - sprintf (charset, "CP%u", (guint) codepage); - locale_format = g_convert (format, -1, charset, "UTF-8", NULL, NULL, NULL); - } - } -#else - locale_format = g_locale_from_utf8 (format, -1, NULL, NULL, NULL); -#endif - if (locale_format != NULL && - strftime (buf, sizeof (buf), locale_format, &tm_mtime) != 0) - { -#ifdef G_OS_WIN32 - /* As above but in opposite direction... */ - if (codepage > 0 && codepage < 65536) - date_str = g_convert (buf, -1, "UTF-8", charset, NULL, NULL, NULL); -#else - date_str = g_locale_to_utf8 (buf, -1, NULL, NULL, NULL); -#endif - } - - if (date_str == NULL) - date_str = g_strdup (_("Unknown")); - - g_free (locale_format); - } - - g_object_set (cell, - "text", date_str, - "sensitive", sensitive, - NULL); - g_free (date_str); + gtk_tree_view_column_add_attribute (column, renderer, "sensitive", MODEL_COL_IS_FOLDER); + g_list_free (list); } GtkWidget * @@ -11705,172 +10348,3 @@ shortcuts_pane_model_filter_new (GtkFileChooserDefault *impl, return GTK_TREE_MODEL (model); } - - -static gboolean -recent_model_sort_row_draggable (GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - RecentModelSort *model; - GtkTreeIter iter, child_iter; - gboolean is_folder; - - model = RECENT_MODEL_SORT (drag_source); - if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path)) - return FALSE; - - recent_get_valid_child_iter (model->impl, &child_iter, &iter); - gtk_tree_model_get (GTK_TREE_MODEL (model->impl->recent_model), &child_iter, - RECENT_MODEL_COL_IS_FOLDER, &is_folder, - -1); - - return is_folder; -} - -static gboolean -recent_model_sort_drag_data_get (GtkTreeDragSource *drag_source, - GtkTreePath *path, - GtkSelectionData *selection_data) -{ - RecentModelSort *model; - GtkTreeIter iter, child_iter; - GFile *file; - gchar *uris[2]; - - model = RECENT_MODEL_SORT (drag_source); - if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path)) - return FALSE; - - recent_get_valid_child_iter (model->impl, &child_iter, &iter); - gtk_tree_model_get (GTK_TREE_MODEL (model->impl->recent_model), &child_iter, - RECENT_MODEL_COL_FILE, &file, - -1); - g_assert (file != NULL); - - uris[0] = g_file_get_uri (file); - uris[1] = NULL; - - gtk_selection_data_set_uris (selection_data, uris); - - g_free (uris[0]); - - return TRUE; -} - -static void -recent_model_sort_drag_source_iface_init (GtkTreeDragSourceIface *iface) -{ - iface->row_draggable = recent_model_sort_row_draggable; - iface->drag_data_get = recent_model_sort_drag_data_get; -} - -static void -_recent_model_sort_class_init (RecentModelSortClass *klass) -{ - -} - -static void -_recent_model_sort_init (RecentModelSort *model) -{ - model->impl = NULL; -} - -static GtkTreeModel * -recent_model_sort_new (GtkFileChooserDefault *impl, - GtkTreeModel *child_model) -{ - RecentModelSort *model; - - model = g_object_new (RECENT_MODEL_SORT_TYPE, - "model", child_model, - NULL); - model->impl = impl; - - return GTK_TREE_MODEL (model); -} - - - -static gboolean -search_model_sort_row_draggable (GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - SearchModelSort *model; - GtkTreeIter iter, child_iter; - gboolean is_folder; - - model = SEARCH_MODEL_SORT (drag_source); - if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path)) - return FALSE; - - search_get_valid_child_iter (model->impl, &child_iter, &iter); - gtk_tree_model_get (GTK_TREE_MODEL (model->impl->search_model), &child_iter, - SEARCH_MODEL_COL_IS_FOLDER, &is_folder, - -1); - - return is_folder; -} - -static gboolean -search_model_sort_drag_data_get (GtkTreeDragSource *drag_source, - GtkTreePath *path, - GtkSelectionData *selection_data) -{ - SearchModelSort *model; - GtkTreeIter iter, child_iter; - GFile *file; - gchar *uris[2]; - - model = SEARCH_MODEL_SORT (drag_source); - if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path)) - return FALSE; - - search_get_valid_child_iter (model->impl, &child_iter, &iter); - gtk_tree_model_get (GTK_TREE_MODEL (model->impl->search_model), &child_iter, - RECENT_MODEL_COL_FILE, &file, - -1); - g_assert (file != NULL); - - uris[0] = g_file_get_uri (file); - uris[1] = NULL; - - gtk_selection_data_set_uris (selection_data, uris); - - g_free (uris[0]); - - return TRUE; -} - -static void -search_model_sort_drag_source_iface_init (GtkTreeDragSourceIface *iface) -{ - iface->row_draggable = search_model_sort_row_draggable; - iface->drag_data_get = search_model_sort_drag_data_get; -} - -static void -_search_model_sort_class_init (SearchModelSortClass *klass) -{ - -} - -static void -_search_model_sort_init (SearchModelSort *model) -{ - model->impl = NULL; -} - -static GtkTreeModel * -search_model_sort_new (GtkFileChooserDefault *impl, - GtkTreeModel *child_model) -{ - SearchModelSort *model; - - model = g_object_new (SEARCH_MODEL_SORT_TYPE, - "model", child_model, - NULL); - model->impl = impl; - - return GTK_TREE_MODEL (model); -} diff --git a/gtk/gtkfilechooserdialog.c b/gtk/gtkfilechooserdialog.c index 0868691e22..2dd3737154 100644 --- a/gtk/gtkfilechooserdialog.c +++ b/gtk/gtkfilechooserdialog.c @@ -224,6 +224,11 @@ file_chooser_widget_response_requested (GtkWidget *widget, { GList *children, *l; + dialog->priv->response_requested = TRUE; + + if (gtk_window_activate_default (GTK_WINDOW (dialog))) + return; + /* There probably isn't a default widget, so make things easier for the * programmer by looking for a reasonable button on our own. */ @@ -239,12 +244,14 @@ file_chooser_widget_response_requested (GtkWidget *widget, response_id = gtk_dialog_get_response_for_widget (GTK_DIALOG (dialog), widget); if (is_stock_accept_response_id (response_id)) { - dialog->priv->response_requested = TRUE; gtk_widget_activate (widget); /* Should we gtk_dialog_response (dialog, response_id) instead? */ break; } } + if (l == NULL) + dialog->priv->response_requested = FALSE; + g_list_free (children); } diff --git a/gtk/gtkfilechooserprivate.h b/gtk/gtkfilechooserprivate.h index be10757c29..9f02b42e67 100644 --- a/gtk/gtkfilechooserprivate.h +++ b/gtk/gtkfilechooserprivate.h @@ -194,17 +194,13 @@ struct _GtkFileChooserDefault GtkWidget *search_entry; GtkSearchEngine *search_engine; GtkQuery *search_query; - GtkListStore *search_model; - GtkTreeModelFilter *search_model_filter; - GtkTreeModelSort *search_model_sort; + GtkFileSystemModel *search_model; /* OPERATION_MODE_RECENT */ GtkWidget *recent_hbox; GtkRecentManager *recent_manager; - GtkListStore *recent_model; + GtkFileSystemModel *recent_model; guint load_recent_id; - GtkTreeModelFilter *recent_model_filter; - GtkTreeModelSort *recent_model_sort; GtkWidget *filter_combo_hbox; GtkWidget *filter_combo; @@ -232,14 +228,11 @@ struct _GtkFileChooserDefault */ GtkTreeModel *shortcuts_combo_filter_model; - GtkTreeModelSort *sort_model; - /* Handles */ GSList *loading_shortcuts; GSList *reload_icon_cancellables; GCancellable *file_list_drag_data_received_cancellable; GCancellable *update_current_folder_cancellable; - GCancellable *show_and_select_files_cancellable; GCancellable *should_respond_get_info_cancellable; GCancellable *file_exists_get_info_cancellable; GCancellable *update_from_entry_cancellable; @@ -308,6 +301,7 @@ struct _GtkFileChooserDefault guint has_search : 1; guint has_recent : 1; guint show_size_column : 1; + guint create_folders : 1; #if 0 guint shortcuts_drag_outside : 1; @@ -315,63 +309,6 @@ struct _GtkFileChooserDefault }; -/* GtkFileSystemModel private */ - -typedef struct _FileModelNode FileModelNode; - -struct _GtkFileSystemModel -{ - GObject parent_instance; - - GtkFileSystem *file_system; - gchar *attributes; - FileModelNode *roots; - GtkFolder *root_folder; - GFile *root_file; - - GtkFileSystemModelFilter filter_func; - gpointer filter_data; - - GSList *idle_clears; - GSource *idle_clear_source; - - gushort max_depth; - - GSList *pending_cancellables; - - guint show_hidden : 1; - guint show_folders : 1; - guint show_files : 1; - guint folders_only : 1; - guint has_editable : 1; -}; - -struct _FileModelNode -{ - GFile *file; - FileModelNode *next; - - GFileInfo *info; - GtkFolder *folder; - - FileModelNode *children; - FileModelNode *parent; - GtkFileSystemModel *model; - - guint ref_count; - guint n_referenced_children; - - gushort depth; - - guint has_dummy : 1; - guint is_dummy : 1; - guint is_visible : 1; - guint loaded : 1; - guint idle_clear : 1; - guint load_pending : 1; -}; - - G_END_DECLS #endif /* __GTK_FILE_CHOOSER_PRIVATE_H__ */ diff --git a/gtk/gtkfilechooserutils.c b/gtk/gtkfilechooserutils.c index 0eab525b3f..69c71d1116 100644 --- a/gtk/gtkfilechooserutils.c +++ b/gtk/gtkfilechooserutils.c @@ -117,6 +117,9 @@ _gtk_file_chooser_install_properties (GObjectClass *klass) g_object_class_override_property (klass, GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION, "do-overwrite-confirmation"); + g_object_class_override_property (klass, + GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS, + "create-folders"); } /** diff --git a/gtk/gtkfilechooserutils.h b/gtk/gtkfilechooserutils.h index c5c1aa75aa..a590cccd16 100644 --- a/gtk/gtkfilechooserutils.h +++ b/gtk/gtkfilechooserutils.h @@ -41,7 +41,8 @@ typedef enum { GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE, GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN, GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION, - GTK_FILE_CHOOSER_PROP_LAST = GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION + GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS, + GTK_FILE_CHOOSER_PROP_LAST = GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS } GtkFileChooserProp; void _gtk_file_chooser_install_properties (GObjectClass *klass); diff --git a/gtk/gtkfilesel.c b/gtk/gtkfilesel.c index 0aa9f17d91..eca76ca910 100644 --- a/gtk/gtkfilesel.c +++ b/gtk/gtkfilesel.c @@ -1355,8 +1355,10 @@ gtk_file_selection_create_dir_confirmed (GtkWidget *widget, if (g_mkdir (sys_full_path, 0777) < 0) { + int errsv = errno; + buf = g_strdup_printf (_("Error creating folder '%s': %s"), - dirname, g_strerror (errno)); + dirname, g_strerror (errsv)); gtk_file_selection_fileop_error (fs, buf); } @@ -1484,8 +1486,10 @@ gtk_file_selection_delete_file_response (GtkDialog *dialog, if (g_unlink (sys_full_path) < 0) { + int errsv = errno; + buf = g_strdup_printf (_("Error deleting file '%s': %s"), - fs->fileop_file, g_strerror (errno)); + fs->fileop_file, g_strerror (errsv)); gtk_file_selection_fileop_error (fs, buf); } @@ -1602,9 +1606,11 @@ gtk_file_selection_rename_file_confirmed (GtkWidget *widget, if (g_rename (sys_old_filename, sys_new_filename) < 0) { + int errsv = errno; + buf = g_strdup_printf (_("Error renaming file \"%s\" to \"%s\": %s"), sys_old_filename, sys_new_filename, - g_strerror (errno)); + g_strerror (errsv)); gtk_file_selection_fileop_error (fs, buf); goto out2; } diff --git a/gtk/gtkfilesystemmodel.c b/gtk/gtkfilesystemmodel.c index 1d0b736d07..095969bdc6 100644 --- a/gtk/gtkfilesystemmodel.c +++ b/gtk/gtkfilesystemmodel.c @@ -19,19 +19,159 @@ */ #include "config.h" -#include <string.h> -#include "gtkfilechooserprivate.h" #include "gtkfilesystemmodel.h" + +#include <stdlib.h> +#include <string.h> + #include "gtkfilesystem.h" #include "gtkintl.h" #include "gtkmarshalers.h" +#include "gtktreedatalist.h" #include "gtktreednd.h" #include "gtktreemodel.h" #include "gtkalias.h" +/*** Structure: how GtkFileSystemModel works + * + * This is a custom GtkTreeModel used to hold a collection of files for GtkFileChooser. There are two use cases: + * + * 1. The model populates itself from a folder, using the GIO file enumerator API. This happens if you use + * _gtk_file_system_model_new_for_directory(). This is the normal usage for showing the contents of a folder. + * + * 2. The caller populates the model by hand, with files not necessarily in the same folder. This happens + * if you use _gtk_file_system_model_new() and then _gtk_file_system_model_add_and_query_file(). This is + * the special kind of usage for "search" and "recent-files", where the file chooser gives the model the + * files to be displayed. + * + * Each file is kept in a FileModelNode structure. Each FileModelNode holds a GFile* and other data. All the + * node structures have the same size, determined at runtime, depending on the number of columns that were passed + * to _gtk_file_system_model_new() or _gtk_file_system_model_new_for_directory() (that is, the size of a node is + * not sizeof (FileModelNode), but rather model->node_size). The last field in the FileModelNode structure, + * node->values[], is an array of GValue, used to hold the data for those columns. + * + * The model stores an array of FileModelNode structures in model->files. This is a GArray where each element is + * model->node_size bytes in size (the model computes that node size when initializing itself). There are + * convenience macros, get_node() and node_index(), to access that array based on an array index or a pointer to + * a node inside the array. + * + * The model accesses files through two of its fields: + * + * model->files - GArray of FileModelNode structures. + * + * model->file_lookup - hash table that maps a GFile* to an index inside the model->files array. + * + * The model->file_lookup hash table is populated lazily. It is both accessed and populated with the + * node_get_for_file() function. The invariant is that the files in model->files[n] for n < g_hash_table_size + * (model->file_lookup) are already added to the hash table. The hash table will get cleared when we re-sort the + * files, as the array will be in a different order and the indexes need to be rebuilt. + * + * Each FileModelNode has a node->visible field, which indicates whether the node is visible in the GtkTreeView. + * A node may be invisible if, for example, it corresponds to a hidden file and the file chooser is not showing + * hidden files. + * + * Since not all nodes in the model->files array may be visible, we need a way to map visible row indexes from + * the treeview to array indexes in our array of files. And thus we introduce a bit of terminology: + * + * index - An index in the model->files array. All variables/fields that represent indexes are either called + * "index" or "i_*", or simply "i" for things like loop counters. + * + * row - An index in the GtkTreeView, i.e. the index of a row within the outward-facing API of the + * GtkFileSystemModel. However, note that our rows are 1-based, not 0-based, for the reason explained in the + * following paragraph. Variables/fields that represent visible rows are called "row", or "r_*", or simply + * "r". + * + * Each FileModelNode has a node->row field which is the number of visible rows in the treeview, *before and + * including* that node. This means that node->row is 1-based, instead of 0-based --- this makes some code + * simpler, believe it or not :) This also means that when the calling GtkTreeView gives us a GtkTreePath, we + * turn the 0-based treepath into a 1-based row for our purposes. If a node is not visible, it will have the + * same row number as its closest preceding visible node. + * + * We try to compute the node->row fields lazily. A node is said to be "valid" if its node->row is accurate. + * For this, the model keeps a model->n_nodes_valid field which is the count of valid nodes starting from the + * beginning of the model->files array. When a node changes its information, or when a node gets deleted, that + * node and the following ones get invalidated by simply setting model->n_nodes_valid to the array index of the + * node. If the model happens to need a node's row number and that node is in the model->files array after + * model->n_nodes_valid, then the nodes get re-validated up to the sought node. See node_validate_rows() for + * this logic. + * + * You never access a node->row directly. Instead, call node_get_tree_row(). That function will validate the nodes + * up to the sought one if the node is not valid yet, and it will return a proper 0-based row. + */ + +/*** DEFINES ***/ + +/* priority used for all async callbacks in the main loop + * This should be higher than redraw priorities so multiple callbacks + * firing can be handled without intermediate redraws */ +#define IO_PRIORITY G_PRIORITY_DEFAULT + +/* random number that everyone else seems to use, too */ +#define FILES_PER_QUERY 100 + +typedef struct _FileModelNode FileModelNode; typedef struct _GtkFileSystemModelClass GtkFileSystemModelClass; +struct _FileModelNode +{ + GFile * file; /* file represented by this node or NULL for editable */ + GFileInfo * info; /* info for this file or NULL if unknown */ + + guint row; /* if valid (see model->n_valid_indexes), visible nodes before and including + * this one - see the "Structure" comment above. + */ + + guint visible :1; /* if the file is currently visible */ + guint frozen_add :1; /* true if the model was frozen and the entry has not been added yet */ + + GValue values[1]; /* actually n_columns values */ +}; + +struct _GtkFileSystemModel +{ + GObject parent_instance; + + GFile * dir; /* directory that's displayed */ + guint dir_thaw_source;/* GSource id for unfreezing the model */ + char * attributes; /* attributes the file info must contain, or NULL for all attributes */ + GFileMonitor * dir_monitor; /* directory that is monitored, or NULL if monitoring was not supported */ + + GCancellable * cancellable; /* cancellable in use for all operations - cancelled on dispose */ + GArray * files; /* array of FileModelNode containing all our files */ + gsize node_size; /* Size of a FileModelNode structure once its ->values field has n_columns */ + guint n_nodes_valid; /* count of valid nodes (i.e. those whose node->row is accurate) */ + GHashTable * file_lookup; /* mapping of GFile => array index in model->files + * This hash table doesn't always have the same number of entries as the files array; + * it can get cleared completely when we resort. + * The hash table gets re-populated in node_get_for_file() if this mismatch is + * detected. + */ + + guint n_columns; /* number of columns */ + GType * column_types; /* types of each column */ + GtkFileSystemModelGetValue get_func; /* function to call to fill in values in columns */ + gpointer get_data; /* data to pass to get_func */ + + GtkFileFilter * filter; /* filter to use for deciding which nodes are visible */ + + int sort_column_id; /* current sorting column */ + GtkSortType sort_order; /* current sorting order */ + GList * sort_list; /* list of sorting functions */ + GtkTreeIterCompareFunc default_sort_func; /* default sort function */ + gpointer default_sort_data; /* data to pass to default sort func */ + GDestroyNotify default_sort_destroy; /* function to call to destroy default_sort_data */ + + guint frozen; /* number of times we're frozen */ + + gboolean filter_on_thaw :1;/* set when filtering needs to happen upon thawing */ + gboolean sort_on_thaw :1;/* set when sorting needs to happen upon thawing */ + + guint show_hidden :1; /* whether to show hidden files */ + guint show_folders :1;/* whether to show folders */ + guint show_files :1; /* whether to show files */ +}; + #define GTK_FILE_SYSTEM_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_MODEL, GtkFileSystemModelClass)) #define GTK_IS_FILE_SYSTEM_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_MODEL)) #define GTK_FILE_SYSTEM_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_MODEL, GtkFileSystemModelClass)) @@ -42,314 +182,352 @@ struct _GtkFileSystemModelClass /* Signals */ - void (*finished_loading) (GtkFileSystemModel *model); + void (*finished_loading) (GtkFileSystemModel *model, GError *error); }; +static void add_file (GtkFileSystemModel *model, + GFile *file, + GFileInfo *info); +static void remove_file (GtkFileSystemModel *model, + GFile *file); -static void gtk_file_system_model_iface_init (GtkTreeModelIface *iface); -static void gtk_file_system_model_finalize (GObject *object); -static void gtk_file_system_model_dispose (GObject *object); - -static void drag_source_iface_init (GtkTreeDragSourceIface *iface); - -static GtkTreeModelFlags gtk_file_system_model_get_flags (GtkTreeModel *tree_model); -static gint gtk_file_system_model_get_n_columns (GtkTreeModel *tree_model); -static GType gtk_file_system_model_get_column_type (GtkTreeModel *tree_model, - gint index); -static gboolean gtk_file_system_model_get_iter (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreePath *path); -static GtkTreePath * gtk_file_system_model_get_path (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static void gtk_file_system_model_get_value (GtkTreeModel *tree_model, - GtkTreeIter *iter, - gint column, - GValue *value); -static gboolean gtk_file_system_model_iter_next (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static gboolean gtk_file_system_model_iter_children (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent); -static gboolean gtk_file_system_model_iter_has_child (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static gint gtk_file_system_model_iter_n_children (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static gboolean gtk_file_system_model_iter_nth_child (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent, - gint n); -static gboolean gtk_file_system_model_iter_parent (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *child); -static void gtk_file_system_model_ref_node (GtkTreeModel *tree_model, - GtkTreeIter *iter); -static void gtk_file_system_model_unref_node (GtkTreeModel *tree_model, - GtkTreeIter *iter); - -static gboolean drag_source_row_draggable (GtkTreeDragSource *drag_source, - GtkTreePath *path); -static gboolean drag_source_drag_data_get (GtkTreeDragSource *drag_source, - GtkTreePath *path, - GtkSelectionData *selection_data); - -static FileModelNode *file_model_node_new (GtkFileSystemModel *model, - GFile *file); -static void file_model_node_free (FileModelNode *node); -static void file_model_node_ref (FileModelNode *node); -static void file_model_node_unref (GtkFileSystemModel *model, - FileModelNode *node); - -static void file_model_node_idle_clear (FileModelNode *node); -static void file_model_node_idle_clear_cancel (FileModelNode *node); -static void file_model_node_child_unref (FileModelNode *parent); - -static GFileInfo * file_model_node_get_info (GtkFileSystemModel *model, - FileModelNode *node); -static gboolean file_model_node_is_visible (GtkFileSystemModel *model, - FileModelNode *node); -static void file_model_node_clear (GtkFileSystemModel *model, - FileModelNode *node); -static FileModelNode * file_model_node_get_children (GtkFileSystemModel *model, - FileModelNode *node); - -static void deleted_callback (GFile *folder, - FileModelNode *node); -static void files_added_callback (GFile *folder, - GSList *paths, - FileModelNode *node); -static void files_changed_callback (GFile *folder, - GSList *paths, - FileModelNode *node); -static void files_removed_callback (GFile *folder, - GSList *paths, - FileModelNode *node); - -static void root_deleted_callback (GFile *folder, - GtkFileSystemModel *model); -static void root_files_added_callback (GFile *folder, - GSList *paths, - GtkFileSystemModel *model); -static void root_files_changed_callback (GFile *folder, - GSList *paths, - GtkFileSystemModel *model); -static void root_files_removed_callback (GFile *folder, - GSList *paths, - GtkFileSystemModel *model); +/* iter setup: + * @user_data: the model + * @user_data2: GUINT_TO_POINTER of array index of current entry + * + * All other fields are unused. Note that the array index does not corrspond + * 1:1 with the path index as entries might not be visible. + */ +#define ITER_INDEX(iter) GPOINTER_TO_UINT((iter)->user_data2) +#define ITER_IS_VALID(model, iter) ((model) == (iter)->user_data) +#define ITER_INIT_FROM_INDEX(model, _iter, _index) G_STMT_START {\ + g_assert (_index < (model)->files->len); \ + (_iter)->user_data = (model); \ + (_iter)->user_data2 = GUINT_TO_POINTER (_index); \ +}G_STMT_END -/* Signal IDs */ -enum { - FINISHED_LOADING, - LAST_SIGNAL -}; +/*** FileModelNode ***/ -static guint file_system_model_signals[LAST_SIGNAL] = { 0 }; +/* Get a FileModelNode structure given an index in the model->files array of nodes */ +#define get_node(_model, _index) ((FileModelNode *) ((_model)->files->data + (_index) * (_model)->node_size)) - - -G_DEFINE_TYPE_WITH_CODE (GtkFileSystemModel, _gtk_file_system_model, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, - gtk_file_system_model_iface_init) - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, - drag_source_iface_init)) +/* Get an index within the model->files array of nodes, given a FileModelNode* */ +#define node_index(_model, _node) (((gchar *) (_node) - (_model)->files->data) / (_model)->node_size) +/* @up_to_index: smallest model->files array index that will be valid after this call + * @up_to_row: smallest node->row that will be valid after this call + * + * If you want to validate up to an index or up to a row, specify the index or + * the row you want and specify G_MAXUINT for the other argument. Pass + * G_MAXUINT for both arguments for "validate everything". + */ static void -_gtk_file_system_model_class_init (GtkFileSystemModelClass *class) +node_validate_rows (GtkFileSystemModel *model, guint up_to_index, guint up_to_row) { - GObjectClass *gobject_class = G_OBJECT_CLASS (class); + guint i, row; - gobject_class->finalize = gtk_file_system_model_finalize; - gobject_class->dispose = gtk_file_system_model_dispose; + if (model->files->len == 0) + return; - file_system_model_signals[FINISHED_LOADING] = - g_signal_new (I_("finished-loading"), - G_OBJECT_CLASS_TYPE (gobject_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkFileSystemModelClass, finished_loading), - NULL, NULL, - _gtk_marshal_VOID__VOID, - G_TYPE_NONE, 0); + up_to_index = MIN (up_to_index, model->files->len - 1); + + i = model->n_nodes_valid; + if (i != 0) + row = get_node (model, i - 1)->row; + else + row = 0; + + while (i <= up_to_index && row <= up_to_row) + { + FileModelNode *node = get_node (model, i); + if (node->visible) + row++; + node->row = row; + i++; + } + model->n_nodes_valid = i; +} + +static guint +node_get_tree_row (GtkFileSystemModel *model, guint index) +{ + if (model->n_nodes_valid <= index) + node_validate_rows (model, index, G_MAXUINT); + + return get_node (model, index)->row - 1; +} + +static void +node_invalidate_index (GtkFileSystemModel *model, guint id) +{ + model->n_nodes_valid = MIN (model->n_nodes_valid, id); +} + +static GtkTreePath * +gtk_tree_path_new_from_node (GtkFileSystemModel *model, guint id) +{ + guint i = node_get_tree_row (model, id); + + g_assert (i < model->files->len); + + return gtk_tree_path_new_from_indices (i, -1); } static void -gtk_file_system_model_iface_init (GtkTreeModelIface *iface) +emit_row_inserted_for_node (GtkFileSystemModel *model, guint id) { - iface->get_flags = gtk_file_system_model_get_flags; - iface->get_n_columns = gtk_file_system_model_get_n_columns; - iface->get_column_type = gtk_file_system_model_get_column_type; - iface->get_iter = gtk_file_system_model_get_iter; - iface->get_path = gtk_file_system_model_get_path; - iface->get_value = gtk_file_system_model_get_value; - iface->iter_next = gtk_file_system_model_iter_next; - iface->iter_children = gtk_file_system_model_iter_children; - iface->iter_has_child = gtk_file_system_model_iter_has_child; - iface->iter_n_children = gtk_file_system_model_iter_n_children; - iface->iter_nth_child = gtk_file_system_model_iter_nth_child; - iface->iter_parent = gtk_file_system_model_iter_parent; - iface->ref_node = gtk_file_system_model_ref_node; - iface->unref_node = gtk_file_system_model_unref_node; + GtkTreePath *path; + GtkTreeIter iter; + + path = gtk_tree_path_new_from_node (model, id); + ITER_INIT_FROM_INDEX (model, &iter, id); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter); + gtk_tree_path_free (path); } static void -_gtk_file_system_model_init (GtkFileSystemModel *model) +emit_row_changed_for_node (GtkFileSystemModel *model, guint id) { - model->show_files = TRUE; - model->show_folders = TRUE; - model->show_hidden = FALSE; + GtkTreePath *path; + GtkTreeIter iter; + + path = gtk_tree_path_new_from_node (model, id); + ITER_INIT_FROM_INDEX (model, &iter, id); + gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, &iter); + gtk_tree_path_free (path); } static void -gtk_file_system_model_finalize (GObject *object) +emit_row_deleted_for_row (GtkFileSystemModel *model, guint row) { - GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object); - FileModelNode *children, *next; + GtkTreePath *path; - if (model->root_folder) - g_object_unref (model->root_folder); + path = gtk_tree_path_new_from_indices (row, -1); + gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path); + gtk_tree_path_free (path); +} - if (model->root_file) - g_object_unref (model->root_file); +static void +node_set_visible (GtkFileSystemModel *model, guint id, gboolean visible) +{ + FileModelNode *node = get_node (model, id); - if (model->file_system) - g_object_unref (model->file_system); + if (node->visible == visible || + node->frozen_add) + return; - children = model->roots; - while (children) + if (visible) { - next = children->next; - file_model_node_free (children); - children = next; + node->visible = TRUE; + node_invalidate_index (model, id); + emit_row_inserted_for_node (model, id); } + else + { + guint row; - g_free (model->attributes); + row = node_get_tree_row (model, id); + g_assert (row < model->files->len); - G_OBJECT_CLASS (_gtk_file_system_model_parent_class)->finalize (object); + node->visible = FALSE; + node_invalidate_index (model, id); + emit_row_deleted_for_row (model, row); + } } - -static void -gtk_file_system_model_dispose (GObject *object) +static gboolean +node_should_be_visible (GtkFileSystemModel *model, guint id) { - GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object); + FileModelNode *node = get_node (model, id); + GtkFileFilterInfo filter_info = { 0, }; + GtkFileFilterFlags required; + gboolean is_folder, result; + char *mime_type = NULL; + char *filename = NULL; + char *uri = NULL; + + if (node->info == NULL) + return FALSE; + + if (!model->show_hidden && + (g_file_info_get_is_hidden (node->info) || g_file_info_get_is_backup (node->info))) + return FALSE; + + is_folder = _gtk_file_info_consider_as_directory (node->info); + + /* wtf? */ + if (model->show_folders != model->show_files && + model->show_folders != is_folder) + return FALSE; - if (model->pending_cancellables) + if (is_folder) + return TRUE; + + if (model->filter == NULL) + return TRUE; + + /* fill info */ + required = gtk_file_filter_get_needed (model->filter); + + filter_info.contains = GTK_FILE_FILTER_DISPLAY_NAME; + filter_info.display_name = g_file_info_get_display_name (node->info); + + if (required & GTK_FILE_FILTER_MIME_TYPE) { - GSList *l; + const char *s = g_file_info_get_content_type (node->info); + if (s) + { + mime_type = g_content_type_get_mime_type (s); + if (mime_type) + { + filter_info.mime_type = mime_type; + filter_info.contains |= GTK_FILE_FILTER_MIME_TYPE; + } + } + } - for (l = model->pending_cancellables; l; l = l->next) - g_cancellable_cancel (l->data); - g_slist_free (model->pending_cancellables); - model->pending_cancellables = NULL; + if (required & GTK_FILE_FILTER_FILENAME) + { + filename = g_file_get_path (node->file); + if (filename) + { + filter_info.filename = filename; + filter_info.contains |= GTK_FILE_FILTER_FILENAME; + } } - G_OBJECT_CLASS (_gtk_file_system_model_parent_class)->dispose (object); -} + if (required & GTK_FILE_FILTER_URI) + { + uri = g_file_get_uri (node->file); + if (uri) + { + filter_info.uri = uri; + filter_info.contains |= GTK_FILE_FILTER_URI; + } + } -static void -drag_source_iface_init (GtkTreeDragSourceIface *iface) -{ - iface->row_draggable = drag_source_row_draggable; - iface->drag_data_get = drag_source_drag_data_get; - iface->drag_data_delete = NULL; + result = gtk_file_filter_filter (model->filter, &filter_info); + + g_free (mime_type); + g_free (filename); + g_free (uri); + + return result; } -/* - * ******************** GtkTreeModel methods ******************** - */ +/*** GtkTreeModel ***/ static GtkTreeModelFlags gtk_file_system_model_get_flags (GtkTreeModel *tree_model) { - GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model); - GtkTreeModelFlags flags = GTK_TREE_MODEL_ITERS_PERSIST; - - if (model->max_depth == 0) - flags |= GTK_TREE_MODEL_LIST_ONLY; - - return flags; + /* GTK_TREE_MODEL_ITERS_PERSIST doesn't work with arrays :( */ + return GTK_TREE_MODEL_LIST_ONLY; } static gint gtk_file_system_model_get_n_columns (GtkTreeModel *tree_model) { - return GTK_FILE_SYSTEM_MODEL_N_COLUMNS; + GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model); + + return model->n_columns; } static GType gtk_file_system_model_get_column_type (GtkTreeModel *tree_model, - gint index) + gint i) { - switch (index) - { - case GTK_FILE_SYSTEM_MODEL_INFO: - return G_TYPE_FILE_INFO; - case GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME: - return G_TYPE_STRING; - default: - g_assert_not_reached (); - return G_TYPE_NONE; - } + GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model); + + g_return_val_if_fail (i >= 0 && (guint) i < model->n_columns, G_TYPE_NONE); + + return model->column_types[i]; } -static gboolean -gtk_file_system_model_get_iter (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreePath *path) +static int +compare_indices (gconstpointer key, gconstpointer _node) { - GtkTreeIter parent; - gint *indices; - gint depth, i; + const FileModelNode *node = _node; + + return GPOINTER_TO_UINT (key) - node->row; +} - indices = gtk_tree_path_get_indices (path); - depth = gtk_tree_path_get_depth (path); +static gboolean +gtk_file_system_model_iter_nth_child (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *parent, + gint n) +{ + GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model); + char *node; + guint id; + guint row_to_find; - g_return_val_if_fail (depth > 0, FALSE); + g_return_val_if_fail (n >= 0, FALSE); - if (!gtk_tree_model_iter_nth_child (tree_model, iter, NULL, indices[0])) + if (parent != NULL) return FALSE; - for (i = 1; i < depth; i++) + row_to_find = n + 1; /* plus one as our node->row numbers are 1-based; see the "Structure" comment at the beginning */ + + if (model->n_nodes_valid > 0 && + get_node (model, model->n_nodes_valid - 1)->row >= row_to_find) + { + /* Fast path - the nodes are valid up to the sought one. + * + * First, find a node with the sought row number...*/ + + node = bsearch (GUINT_TO_POINTER (row_to_find), + model->files->data, + model->n_nodes_valid, + model->node_size, + compare_indices); + if (node == NULL) + return FALSE; + + /* ... Second, back up until we find the first visible node with that row number */ + + id = node_index (model, node); + while (!get_node (model, id)->visible) + id--; + + g_assert (get_node (model, id)->row == row_to_find); + } + else { - parent = *iter; - if (!gtk_tree_model_iter_nth_child (tree_model, iter, &parent, indices[i])) - return FALSE; + /* Slow path - the nodes need to be validated up to the sought one */ + + node_validate_rows (model, G_MAXUINT, n); /* note that this is really "n", not row_to_find - see node_validate_rows() */ + id = model->n_nodes_valid - 1; + if (model->n_nodes_valid == 0 || get_node (model, id)->row != row_to_find) + return FALSE; } + ITER_INIT_FROM_INDEX (model, iter, id); return TRUE; } +static gboolean +gtk_file_system_model_get_iter (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreePath *path) +{ + g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE); + + return gtk_file_system_model_iter_nth_child (tree_model, + iter, + NULL, + gtk_tree_path_get_indices (path)[0]); +} + static GtkTreePath * gtk_file_system_model_get_path (GtkTreeModel *tree_model, GtkTreeIter *iter) { GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model); - FileModelNode *node = iter->user_data; - - GtkTreePath *result = gtk_tree_path_new (); - - while (node) - { - FileModelNode *parent = node->parent; - FileModelNode *children; - int n = 0; - - if (parent) - children = parent->children; - else - children = model->roots; - - while (children != node) - { - if (children->is_visible) - n++; - children = children->next; - } - gtk_tree_path_prepend_index (result, n); + g_return_val_if_fail (ITER_IS_VALID (model, iter), NULL); - node = parent; - } - - return result; + return gtk_tree_path_new_from_node (model, ITER_INDEX (iter)); } static void @@ -359,51 +537,42 @@ gtk_file_system_model_get_value (GtkTreeModel *tree_model, GValue *value) { GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model); - FileModelNode *node = iter->user_data; - GFileInfo *info; + const GValue *original; - switch (column) - { - case GTK_FILE_SYSTEM_MODEL_INFO: - if (model->has_editable && node == model->roots) - info = NULL; - else - info = file_model_node_get_info (model, node); + g_return_if_fail ((guint) column < model->n_columns); + g_return_if_fail (ITER_IS_VALID (model, iter)); - g_value_set_object (value, info); - break; - case GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME: - { - g_value_init (value, G_TYPE_STRING); - - if (model->has_editable && node == model->roots) - g_value_set_static_string (value, ""); - else - { - GFileInfo *info = file_model_node_get_info (model, node); - - g_value_set_string (value, g_file_info_get_display_name (info)); - } - } - break; - default: - g_assert_not_reached (); + original = _gtk_file_system_model_get_value (model, iter, column); + if (original) + { + g_value_init (value, G_VALUE_TYPE (original)); + g_value_copy (original, value); } + else + g_value_init (value, model->column_types[column]); } static gboolean gtk_file_system_model_iter_next (GtkTreeModel *tree_model, GtkTreeIter *iter) { - FileModelNode *node = iter->user_data; + GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model); + guint i; - node = node->next; - while (node && !node->is_visible) - node = node->next; - - iter->user_data = node; + g_return_val_if_fail (ITER_IS_VALID (model, iter), FALSE); + + for (i = ITER_INDEX (iter) + 1; i < model->files->len; i++) + { + FileModelNode *node = get_node (model, i); - return node != NULL; + if (node->visible) + { + ITER_INIT_FROM_INDEX (model, iter, i); + return TRUE; + } + } + + return FALSE; } static gboolean @@ -411,41 +580,14 @@ gtk_file_system_model_iter_children (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent) { - GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model); - FileModelNode *children; - - if (parent) - { - FileModelNode *parent_node = parent->user_data; - children = file_model_node_get_children (model, parent_node); - } - else - { - children = model->roots; - } - - while (children && !children->is_visible) - children = children->next; - - iter->user_data = children; - - return children != NULL; + return FALSE; } static gboolean gtk_file_system_model_iter_has_child (GtkTreeModel *tree_model, GtkTreeIter *iter) { - FileModelNode *node = iter->user_data; - GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model); - - if (node->depth == model->max_depth) - return FALSE; - else - { - GFileInfo *info = file_model_node_get_info (model, node); - return _gtk_file_info_consider_as_directory (info); - } + return FALSE; } static gint @@ -453,110 +595,305 @@ gtk_file_system_model_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter) { GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model); - FileModelNode *children; - gint n = 0; if (iter) + return 0; + + return node_get_tree_row (model, model->files->len - 1) + 1; +} + +static gboolean +gtk_file_system_model_iter_parent (GtkTreeModel *tree_model, + GtkTreeIter *iter, + GtkTreeIter *child) +{ + return FALSE; +} + +static void +gtk_file_system_model_ref_node (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + /* nothing to do */ +} + +static void +gtk_file_system_model_unref_node (GtkTreeModel *tree_model, + GtkTreeIter *iter) +{ + /* nothing to do */ +} + +static void +gtk_file_system_model_iface_init (GtkTreeModelIface *iface) +{ + iface->get_flags = gtk_file_system_model_get_flags; + iface->get_n_columns = gtk_file_system_model_get_n_columns; + iface->get_column_type = gtk_file_system_model_get_column_type; + iface->get_iter = gtk_file_system_model_get_iter; + iface->get_path = gtk_file_system_model_get_path; + iface->get_value = gtk_file_system_model_get_value; + iface->iter_next = gtk_file_system_model_iter_next; + iface->iter_children = gtk_file_system_model_iter_children; + iface->iter_has_child = gtk_file_system_model_iter_has_child; + iface->iter_n_children = gtk_file_system_model_iter_n_children; + iface->iter_nth_child = gtk_file_system_model_iter_nth_child; + iface->iter_parent = gtk_file_system_model_iter_parent; + iface->ref_node = gtk_file_system_model_ref_node; + iface->unref_node = gtk_file_system_model_unref_node; +} + +/*** GtkTreeSortable ***/ + +typedef struct _SortData SortData; +struct _SortData { + GtkFileSystemModel * model; + GtkTreeIterCompareFunc func; + gpointer data; + int order; /* -1 to invert sort order or 1 to keep it */ +}; + +/* returns FALSE if no sort necessary */ +static gboolean +sort_data_init (SortData *data, GtkFileSystemModel *model) +{ + GtkTreeDataSortHeader *header; + + if (model->files->len <= 2) + return FALSE; + + switch (model->sort_column_id) { - FileModelNode *node = iter->user_data; - children = file_model_node_get_children (model, node); + case GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID: + if (!model->default_sort_func) + return FALSE; + data->func = model->default_sort_func; + data->data = model->default_sort_data; + break; + case GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID: + return FALSE; + default: + header = _gtk_tree_data_list_get_header (model->sort_list, model->sort_column_id); + if (header == NULL) + return FALSE; + data->func = header->func; + data->data = header->data; + break; } - else + + data->order = model->sort_order == GTK_SORT_DESCENDING ? -1 : 1; + data->model = model; + return TRUE; +} + +static int +compare_array_element (gconstpointer a, gconstpointer b, gpointer user_data) +{ + SortData *data = user_data; + GtkTreeIter itera, iterb; + + ITER_INIT_FROM_INDEX (data->model, &itera, node_index (data->model, a)); + ITER_INIT_FROM_INDEX (data->model, &iterb, node_index (data->model, b)); + return data->func (GTK_TREE_MODEL (data->model), &itera, &iterb, data->data) * data->order; +} + +static void +gtk_file_system_model_sort (GtkFileSystemModel *model) +{ + SortData data; + + if (model->frozen) { - children = model->roots; + model->sort_on_thaw = TRUE; + return; } - while (children) + if (sort_data_init (&data, model)) { - if (children->is_visible) - n++; - children = children->next; + GtkTreePath *path; + guint i; + guint r, n_visible_rows; + + node_validate_rows (model, G_MAXUINT, G_MAXUINT); + n_visible_rows = node_get_tree_row (model, model->files->len - 1) + 1; + model->n_nodes_valid = 0; + g_hash_table_remove_all (model->file_lookup); + g_qsort_with_data (get_node (model, 1), /* start at index 1; don't sort the editable row */ + model->files->len - 1, + model->node_size, + compare_array_element, + &data); + g_assert (model->n_nodes_valid == 0); + g_assert (g_hash_table_size (model->file_lookup) == 0); + if (n_visible_rows) + { + int *new_order = g_new (int, n_visible_rows); + + r = 0; + for (i = 0; i < model->files->len; i++) + { + FileModelNode *node = get_node (model, i); + if (!node->visible) + { + node->row = r; + continue; + } + + new_order[r] = node->row; + r++; + node->row = r; + } + g_assert (r == n_visible_rows); + path = gtk_tree_path_new (); + gtk_tree_model_rows_reordered (GTK_TREE_MODEL (model), + path, + NULL, + new_order); + gtk_tree_path_free (path); + g_free (new_order); + } } - return n; + model->sort_on_thaw = FALSE; +} + +static void +gtk_file_system_model_sort_node (GtkFileSystemModel *model, guint node) +{ + /* FIXME: improve */ + gtk_file_system_model_sort (model); } static gboolean -gtk_file_system_model_iter_nth_child (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *parent, - gint n) +gtk_file_system_model_get_sort_column_id (GtkTreeSortable *sortable, + gint *sort_column_id, + GtkSortType *order) { - GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (tree_model); - FileModelNode *children; + GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (sortable); - if (parent) - { - FileModelNode *parent_node = parent->user_data; - children = file_model_node_get_children (model, parent_node); - } - else - { - children = model->roots; - } + if (sort_column_id) + *sort_column_id = model->sort_column_id; + if (order) + *order = model->sort_order; + + if (model->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID || + model->sort_column_id == GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) + return FALSE; + + return TRUE; +} - while (children && !children->is_visible) - children = children->next; +static void +gtk_file_system_model_set_sort_column_id (GtkTreeSortable *sortable, + gint sort_column_id, + GtkSortType order) +{ + GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (sortable); - while (n && children) + if ((model->sort_column_id == sort_column_id) && + (model->sort_order == order)) + return; + + if (sort_column_id != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID) { - n--; - children = children->next; - while (children && !children->is_visible) - children = children->next; + if (sort_column_id != GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) + { + GtkTreeDataSortHeader *header = NULL; + + header = _gtk_tree_data_list_get_header (model->sort_list, + sort_column_id); + + /* We want to make sure that we have a function */ + g_return_if_fail (header != NULL); + g_return_if_fail (header->func != NULL); + } + else + { + g_return_if_fail (model->default_sort_func != NULL); + } } - iter->user_data = children; - return children != NULL; + model->sort_column_id = sort_column_id; + model->sort_order = order; + + gtk_tree_sortable_sort_column_changed (sortable); + + gtk_file_system_model_sort (model); } -static gboolean -gtk_file_system_model_iter_parent (GtkTreeModel *tree_model, - GtkTreeIter *iter, - GtkTreeIter *child) +static void +gtk_file_system_model_set_sort_func (GtkTreeSortable *sortable, + gint sort_column_id, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy) { - FileModelNode *node = child->user_data; - - node = node->parent; - iter->user_data = node; + GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (sortable); + + model->sort_list = _gtk_tree_data_list_set_header (model->sort_list, + sort_column_id, + func, data, destroy); - return node != NULL; + if (model->sort_column_id == sort_column_id) + gtk_file_system_model_sort (model); } static void -gtk_file_system_model_ref_node (GtkTreeModel *tree_model, - GtkTreeIter *iter) +gtk_file_system_model_set_default_sort_func (GtkTreeSortable *sortable, + GtkTreeIterCompareFunc func, + gpointer data, + GDestroyNotify destroy) { - file_model_node_ref (iter->user_data); + GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (sortable); + + if (model->default_sort_destroy) + { + GDestroyNotify d = model->default_sort_destroy; + + model->default_sort_destroy = NULL; + d (model->default_sort_data); + } + + model->default_sort_func = func; + model->default_sort_data = data; + model->default_sort_destroy = destroy; + + if (model->sort_column_id == GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID) + gtk_file_system_model_sort (model); +} + +static gboolean +gtk_file_system_model_has_default_sort_func (GtkTreeSortable *sortable) +{ + GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (sortable); + + return (model->default_sort_func != NULL); } static void -gtk_file_system_model_unref_node (GtkTreeModel *tree_model, - GtkTreeIter *iter) +gtk_file_system_model_sortable_init (GtkTreeSortableIface *iface) { - file_model_node_unref (GTK_FILE_SYSTEM_MODEL (tree_model), - iter->user_data); + iface->get_sort_column_id = gtk_file_system_model_get_sort_column_id; + iface->set_sort_column_id = gtk_file_system_model_set_sort_column_id; + iface->set_sort_func = gtk_file_system_model_set_sort_func; + iface->set_default_sort_func = gtk_file_system_model_set_default_sort_func; + iface->has_default_sort_func = gtk_file_system_model_has_default_sort_func; } +/*** GtkTreeDragSource ***/ + static gboolean drag_source_row_draggable (GtkTreeDragSource *drag_source, GtkTreePath *path) { - GtkFileSystemModel *model; + GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (drag_source); GtkTreeIter iter; - FileModelNode *node; - - model = GTK_FILE_SYSTEM_MODEL (drag_source); if (!gtk_file_system_model_get_iter (GTK_TREE_MODEL (model), &iter, path)) return FALSE; - if (!model->has_editable) - return TRUE; - - node = iter.user_data; - return (node != model->roots); + return ITER_INDEX (&iter) != 0; } static gboolean @@ -564,219 +901,498 @@ drag_source_drag_data_get (GtkTreeDragSource *drag_source, GtkTreePath *path, GtkSelectionData *selection_data) { - GtkFileSystemModel *model; + GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (drag_source); + FileModelNode *node; GtkTreeIter iter; - GFile *file; char *uris[2]; - model = GTK_FILE_SYSTEM_MODEL (drag_source); - if (!gtk_file_system_model_get_iter (GTK_TREE_MODEL (model), &iter, path)) return FALSE; - file = _gtk_file_system_model_get_file (model, &iter); - g_assert (file != NULL); + node = get_node (model, ITER_INDEX (&iter)); + if (node->file == NULL) + return FALSE; - uris[0] = g_file_get_uri (file); + uris[0] = g_file_get_uri (node->file); uris[1] = NULL; - gtk_selection_data_set_uris (selection_data, uris); - g_free (uris[0]); return TRUE; } -/* Callback used when the root folder finished loading */ static void -root_folder_finished_loading_cb (GFile *folder, - GtkFileSystemModel *model) +drag_source_iface_init (GtkTreeDragSourceIface *iface) +{ + iface->row_draggable = drag_source_row_draggable; + iface->drag_data_get = drag_source_drag_data_get; + iface->drag_data_delete = NULL; +} + +/*** GtkFileSystemModel ***/ + +/* Signal IDs */ +enum { + FINISHED_LOADING, + LAST_SIGNAL +}; + +static guint file_system_model_signals[LAST_SIGNAL] = { 0 }; + + + +G_DEFINE_TYPE_WITH_CODE (GtkFileSystemModel, _gtk_file_system_model, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, + gtk_file_system_model_iface_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE, + gtk_file_system_model_sortable_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, + drag_source_iface_init)) + +static void +gtk_file_system_model_dispose (GObject *object) { - g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0); + GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object); + + if (model->dir_thaw_source) + { + g_source_remove (model->dir_thaw_source); + model->dir_thaw_source = 0; + } + + g_cancellable_cancel (model->cancellable); + if (model->dir_monitor) + g_file_monitor_cancel (model->dir_monitor); + + G_OBJECT_CLASS (_gtk_file_system_model_parent_class)->dispose (object); } + static void -got_root_folder_cb (GCancellable *cancellable, - GtkFolder *folder, - const GError *error, - gpointer data) +gtk_file_system_model_finalize (GObject *object) { - gboolean cancelled = g_cancellable_is_cancelled (cancellable); - GtkFileSystemModel *model = data; - GSList *tmp_list; + GtkFileSystemModel *model = GTK_FILE_SYSTEM_MODEL (object); + guint i; - tmp_list = g_slist_find (model->pending_cancellables, cancellable); - if (!tmp_list) - goto out; + for (i = 0; i < model->files->len; i++) + { + int v; - model->pending_cancellables = g_slist_delete_link (model->pending_cancellables, - tmp_list); + FileModelNode *node = get_node (model, i); + if (node->file) + g_object_unref (node->file); + if (node->info) + g_object_unref (node->info); - if (cancelled || !folder) - goto out; + for (v = 0; v < model->n_columns; v++) + if (G_VALUE_TYPE (&node->values[v]) != G_TYPE_INVALID) + g_value_unset (&node->values[v]); + } + g_array_free (model->files, TRUE); - model->root_folder = g_object_ref (folder); + g_object_unref (model->cancellable); + g_free (model->attributes); + if (model->dir) + g_object_unref (model->dir); + if (model->dir_monitor) + g_object_unref (model->dir_monitor); + g_hash_table_destroy (model->file_lookup); + if (model->filter) + g_object_unref (model->filter); - g_signal_connect_object (model->root_folder, "finished-loading", - G_CALLBACK (root_folder_finished_loading_cb), model, 0); - g_signal_connect_object (model->root_folder, "deleted", - G_CALLBACK (root_deleted_callback), model, 0); - g_signal_connect_object (model->root_folder, "files-added", - G_CALLBACK (root_files_added_callback), model, 0); - g_signal_connect_object (model->root_folder, "files-changed", - G_CALLBACK (root_files_changed_callback), model, 0); - g_signal_connect_object (model->root_folder, "files-removed", - G_CALLBACK (root_files_removed_callback), model, 0); + g_slice_free1 (sizeof (GType) * model->n_columns, model->column_types); -out: - g_object_unref (model); - g_object_unref (cancellable); + _gtk_tree_data_list_header_free (model->sort_list); + if (model->default_sort_destroy) + model->default_sort_destroy (model->default_sort_data); + + G_OBJECT_CLASS (_gtk_file_system_model_parent_class)->finalize (object); } -/** - * _gtk_file_system_model_new: - * @file_system: an object implementing #GtkFileSystem - * @root_file: the root file path to show. - * @max_depth: the maximum depth from the children of @root_file - * or the roots of the file system to display in - * the file selector). A depth of 0 displays - * only the immediate children of @root_file, - * or the roots of the filesystem. -1 for no - * maximum depth. - * @error: location to store error, or %NULL. - * - * Creates a new #GtkFileSystemModel object. The #GtkFileSystemModel - * object wraps a #GtkFileSystem interface as a #GtkTreeModel. - * Using the @root_file and @max_depth parameters, the tree model - * can be restricted to a subportion of the entire file system. - * - * Return value: the newly created #GtkFileSystemModel object, or NULL if there - * was an error. - **/ -GtkFileSystemModel * -_gtk_file_system_model_new (GtkFileSystem *file_system, - GFile *root_file, - gint max_depth, - const gchar *attributes, - GError **error) +static void +_gtk_file_system_model_class_init (GtkFileSystemModelClass *class) { - GtkFileSystemModel *model; - GCancellable *cancellable; + GObjectClass *gobject_class = G_OBJECT_CLASS (class); - g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL); - g_return_val_if_fail (G_IS_FILE (root_file), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); + gobject_class->finalize = gtk_file_system_model_finalize; + gobject_class->dispose = gtk_file_system_model_dispose; - /* Then, actually create the model and the root nodes */ + file_system_model_signals[FINISHED_LOADING] = + g_signal_new (I_("finished-loading"), + G_OBJECT_CLASS_TYPE (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkFileSystemModelClass, finished_loading), + NULL, NULL, + _gtk_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); +} - model = g_object_new (GTK_TYPE_FILE_SYSTEM_MODEL, NULL); - model->file_system = g_object_ref (file_system); - if (max_depth < 0) - model->max_depth = G_MAXUSHORT; - else - model->max_depth = MIN (max_depth, G_MAXUSHORT); +static void +_gtk_file_system_model_init (GtkFileSystemModel *model) +{ + model->show_files = TRUE; + model->show_folders = TRUE; + model->show_hidden = FALSE; - model->attributes = g_strdup (attributes); - model->root_folder = NULL; - model->root_file = g_object_ref (root_file); + model->sort_column_id = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID; - model->roots = NULL; + model->file_lookup = g_hash_table_new (g_file_hash, (GEqualFunc) g_file_equal); + model->cancellable = g_cancellable_new (); +} - cancellable = _gtk_file_system_get_folder (file_system, root_file, - attributes, - got_root_folder_cb, - g_object_ref (model)); - if (!cancellable) - { - /* In this case got_root_folder_cb() will never be called, so we - * need to unref model twice. - */ - g_object_unref (model); - g_object_unref (model); +/*** API ***/ - g_set_error_literal (error, - GTK_FILE_CHOOSER_ERROR, - GTK_FILE_CHOOSER_ERROR_NONEXISTENT, - _("Could not obtain root folder")); +static void +gtk_file_system_model_closed_enumerator (GObject *object, GAsyncResult *res, gpointer data) +{ + g_file_enumerator_close_finish (G_FILE_ENUMERATOR (object), res, NULL); +} - return NULL; +static gboolean +thaw_func (gpointer data) +{ + GtkFileSystemModel *model = data; + + _gtk_file_system_model_thaw_updates (model); + model->dir_thaw_source = 0; + + return FALSE; +} + +static void +gtk_file_system_model_got_files (GObject *object, GAsyncResult *res, gpointer data) +{ + GFileEnumerator *enumerator = G_FILE_ENUMERATOR (object); + GtkFileSystemModel *model = data; + GList *walk, *files; + GError *error = NULL; + + gdk_threads_enter (); + + files = g_file_enumerator_next_files_finish (enumerator, res, &error); + + if (files) + { + if (model->dir_thaw_source == 0) + { + _gtk_file_system_model_freeze_updates (model); + model->dir_thaw_source = gdk_threads_add_timeout_full (IO_PRIORITY + 1, + 50, + thaw_func, + model, + NULL); + } + + for (walk = files; walk; walk = walk->next) + { + const char *name; + GFileInfo *info; + GFile *file; + + info = walk->data; + name = g_file_info_get_name (info); + if (name == NULL) + { + /* Shouldn't happen, but the APIs allow it */ + g_object_unref (info); + continue; + } + file = g_file_get_child (model->dir, name); + add_file (model, file, info); + g_object_unref (file); + g_object_unref (info); + } + g_list_free (files); + + g_file_enumerator_next_files_async (enumerator, + g_file_is_native (model->dir) ? 50 * FILES_PER_QUERY : FILES_PER_QUERY, + IO_PRIORITY, + model->cancellable, + gtk_file_system_model_got_files, + model); } + else + { + g_file_enumerator_close_async (enumerator, + IO_PRIORITY, + model->cancellable, + gtk_file_system_model_closed_enumerator, + model); + if (model->dir_thaw_source != 0) + { + g_source_remove (model->dir_thaw_source); + model->dir_thaw_source = 0; + _gtk_file_system_model_thaw_updates (model); + } - model->pending_cancellables = g_slist_append (model->pending_cancellables, cancellable); + g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0, error); - return model; + if (error) + g_error_free (error); + + g_object_unref (model); + } + + gdk_threads_leave (); } static void -model_refilter_recurse (GtkFileSystemModel *model, - FileModelNode *parent, - GtkTreePath *path) +gtk_file_system_model_query_done (GObject * object, + GAsyncResult *res, + gpointer data) { - GtkTreeModel *tree_model = GTK_TREE_MODEL (model); - int i = 0; - FileModelNode *nodes; - gboolean has_children = FALSE; + GtkFileSystemModel *model = data; /* only a valid pointer if not cancelled */ + GFile *file = G_FILE (object); + GFileInfo *info; - if (parent && !parent->loaded) + info = g_file_query_info_finish (file, res, NULL); + if (info == NULL) return; - if (parent) - nodes = parent->children; - else - nodes = model->roots; + _gtk_file_system_model_update_file (model, file, info, TRUE); +} - while (nodes) +static void +gtk_file_system_model_monitor_change (GFileMonitor * monitor, + GFile * file, + GFile * other_file, + GFileMonitorEvent type, + GtkFileSystemModel *model) +{ + switch (type) { - FileModelNode *next = nodes->next; - gboolean is_visible; - - gtk_tree_path_append_index (path, i); + case G_FILE_MONITOR_EVENT_CREATED: + case G_FILE_MONITOR_EVENT_CHANGED: + case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: + /* We can treat all of these the same way */ + g_file_query_info_async (file, + model->attributes, + G_FILE_QUERY_INFO_NONE, + IO_PRIORITY, + model->cancellable, + gtk_file_system_model_query_done, + model); + break; + case G_FILE_MONITOR_EVENT_DELETED: + remove_file (model, file); + break; + case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: + /* FIXME: use freeze/thaw with this somehow? */ + case G_FILE_MONITOR_EVENT_PRE_UNMOUNT: + case G_FILE_MONITOR_EVENT_UNMOUNTED: + default: + /* ignore these */ + break; + } +} - is_visible = file_model_node_is_visible (model, nodes); - - if (!is_visible && nodes->is_visible) - { - file_model_node_clear (model, nodes); - gtk_tree_model_row_deleted (tree_model, path); +static void +gtk_file_system_model_got_enumerator (GObject *dir, GAsyncResult *res, gpointer data) +{ + GtkFileSystemModel *model = data; + GFileEnumerator *enumerator; + GError *error = NULL; - nodes->is_visible = FALSE; - } - else if (is_visible && !nodes->is_visible) - { - GtkTreeIter iter; + gdk_threads_enter (); - iter.user_data = nodes; - nodes->is_visible = TRUE; - gtk_tree_model_row_inserted (tree_model, path, &iter); - } - else - model_refilter_recurse (model, nodes, path); + enumerator = g_file_enumerate_children_finish (G_FILE (dir), res, &error); + if (enumerator == NULL) + { + g_signal_emit (model, file_system_model_signals[FINISHED_LOADING], 0, error); + g_object_unref (model); + g_error_free (error); + } + else + { + g_file_enumerator_next_files_async (enumerator, + g_file_is_native (model->dir) ? 50 * FILES_PER_QUERY : FILES_PER_QUERY, + IO_PRIORITY, + model->cancellable, + gtk_file_system_model_got_files, + model); + g_object_unref (enumerator); + model->dir_monitor = g_file_monitor_directory (model->dir, + G_FILE_MONITOR_NONE, + model->cancellable, + NULL); /* we don't mind if directory monitoring isn't supported, so the GError is NULL here */ + if (model->dir_monitor) + g_signal_connect (model->dir_monitor, + "changed", + G_CALLBACK (gtk_file_system_model_monitor_change), + model); + } + + gdk_threads_leave (); +} + +static void +gtk_file_system_model_set_n_columns (GtkFileSystemModel *model, + gint n_columns, + va_list args) +{ + guint i; + + g_assert (model->files == NULL); + g_assert (n_columns > 0); - if (is_visible) + model->n_columns = n_columns; + model->column_types = g_slice_alloc (sizeof (GType) * n_columns); + + model->node_size = sizeof (FileModelNode) + sizeof (GValue) * (n_columns - 1); /* minus 1 because FileModelNode.values[] has a default size of 1 */ + + for (i = 0; i < (guint) n_columns; i++) + { + GType type = va_arg (args, GType); + if (! _gtk_tree_data_list_check_type (type)) { - has_children = TRUE; - i++; + g_error ("%s: type %s cannot be a column type for GtkFileSystemModel\n", G_STRLOC, g_type_name (type)); + return; /* not reached */ } - - gtk_tree_path_up (path); - - nodes = next; - } - if (parent && !has_children) - { - /* Fixme - need to insert dummy node here */ + model->column_types[i] = type; } + + model->sort_list = _gtk_tree_data_list_header_new (n_columns, model->column_types); + + model->files = g_array_sized_new (FALSE, FALSE, model->node_size, FILES_PER_QUERY); + /* add editable node at start */ + g_array_set_size (model->files, 1); + memset (get_node (model, 0), 0, model->node_size); } static void -model_refilter_all (GtkFileSystemModel *model) +gtk_file_system_model_set_directory (GtkFileSystemModel *model, + GFile * dir, + const gchar * attributes) { - GtkTreePath *path; + g_assert (G_IS_FILE (dir)); - path = gtk_tree_path_new (); - model_refilter_recurse (model, NULL, path); - gtk_tree_path_free (path); + model->dir = g_object_ref (dir); + model->attributes = g_strdup (attributes); + + g_object_ref (model); + g_file_enumerate_children_async (model->dir, + attributes, + G_FILE_QUERY_INFO_NONE, + IO_PRIORITY, + model->cancellable, + gtk_file_system_model_got_enumerator, + model); + +} + +static GtkFileSystemModel * +_gtk_file_system_model_new_valist (GtkFileSystemModelGetValue get_func, + gpointer get_data, + guint n_columns, + va_list args) +{ + GtkFileSystemModel *model; + + model = g_object_new (GTK_TYPE_FILE_SYSTEM_MODEL, NULL); + model->get_func = get_func; + model->get_data = get_data; + + gtk_file_system_model_set_n_columns (model, n_columns, args); + + return model; +} + +/** + * _gtk_file_system_model_new: + * @get_func: function to call for getting a value + * @get_data: user data argument passed to @get_func + * @n_columns: number of columns + * @...: @n_columns #GType types for the columns + * + * Creates a new #GtkFileSystemModel object. You need to add files + * to the list using _gtk_file_system_model_add_and_query_file() + * or _gtk_file_system_model_update_file(). + * + * Return value: the newly created #GtkFileSystemModel + **/ +GtkFileSystemModel * +_gtk_file_system_model_new (GtkFileSystemModelGetValue get_func, + gpointer get_data, + guint n_columns, + ...) +{ + GtkFileSystemModel *model; + va_list args; + + g_return_val_if_fail (get_func != NULL, NULL); + g_return_val_if_fail (n_columns > 0, NULL); + + va_start (args, n_columns); + model = _gtk_file_system_model_new_valist (get_func, get_data, n_columns, args); + va_end (args); + + return model; +} + +/** + * _gtk_file_system_model_new_for_directory: + * @directory: the directory to show. + * @attributes: attributes to immediately load or %NULL for all + * @get_func: function that the model should call to query data about a file + * @get_data: user data to pass to the @get_func + * @n_columns: number of columns + * @...: @n_columns #GType types for the columns + * + * Creates a new #GtkFileSystemModel object. The #GtkFileSystemModel + * object wraps the given @directory as a #GtkTreeModel. + * The model will query the given directory with the given @attributes + * and add all files inside the directory automatically. If supported, + * it will also monitor the drectory and update the model's + * contents to reflect changes, if the @directory supports monitoring. + * + * Return value: the newly created #GtkFileSystemModel + **/ +GtkFileSystemModel * +_gtk_file_system_model_new_for_directory (GFile * dir, + const gchar * attributes, + GtkFileSystemModelGetValue get_func, + gpointer get_data, + guint n_columns, + ...) +{ + GtkFileSystemModel *model; + va_list args; + + g_return_val_if_fail (G_IS_FILE (dir), NULL); + g_return_val_if_fail (get_func != NULL, NULL); + g_return_val_if_fail (n_columns > 0, NULL); + + va_start (args, n_columns); + model = _gtk_file_system_model_new_valist (get_func, get_data, n_columns, args); + va_end (args); + + gtk_file_system_model_set_directory (model, dir, attributes); + + return model; +} + +static void +gtk_file_system_model_refilter_all (GtkFileSystemModel *model) +{ + guint i; + + if (model->frozen) + { + model->filter_on_thaw = TRUE; + return; + } + + _gtk_file_system_model_freeze_updates (model); + + /* start at index 1, don't change the editable */ + for (i = 1; i < model->files->len; i++) + { + node_set_visible (model, i, node_should_be_visible (model, i)); + } + + model->filter_on_thaw = FALSE; + _gtk_file_system_model_thaw_updates (model); } /** @@ -791,12 +1407,14 @@ void _gtk_file_system_model_set_show_hidden (GtkFileSystemModel *model, gboolean show_hidden) { + g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); + show_hidden = show_hidden != FALSE; if (show_hidden != model->show_hidden) { model->show_hidden = show_hidden; - model_refilter_all (model); + gtk_file_system_model_refilter_all (model); } } @@ -812,12 +1430,14 @@ void _gtk_file_system_model_set_show_folders (GtkFileSystemModel *model, gboolean show_folders) { + g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); + show_folders = show_folders != FALSE; if (show_folders != model->show_folders) { model->show_folders = show_folders; - model_refilter_all (model); + gtk_file_system_model_refilter_all (model); } } @@ -834,29 +1454,74 @@ void _gtk_file_system_model_set_show_files (GtkFileSystemModel *model, gboolean show_files) { + g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); + show_files = show_files != FALSE; if (show_files != model->show_files) { model->show_files = show_files; - model_refilter_all (model); + gtk_file_system_model_refilter_all (model); } } /** + * _gtk_file_system_model_get_cancellable: + * @model: the model + * + * Gets the cancellable used by the @model. This is the cancellable used + * internally by the @model that will be cancelled when @model is + * disposed. So you can use it for operations that should be cancelled + * when the model goes away. + * + * Returns: The cancellable used by @model + **/ +GCancellable * +_gtk_file_system_model_get_cancellable (GtkFileSystemModel *model) +{ + g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), NULL); + + return model->cancellable; +} + +/** + * _gtk_file_system_model_iter_is_visible: + * @model: the model + * @iter: a valid iterator + * + * Checks if the iterator is visible. A visible iterator references + * a row that is currently exposed using the #GtkTreeModel API. If + * the iterator is invisible, it references a file that is not shown + * for some reason, such as being filtered by the current filter or + * being a hidden file. + * + * Returns: %TRUE if the iterator is visible + **/ +gboolean +_gtk_file_system_model_iter_is_visible (GtkFileSystemModel *model, + GtkTreeIter *iter) +{ + FileModelNode *node; + + g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), FALSE); + g_return_val_if_fail (iter != NULL, FALSE); + + node = get_node (model, ITER_INDEX (iter)); + return node->visible; +} + +/** * _gtk_file_system_model_get_info: * @model: a #GtkFileSystemModel * @iter: a #GtkTreeIter pointing to a row of @model * * Gets the #GFileInfo structure for a particular row - * of @model. The information included in this structure - * is determined by the @types parameter to - * _gtk_file_system_model_new(). + * of @model. * * Return value: a #GFileInfo structure. This structure * is owned by @model and must not be modified or freed. - * If you want to save the information for later use, - * you must make a copy, since the structure may be + * If you want to keep the information for later use, + * you must take a reference, since the structure may be * freed on later changes to the file system. If you have * called _gtk_file_system_model_add_editable() and the @iter * corresponds to the row that this function returned, the @@ -868,11 +1533,12 @@ _gtk_file_system_model_get_info (GtkFileSystemModel *model, { FileModelNode *node; - node = iter->user_data; - if (model->has_editable && node == model->roots) - return NULL; - else - return file_model_node_get_info (model, node); + g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), NULL); + g_return_val_if_fail (iter != NULL, NULL); + + node = get_node (model, ITER_INDEX (iter)); + g_assert (node->info == NULL || G_IS_FILE_INFO (node->info)); + return node->info; } /** @@ -880,7 +1546,7 @@ _gtk_file_system_model_get_info (GtkFileSystemModel *model, * @model: a #GtkFileSystemModel * @iter: a #GtkTreeIter pointing to a row of @model * - * Gets the path for a particular row in @model. + * Gets the file for a particular row in @model. * * Return value: the file. This object is owned by @model and * or freed. If you want to save the path for later use, @@ -891,311 +1557,273 @@ GFile * _gtk_file_system_model_get_file (GtkFileSystemModel *model, GtkTreeIter *iter) { - FileModelNode *node = iter->user_data; + FileModelNode *node; - if (model->has_editable && node == model->roots) - return NULL; + g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), NULL); - if (node->is_dummy) - return node->parent->file; - else - return node->file; + node = get_node (model, ITER_INDEX (iter)); + return node->file; } -static void -unref_node_and_parents (GtkFileSystemModel *model, - FileModelNode *node) +/** + * _gtk_file_system_model_get_value: + * @model: a #GtkFileSystemModel + * @iter: a #GtkTreeIter pointing to a row of @model + * @column: the column to get the value for + * + * Gets the value associated with the given row @iter and @column. + * If no value is available yet and the default value should be used, + * %NULL is returned. + * This is a performance optimization for the calls + * gtk_tree_model_get() or gtk_tree_model_get_value(), which copy + * the value and spend a considerable amount of time in iterator + * lookups. Both of which are slow. + * + * Returns: a pointer to the actual value as stored in @model or %NULL + * if no value available yet. + **/ +const GValue * +_gtk_file_system_model_get_value (GtkFileSystemModel *model, + GtkTreeIter * iter, + int column) { - file_model_node_unref (model, node); - if (node->parent) - file_model_node_unref (model, node->parent); + FileModelNode *node; + + g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), NULL); + g_return_val_if_fail (column >= 0 && (guint) column < model->n_columns, NULL); + + node = get_node (model, ITER_INDEX (iter)); + + if (!G_VALUE_TYPE (&node->values[column])) + { + g_value_init (&node->values[column], model->column_types[column]); + if (!model->get_func (model, + node->file, + node->info, + column, + &node->values[column], + model->get_data)) + { + g_value_unset (&node->values[column]); + return NULL; + } + } + + return &node->values[column]; } -static FileModelNode * -find_child_node (GtkFileSystemModel *model, - FileModelNode *parent_node, - GFile *file) +static guint +node_get_for_file (GtkFileSystemModel *model, + GFile * file) { - FileModelNode *children; - - if (parent_node) - children = file_model_node_get_children (model, parent_node); - else - children = model->roots; + guint i; + + i = GPOINTER_TO_UINT (g_hash_table_lookup (model->file_lookup, file)); + if (i != 0) + return i; - while (children) + /* Node 0 is the editable row and has no associated file or entry in the table, so we start counting from 1. + * + * The invariant here is that the files in model->files[n] for n < g_hash_table_size (model->file_lookup) + * are already added to the hash table. The table can get cleared when we re-sort; this loop merely rebuilds + * our (file -> index) mapping on demand. + * + * If we exit the loop, the next pending batch of mappings will be resolved when this function gets called again + * with another file that is not yet in the mapping. + */ + for (i = g_hash_table_size (model->file_lookup) + 1; i < model->files->len; i++) { - if (children->is_visible && - children->file && - g_file_equal (children->file, file)) - return children; + FileModelNode *node = get_node (model, i); - children = children->next; + g_hash_table_insert (model->file_lookup, node->file, GUINT_TO_POINTER (i)); + if (g_file_equal (node->file, file)) + return i; } - return NULL; + return 0; } /** - * _gtk_file_system_model_set_filter: - * @mode: a #GtkFileSystemModel - * @filter: function to be called for each file - * @user_data: data to pass to @filter - * - * Sets a callback called for each file/directory to see whether - * it should be included in model. If this function was made - * public, we'd want to include a GDestroyNotify as well. + * _gtk_file_system_model_get_iter_for_file: + * @model: the model + * @iter: the iterator to be initialized + * @file: the file to look up + * + * Initializes @iter to point to the row used for @file, if @file is part + * of the model. Note that upon successful return, @iter may point to an + * invisible row in the @model. Use + * _gtk_file_system_model_iter_is_visible() to make sure it is visible to + * the tree view. + * + * Returns: %TRUE if file is part of the model and @iter was initialized **/ -void -_gtk_file_system_model_set_filter (GtkFileSystemModel *model, - GtkFileSystemModelFilter filter, - gpointer user_data) +gboolean +_gtk_file_system_model_get_iter_for_file (GtkFileSystemModel *model, + GtkTreeIter *iter, + GFile * file) { - g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); - - model->filter_func = filter; - model->filter_data = user_data; + guint i; - model_refilter_all (model); -} + g_return_val_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model), FALSE); + g_return_val_if_fail (iter != NULL, FALSE); + g_return_val_if_fail (G_IS_FILE (file), FALSE); + i = node_get_for_file (model, file); -struct RefPathData -{ - GtkFileSystemModel *model; - FileModelNode *node; - FileModelNode *parent_node; - GSList *files; - GSList *cleanups; - GtkFileSystemModelPathFunc func; - gpointer user_data; -}; + if (i == 0) + return FALSE; -/* FIXME: maybe we have to wait on finished-loading? */ + ITER_INIT_FROM_INDEX (model, iter, i); + return TRUE; +} + +/** + * add_file: + * @model: the model + * @file: the file to add + * @info: the information to associate with the file + * + * Adds the given @file with its associated @info to the @model. + * If the model is frozen, the file will only show up after it is thawn. + **/ static void -ref_path_cb (GCancellable *cancellable, - GtkFolder *folder, - const GError *error, - gpointer data) +add_file (GtkFileSystemModel *model, + GFile *file, + GFileInfo *info) { - struct RefPathData *info = data; - gboolean cancelled = g_cancellable_is_cancelled (cancellable); - - if (!g_slist_find (info->model->pending_cancellables, cancellable)) - goto out; - - info->model->pending_cancellables = g_slist_remove (info->model->pending_cancellables, cancellable); - - /* Note that !folder means that the child node was already - * found, without using get_folder. - */ - if (cancelled || error) - goto out; - - if (folder) - info->cleanups = g_slist_prepend (info->cleanups, folder); - else if ((info->files != NULL && info->files->next == NULL) /* g_slist_length == 1 */ - && g_file_equal (info->node->file, info->files->data)) - { - /* Done, now call the function */ - if (info->node) - { - GtkTreeIter iter; - GtkTreePath *path; - - iter.user_data = info->node; - path = gtk_tree_model_get_path (GTK_TREE_MODEL (info->model), &iter); - - (* info->func) (info->model, path, &iter, info->user_data); - - gtk_tree_path_free (path); - } - - goto out; - } - - info->node = find_child_node (info->model, info->parent_node, info->files->data); - if (info->node) - file_model_node_ref (info->node); - else - { - goto out; - } + FileModelNode *node; + + g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); + g_return_if_fail (G_IS_FILE (file)); + g_return_if_fail (G_IS_FILE_INFO (info)); - g_object_unref (info->files->data); - info->files = g_slist_remove (info->files, info->files->data); + node = g_slice_alloc0 (model->node_size); + node->file = g_object_ref (file); + if (info) + node->info = g_object_ref (info); + node->frozen_add = model->frozen ? TRUE : FALSE; - if (info->files == NULL) - { - /* Done, now call the function */ - if (info->node) - { - GtkTreeIter iter; - GtkTreePath *path; + g_array_append_vals (model->files, node, 1); + g_slice_free1 (model->node_size, node); - iter.user_data = info->node; - path = gtk_tree_model_get_path (GTK_TREE_MODEL (info->model), &iter); + if (!model->frozen) + node_set_visible (model, model->files->len -1, + node_should_be_visible (model, model->files->len - 1)); + gtk_file_system_model_sort_node (model, model->files->len -1); +} - (* info->func) (info->model, path, &iter, info->user_data); +/** + * remove_file: + * @model: the model + * @file: file to remove from the model. The file must have been + * added to the model previously + * + * Removes the given file from the model. If the file is not part of + * @model, this function does nothing. + **/ +static void +remove_file (GtkFileSystemModel *model, + GFile *file) +{ + FileModelNode *node; + guint id; - gtk_tree_path_free (path); - } + g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); + g_return_if_fail (G_IS_FILE (file)); - goto out; - } - else - { - info->parent_node = info->node; + id = node_get_for_file (model, file); + if (id == 0) + return; - if (info->parent_node->loaded) - { - info->node = find_child_node (info->model, info->parent_node, info->files->data); - ref_path_cb (NULL, NULL, NULL, info); - } - else - { - GCancellable *cancellable; - - cancellable = _gtk_file_system_get_folder (info->model->file_system, - info->files->data, - info->model->attributes, - ref_path_cb, data); - info->model->pending_cancellables = - g_slist_append (info->model->pending_cancellables, cancellable); - } + node = get_node (model, id); + node_set_visible (model, id, FALSE); - return; - } + g_hash_table_remove (model->file_lookup, file); + g_object_unref (node->file); -out: - if (info->node) - unref_node_and_parents (info->model, info->node); - g_slist_foreach (info->files, (GFunc)g_object_unref, NULL); - g_slist_free (info->files); - g_slist_foreach (info->cleanups, (GFunc)g_object_unref, NULL); - g_slist_free (info->cleanups); - g_object_unref (info->model); - g_free (info); + if (node->info) + g_object_unref (node->info); - g_object_unref (cancellable); + g_array_remove_index (model->files, id); + /* We don't need to resort, as removing a row doesn't change the sorting order */ } /** - * _gtk_file_system_model_path_do: - * @model: a #GtkFileSystemModel - * @path: a path pointing to a file in the filesystem - * for @model. - * @func: Function to call with the path and iter corresponding - * to @path. - * @user_data: data to pass to @func - * - * Locates @path within @model, referencing - * (gtk_tree_model_ref_node()) all parent nodes, - * calls @func passing in the path and iter for @path, - * then unrefs all the parent nodes. + * _gtk_file_system_model_update_file: + * @model: the model + * @file: the file + * @info: the new file info + * @requires_resort: FIXME: get rid of this argument * - * The reason for doing this operation as a callback - * is so that if the operation performed with the - * path and iter results in referencing the node - * and/or parent nodes, we don't load all the information - * about the nodes. - * - * This function is particularly useful for expanding - * a #GtkTreeView to a particular point in the file system. - */ + * Tells the file system model that the file changed and that the + * new @info should be used for it now. If the file is not part of + * @model, it will get added automatically. + **/ void -_gtk_file_system_model_path_do (GtkFileSystemModel *model, - GFile *file, - GtkFileSystemModelPathFunc func, - gpointer user_data) +_gtk_file_system_model_update_file (GtkFileSystemModel *model, + GFile *file, + GFileInfo *info, + gboolean requires_resort) { - GFile *parent_file; - GSList *files = NULL; FileModelNode *node; - struct RefPathData *info; - - if (g_file_equal (file, model->root_file)) - return; + guint i, id; + GFileInfo *old_info; - parent_file = g_file_get_parent (file); + g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); + g_return_if_fail (G_IS_FILE (file)); + g_return_if_fail (G_IS_FILE_INFO (info)); - if (!parent_file) - return; + id = node_get_for_file (model, file); + if (id == 0) + add_file (model, file, info); - files = g_slist_prepend (files, g_object_ref (file)); - while (!g_file_equal (parent_file, model->root_file)) - { - files = g_slist_prepend (files, parent_file); - parent_file = g_file_get_parent (parent_file); - if (!parent_file) - { - g_slist_foreach (files, (GFunc) g_object_unref, NULL); - g_slist_free (files); - return; - } - } - g_object_unref (parent_file); + node = get_node (model, id); - if (files == NULL) - return; + old_info = node->info; + node->info = g_object_ref (info); + if (old_info) + g_object_unref (old_info); - /* Now we have all paths, except the root path */ - node = find_child_node (model, NULL, files->data); - if (!node) + for (i = 0; i < model->n_columns; i++) { - g_slist_foreach (files, (GFunc) g_object_unref, NULL); - g_slist_free (files); - return; + if (G_VALUE_TYPE (&node->values[i])) + g_value_unset (&node->values[i]); } - file_model_node_ref (node); + if (node->visible) + emit_row_changed_for_node (model, id); - g_object_unref (files->data); - files = g_slist_remove (files, files->data); + if (requires_resort) + gtk_file_system_model_sort_node (model, id); +} - if (files == NULL) - { - /* Done, now call the function */ - if (node) - { - GtkTreeIter iter; - GtkTreePath *path; +/** + * _gtk_file_system_model_set_filter: + * @mode: a #GtkFileSystemModel + * @filter: %NULL or filter to use + * + * Sets a filter to be used for deciding if a row should be visible or not. + * Directories are always visible. + **/ +void +_gtk_file_system_model_set_filter (GtkFileSystemModel *model, + GtkFileFilter * filter) +{ + GtkFileFilter *old_filter; - iter.user_data = node; - path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter); + g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); + g_return_if_fail (filter == NULL || GTK_IS_FILE_FILTER (filter)); + + if (filter) + g_object_ref (filter); - (* func) (model, path, &iter, user_data); + old_filter = model->filter; + model->filter = filter; - gtk_tree_path_free (path); - unref_node_and_parents (model, node); - } - } - else - { - info = g_new0 (struct RefPathData, 1); - info->files = files; - info->model = g_object_ref (model); - info->func = func; - info->user_data = user_data; - info->node = node; - - if (info->node->loaded) - { - info->parent_node = info->node; - info->node = find_child_node (model, info->parent_node, info->files->data); - ref_path_cb (NULL, NULL, NULL, info); - } - else - { - GCancellable *cancellable; + if (old_filter) + g_object_unref (old_filter); - cancellable = _gtk_file_system_get_folder (model->file_system, - files->data, - model->attributes, - ref_path_cb, info); - model->pending_cancellables = g_slist_append (model->pending_cancellables, cancellable); - } - } + gtk_file_system_model_refilter_all (model); } /** @@ -1211,26 +1839,11 @@ _gtk_file_system_model_path_do (GtkFileSystemModel *model, void _gtk_file_system_model_add_editable (GtkFileSystemModel *model, GtkTreeIter *iter) { - FileModelNode *node; - GtkTreePath *path; - - g_return_if_fail (!model->has_editable); - - model->has_editable = TRUE; - - node = file_model_node_new (model, NULL); - node->is_visible = TRUE; - - node->next = model->roots; - model->roots = node; - - path = gtk_tree_path_new (); - gtk_tree_path_append_index (path, 0); - iter->user_data = node; - - gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, iter); + g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); + g_return_if_fail (!get_node (model, 0)->visible); - gtk_tree_path_free (path); + node_set_visible (model, 0, TRUE); + ITER_INIT_FROM_INDEX (model, iter, 0); } /** @@ -1244,735 +1857,148 @@ _gtk_file_system_model_add_editable (GtkFileSystemModel *model, GtkTreeIter *ite void _gtk_file_system_model_remove_editable (GtkFileSystemModel *model) { - GtkTreePath *path; - FileModelNode *node; - - g_return_if_fail (model->has_editable); - - model->has_editable = FALSE; - - node = model->roots; - model->roots = model->roots->next; - file_model_node_free (node); - - path = gtk_tree_path_new (); - gtk_tree_path_append_index (path, 0); - - gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path); - - gtk_tree_path_free (path); -} - -static FileModelNode * -file_model_node_new (GtkFileSystemModel *model, - GFile *file) -{ - FileModelNode *node = g_new0 (FileModelNode, 1); - - node->model = model; - node->file = file ? g_object_ref (file) : NULL; - - return node; -} - -static void -file_model_node_free (FileModelNode *node) -{ - file_model_node_clear (node->model, node); - - if (node->file) - g_object_unref (node->file); - - if (node->info) - g_object_unref (node->info); - - g_free (node); -} - -static GFileInfo * -file_model_node_get_info (GtkFileSystemModel *model, - FileModelNode *node) -{ - if (!node->info) - { - if (node->is_dummy) - { - node->info = g_file_info_new (); - g_file_info_set_display_name (node->info, _("(Empty)")); - } - else if (node->parent || model->root_folder) - { - node->info = _gtk_folder_get_info ((node->parent != NULL) ? node->parent->folder : model->root_folder, - node->file); - } - else - g_assert_not_reached (); - } - - return node->info; -} - -static gboolean -file_model_node_is_visible (GtkFileSystemModel *model, - FileModelNode *node) -{ - if (model->show_folders != model->show_files || - !model->show_hidden || - model->filter_func) - { - GFileInfo *info = file_model_node_get_info (model, node); - gboolean is_folder; - - if (!info) - { - /* File probably disappeared underneath us or resides in a - directory where we have only partial access rights. */ - return FALSE; - } - - is_folder = _gtk_file_info_consider_as_directory (info); - - if (model->show_folders != model->show_files && - model->show_folders != is_folder) - return FALSE; - - if (!model->show_hidden && - (g_file_info_get_is_hidden (info) || g_file_info_get_is_backup (info))) - return FALSE; - - if (model->filter_func && - !model->filter_func (model, node->file, info, model->filter_data)) - return FALSE; - } - - return TRUE; -} - -static void -file_model_node_clear (GtkFileSystemModel *model, - FileModelNode *node) -{ - FileModelNode *children; - - file_model_node_idle_clear_cancel (node); - - children = node->children; - node->children = NULL; - node->loaded = FALSE; - - while (children) - { - FileModelNode *next = children->next; - - file_model_node_free (children); - - children = next; - } - - if (node->folder) - { - /* Unreffing node->folder may cause roots_changed, - * so we need to be careful about ordering. - */ - GtkFolder *folder = node->folder; - node->folder = NULL; - - g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (deleted_callback), node); - g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (files_added_callback), node); - g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (files_changed_callback), node); - g_signal_handlers_disconnect_by_func (folder, G_CALLBACK (files_removed_callback), node); - - g_object_unref (folder); - } -} - -static void -file_model_node_ref (FileModelNode *node) -{ - node->ref_count++; - if (node->ref_count == 1 && node->parent) - node->parent->n_referenced_children++; -} - -static gboolean -idle_clear_callback (GtkFileSystemModel *model) -{ - while (model->idle_clears) - { - FileModelNode *node = model->idle_clears->data; - model->idle_clears = g_slist_delete_link (model->idle_clears, model->idle_clears); - - node->idle_clear = FALSE; - file_model_node_clear (node->model, node); - } - - return FALSE; -} - -static void -file_model_node_idle_clear (FileModelNode *node) -{ - if (!node->idle_clear) - { - GtkFileSystemModel *model = node->model; - - node->idle_clear = TRUE; - if (!model->idle_clears) - { - model->idle_clear_source = g_idle_source_new (); - g_source_set_priority (model->idle_clear_source, G_PRIORITY_HIGH); - g_source_set_closure (model->idle_clear_source, - g_cclosure_new_object (G_CALLBACK (idle_clear_callback), - G_OBJECT (model))); - g_source_attach (model->idle_clear_source, NULL); - } - - model->idle_clears = g_slist_prepend (model->idle_clears, node); - node->idle_clear = TRUE; - } -} - -static void -file_model_node_idle_clear_cancel (FileModelNode *node) -{ - if (node->idle_clear) - { - GtkFileSystemModel *model = node->model; + g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); + g_return_if_fail (get_node (model, 0)->visible); - model->idle_clears = g_slist_remove (model->idle_clears, node); - if (!model->idle_clears) - { - g_source_destroy (model->idle_clear_source); - model->idle_clear_source = NULL; - } - - node->idle_clear = FALSE; - } + node_set_visible (model, 0, FALSE); } -static void -file_model_node_unref (GtkFileSystemModel *model, - FileModelNode *node) +/** + * _gtk_file_system_model_freeze_updates: + * @model: a #GtkFileSystemModel + * + * Freezes most updates on the model, so that performing multiple + * operations on the files in the model do not cause any events. + * Use _gtk_file_system_model_thaw_updates() to resume proper + * operations. It is fine to call this function multiple times as + * long as freeze and thaw calls are balanced. + **/ +void +_gtk_file_system_model_freeze_updates (GtkFileSystemModel *model) { - node->ref_count--; - if (node->ref_count == 0) - { - file_model_node_clear (model, node); - if (node->parent) - file_model_node_child_unref (node->parent); - } -} + g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); -static void -file_model_node_child_unref (FileModelNode *parent) -{ - parent->n_referenced_children--; - if (parent->n_referenced_children == 0) - file_model_node_idle_clear (parent); + model->frozen++; } -struct GetChildrenData -{ - GtkFileSystemModel *model; - FileModelNode *node; -}; - -static void -get_children_get_folder_cb (GCancellable *cancellable, - GtkFolder *folder, - const GError *error, - gpointer callback_data) +/** + * _gtk_file_system_model_thaw_updates: + * @model: a #GtkFileSystemModel + * + * Undoes the effect of a previous call to + * _gtk_file_system_model_freeze_updates() + **/ +void +_gtk_file_system_model_thaw_updates (GtkFileSystemModel *model) { - gboolean cancelled = g_cancellable_is_cancelled (cancellable); - struct GetChildrenData *data = callback_data; - FileModelNode *child_node; - GSList *tmp_list; - - tmp_list = g_slist_find (data->model->pending_cancellables, cancellable); - - if (!tmp_list) - goto out; - - data->model->pending_cancellables = g_slist_delete_link (data->model->pending_cancellables, tmp_list); - - if (cancelled || !folder) - { - /* error, no folder, remove dummy child */ - if (data->node->parent && data->node->parent->has_dummy) - { - data->node->parent->children = NULL; - data->node->parent->has_dummy = FALSE; - } - - file_model_node_free (data->node); - - goto out; - } - - data->node->folder = folder; - data->node->load_pending = FALSE; - - g_signal_connect (data->node->folder, "deleted", - G_CALLBACK (deleted_callback), data->node); - g_signal_connect (data->node->folder, "files-added", - G_CALLBACK (files_added_callback), data->node); - g_signal_connect (data->node->folder, "files-changed", - G_CALLBACK (files_changed_callback), data->node); - g_signal_connect (data->node->folder, "files-removed", - G_CALLBACK (files_removed_callback), data->node); - - data->node->loaded = TRUE; + gboolean stuff_added; - /* We claimed this folder had children, so we - * have to add a dummy child, possibly to remove later. - */ - child_node = file_model_node_new (data->model, NULL); - child_node->is_visible = TRUE; - child_node->parent = data->node; - child_node->is_dummy = TRUE; - - data->node->children = child_node; - data->node->has_dummy = TRUE; - - g_object_set_data (G_OBJECT (data->node->folder), I_("model-node"), data->node); - -out: - g_object_unref (data->model); - g_free (data); + g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); + g_return_if_fail (model->frozen > 0); - g_object_unref (cancellable); -} + model->frozen--; + if (model->frozen > 0) + return; -static FileModelNode * -file_model_node_get_children (GtkFileSystemModel *model, - FileModelNode *node) -{ - if (node->ref_count == 0) - return NULL; + stuff_added = get_node (model, model->files->len - 1)->frozen_add; - if (!node->loaded && !node->load_pending) + if (model->filter_on_thaw) + gtk_file_system_model_refilter_all (model); + if (model->sort_on_thaw) + gtk_file_system_model_sort (model); + if (stuff_added) { - GFileInfo *info = file_model_node_get_info (model, node); - gboolean has_children = FALSE; - gboolean is_folder = _gtk_file_info_consider_as_directory (info); - - file_model_node_idle_clear_cancel (node); + guint i; - if (node->depth < model->max_depth && is_folder) + for (i = 0; i < model->files->len; i++) { - struct GetChildrenData *data; - GCancellable *cancellable; - - data = g_new (struct GetChildrenData, 1); - data->model = g_object_ref (model); - data->node = node; - - cancellable = - _gtk_file_system_get_folder (model->file_system, - node->file, - model->attributes, - get_children_get_folder_cb, - data); - - model->pending_cancellables = g_slist_append (model->pending_cancellables, cancellable); - node->load_pending = TRUE; - - if (!has_children) - { - /* The hard case ... we claimed this folder had children, but actually - * it didn't. We have to add a dummy child, possibly to remove later. - */ - FileModelNode *child_node = file_model_node_new (model, NULL); - child_node->is_visible = TRUE; - child_node->parent = node; - child_node->is_dummy = TRUE; - - node->children = child_node; - node->has_dummy = TRUE; - } - } - } - - return node->children; -} - -static gint -file_compare_func (GFile *file1, - GFile *file2) -{ - gchar *uri1, *uri2; - gint result; - - uri1 = g_file_get_uri (file1); - uri2 = g_file_get_uri (file2); - - result = g_strcmp0 (uri1, uri2); - - g_free (uri1); - g_free (uri2); - - return result; -} - -static GSList * -sort_file_list (GSList *list) -{ - GSList *copy; - - copy = g_slist_copy (list); - return g_slist_sort (copy, (GCompareFunc) file_compare_func); -} - -static void -do_files_added (GtkFileSystemModel *model, - FileModelNode *parent_node, - GSList *files) -{ - GtkTreeModel *tree_model = GTK_TREE_MODEL (model); - FileModelNode *children; - FileModelNode *prev = NULL; - GtkTreeIter iter; - GtkTreePath *path; - GSList *sorted_files, *tmp_list; - - sorted_files = sort_file_list (files); - - if (parent_node) - { - iter.user_data = parent_node; - path = gtk_tree_model_get_path (tree_model, &iter); - children = parent_node->children; - } - else - { - path = gtk_tree_path_new (); - children = model->roots; - } - - gtk_tree_path_down (path); - - if (parent_node && parent_node->has_dummy) - { - prev = children; - children = children->next; - gtk_tree_path_next (path); - } - - for (tmp_list = sorted_files; tmp_list; tmp_list = tmp_list->next) - { - GFile *file = tmp_list->data; - - while (children && - (!children->file || !g_file_equal (children->file, file))) - { - prev = children; - if (children->is_visible) - gtk_tree_path_next (path); - - children = children->next; - } - - if (children && - children->file && g_file_equal (children->file, file)) - { - /* Shouldn't happen */ - } - else - { - FileModelNode *new; - - new = file_model_node_new (model, file); - - if (children) - new->next = children; - if (prev) - prev->next = new; - else if (parent_node) - parent_node->children = new; - else - model->roots = new; - - prev = new; - - if (parent_node) - { - new->parent = parent_node; - new->depth = parent_node->depth + 1; - } - - new->is_visible = file_model_node_is_visible (model, new); - - if (new->is_visible) - { - iter.user_data = new; - gtk_tree_path_free (path); - path = gtk_tree_model_get_path (tree_model, &iter); - gtk_tree_model_row_inserted (tree_model, path, &iter); - - if (gtk_file_system_model_iter_has_child (tree_model, &iter)) - gtk_tree_model_row_has_child_toggled (tree_model, path, &iter); - - if (parent_node && parent_node->has_dummy) - { - FileModelNode *dummy = parent_node->children; - GtkTreePath *dummy_path; - - parent_node->children = parent_node->children->next; - parent_node->has_dummy = FALSE; - - dummy_path = gtk_tree_path_copy (path); - gtk_tree_path_up (dummy_path); - gtk_tree_path_down (dummy_path); - - gtk_tree_model_row_deleted (tree_model, dummy_path); - gtk_tree_path_free (dummy_path); - - if (dummy->ref_count) - file_model_node_child_unref (parent_node); - file_model_node_free (dummy); - } - - gtk_tree_path_next (path); - } - } - } - - gtk_tree_path_free (path); - g_slist_free (sorted_files); -} - -static void -do_files_changed (GtkFileSystemModel *model, - FileModelNode *parent_node, - GSList *files) -{ - GtkTreeModel *tree_model = GTK_TREE_MODEL (model); - FileModelNode *children; - GtkTreeIter iter; - GtkTreePath *path; - GSList *sorted_files; - GSList *tmp_list; - - sorted_files = sort_file_list (files); - - if (parent_node) - { - iter.user_data = parent_node; - path = gtk_tree_model_get_path (tree_model, &iter); - children = parent_node->children; - } - else - { - path = gtk_tree_path_new (); - children = model->roots; - } - - gtk_tree_path_down (path); - - if (parent_node && parent_node->has_dummy) - { - children = children->next; - gtk_tree_path_next (path); - } + FileModelNode *node = get_node (model, i); - for (tmp_list = sorted_files; tmp_list; tmp_list = tmp_list->next) - { - GFile *file = tmp_list->data; - - while (children && - (!children->file || !g_file_equal (children->file, file))) - { - if (children->is_visible) - gtk_tree_path_next (path); - - children = children->next; - } - - if (children && - children->file && g_file_equal (children->file, file)) - { - gtk_tree_model_row_changed (tree_model, path, &iter); - } - else - { - /* Shouldn't happen */ - } + if (!node->frozen_add) + continue; + node->frozen_add = FALSE; + node_set_visible (model, i, node_should_be_visible (model, i)); + } } - - gtk_tree_path_free (path); - g_slist_free (sorted_files); } -static void -do_files_removed (GtkFileSystemModel *model, - FileModelNode *parent_node, - GSList *files) +/** + * _gtk_file_system_model_clear_cache: + * @model: a #GtkFileSystemModel + * @column: the column to clear or -1 for all columns + * + * Clears the cached values in the model for the given @column. Use + * this function whenever your get_value function would return different + * values for a column. + * The file chooser uses this for example when the icon theme changes to + * invalidate the cached pixbufs. + **/ +void +_gtk_file_system_model_clear_cache (GtkFileSystemModel *model, + int column) { - GtkTreeModel *tree_model = GTK_TREE_MODEL (model); - FileModelNode *children; - FileModelNode *prev = NULL; - GtkTreeIter iter; - GtkTreePath *path; - GSList *sorted_files; - GSList *tmp_list; - FileModelNode *tmp_child; - gint n_visible; + guint i; + int start, end; + gboolean changed; - sorted_files = sort_file_list (files); + g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); + g_return_if_fail (column >= -1 && (guint) column < model->n_columns); - if (parent_node) + if (column > -1) { - iter.user_data = parent_node; - path = gtk_tree_model_get_path (tree_model, &iter); - children = parent_node->children; + start = column; + end = column + 1; } else { - path = gtk_tree_path_new (); - children = model->roots; + start = 0; + end = model->n_columns; } - /* Count the number of currently visible children, so that - * can catch when we need to insert a dummy node. - */ - n_visible = 0; - for (tmp_child = children; tmp_child; tmp_child = tmp_child->next) + for (i = 0; i < model->files->len; i++) { - if (tmp_child->is_visible) - n_visible++; - } - - gtk_tree_path_down (path); - - if (parent_node && parent_node->has_dummy) - { - prev = children; - children = children->next; - gtk_tree_path_next (path); - } - - for (tmp_list = sorted_files; tmp_list; tmp_list = tmp_list->next) - { - GFile *file = tmp_list->data; - - while (children && - (!children->file || !g_file_equal (children->file, file))) - { - prev = children; - if (children->is_visible) - gtk_tree_path_next (path); - - children = children->next; - } - - if (children && - children->file && g_file_equal (children->file, file)) - { - FileModelNode *next = children->next; - - if (children->is_visible) - n_visible--; - - if (parent_node && n_visible == 0) - { - FileModelNode *dummy = file_model_node_new (model, NULL); - dummy->is_visible = TRUE; - dummy->parent = parent_node; - dummy->is_dummy = TRUE; - - parent_node->children = dummy; - parent_node->has_dummy = TRUE; - - iter.user_data = dummy; - gtk_tree_model_row_inserted (tree_model, path, &iter); - gtk_tree_path_next (path); + FileModelNode *node = get_node (model, i); + changed = FALSE; + for (column = start; column < end; column++) + { + if (!G_VALUE_TYPE (&node->values[column])) + continue; + + g_value_unset (&node->values[column]); + changed = TRUE; + } - prev = dummy; - } - - if (prev) - prev->next = next; - else if (parent_node) - parent_node->children = next; - else - model->roots = next; - - if (parent_node && children->ref_count) - file_model_node_child_unref (parent_node); - - if (children->is_visible) - gtk_tree_model_row_deleted (tree_model, path); - - file_model_node_free (children); - - children = next; - } - else - { - /* Shouldn't happen */ - } + if (changed && node->visible) + emit_row_changed_for_node (model, i); } - gtk_tree_path_free (path); - g_slist_free (sorted_files); -} - -static void -deleted_callback (GFile *folder, - FileModelNode *node) -{ + /* FIXME: resort? */ } -static void -files_added_callback (GFile *folder, - GSList *files, - FileModelNode *node) -{ - do_files_added (node->model, node, files); -} - -static void -files_changed_callback (GFile *folder, - GSList *files, - FileModelNode *node) -{ - do_files_changed (node->model, node, files); -} - -static void -files_removed_callback (GFile *folder, - GSList *files, - FileModelNode *node) -{ - do_files_removed (node->model, node, files); -} - -static void -root_deleted_callback (GFile *folder, - GtkFileSystemModel *model) -{ -} - -static void -root_files_added_callback (GFile *folder, - GSList *files, - GtkFileSystemModel *model) -{ - do_files_added (model, NULL, files); -} - -static void -root_files_changed_callback (GFile *folder, - GSList *files, - GtkFileSystemModel *model) -{ - do_files_changed (model, NULL, files); -} - -static void -root_files_removed_callback (GFile *folder, - GSList *files, - GtkFileSystemModel *model) +/** + * _gtk_file_system_model_add_and_query_file: + * @model: a #GtkFileSystemModel + * @file: the file to add + * @attributes: attributes to query before adding the file + * + * This is a conenience function that calls g_file_query_info_async() on + * the given file, and when successful, adds it to the model. + * Upon failure, the @file is discarded. + **/ +void +_gtk_file_system_model_add_and_query_file (GtkFileSystemModel *model, + GFile * file, + const char * attributes) { - do_files_removed (model, NULL, files); + g_return_if_fail (GTK_IS_FILE_SYSTEM_MODEL (model)); + g_return_if_fail (G_IS_FILE (file)); + g_return_if_fail (attributes != NULL); + + g_file_query_info_async (file, + attributes, + G_FILE_QUERY_INFO_NONE, + IO_PRIORITY, + model->cancellable, + gtk_file_system_model_query_done, + model); } diff --git a/gtk/gtkfilesystemmodel.h b/gtk/gtkfilesystemmodel.h index 750fa33663..a6fbab936f 100644 --- a/gtk/gtkfilesystemmodel.h +++ b/gtk/gtkfilesystemmodel.h @@ -21,7 +21,8 @@ #ifndef __GTK_FILE_SYSTEM_MODEL_H__ #define __GTK_FILE_SYSTEM_MODEL_H__ -#include "gtkfilesystem.h" +#include <gio/gio.h> +#include <gtk/gtkfilefilter.h> #include <gtk/gtktreemodel.h> G_BEGIN_DECLS @@ -34,46 +35,58 @@ typedef struct _GtkFileSystemModel GtkFileSystemModel; GType _gtk_file_system_model_get_type (void) G_GNUC_CONST; -typedef enum { - GTK_FILE_SYSTEM_MODEL_INFO, - GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME, - GTK_FILE_SYSTEM_MODEL_N_COLUMNS -} GtkFileSystemModelColumns; +typedef gboolean (*GtkFileSystemModelGetValue) (GtkFileSystemModel *model, + GFile *file, + GFileInfo *info, + int column, + GValue *value, + gpointer user_data); -GtkFileSystemModel *_gtk_file_system_model_new (GtkFileSystem *file_system, - GFile *root_file, - gint max_depth, - const gchar *attributes, - GError **error); +GtkFileSystemModel *_gtk_file_system_model_new (GtkFileSystemModelGetValue get_func, + gpointer get_data, + guint n_columns, + ...); +GtkFileSystemModel *_gtk_file_system_model_new_for_directory(GFile * dir, + const gchar * attributes, + GtkFileSystemModelGetValue get_func, + gpointer get_data, + guint n_columns, + ...); +GCancellable * _gtk_file_system_model_get_cancellable (GtkFileSystemModel *model); +gboolean _gtk_file_system_model_iter_is_visible (GtkFileSystemModel *model, + GtkTreeIter *iter); GFileInfo * _gtk_file_system_model_get_info (GtkFileSystemModel *model, GtkTreeIter *iter); +gboolean _gtk_file_system_model_get_iter_for_file(GtkFileSystemModel *model, + GtkTreeIter *iter, + GFile *file); GFile * _gtk_file_system_model_get_file (GtkFileSystemModel *model, GtkTreeIter *iter); +const GValue * _gtk_file_system_model_get_value (GtkFileSystemModel *model, + GtkTreeIter * iter, + int column); + +void _gtk_file_system_model_add_and_query_file (GtkFileSystemModel *model, + GFile *file, + const char *attributes); +void _gtk_file_system_model_update_file (GtkFileSystemModel *model, + GFile *file, + GFileInfo *info, + gboolean requires_resort); + void _gtk_file_system_model_set_show_hidden (GtkFileSystemModel *model, gboolean show_hidden); void _gtk_file_system_model_set_show_folders (GtkFileSystemModel *model, gboolean show_folders); void _gtk_file_system_model_set_show_files (GtkFileSystemModel *model, gboolean show_files); +void _gtk_file_system_model_freeze_updates (GtkFileSystemModel *model); +void _gtk_file_system_model_thaw_updates (GtkFileSystemModel *model); +void _gtk_file_system_model_clear_cache (GtkFileSystemModel *model, + int column); -typedef gboolean (*GtkFileSystemModelFilter) (GtkFileSystemModel *model, - GFile *file, - GFileInfo *info, - gpointer user_data); - -void _gtk_file_system_model_set_filter (GtkFileSystemModel *model, - GtkFileSystemModelFilter filter, - gpointer user_data); - -typedef void (*GtkFileSystemModelPathFunc) (GtkFileSystemModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer user_data); - -void _gtk_file_system_model_path_do (GtkFileSystemModel *model, - GFile *file, - GtkFileSystemModelPathFunc func, - gpointer user_data); +void _gtk_file_system_model_set_filter (GtkFileSystemModel *model, + GtkFileFilter *filter); void _gtk_file_system_model_add_editable (GtkFileSystemModel *model, GtkTreeIter *iter); diff --git a/gtk/gtkgamma.c b/gtk/gtkgamma.c index fd03f857ef..5485dede0f 100644 --- a/gtk/gtkgamma.c +++ b/gtk/gtkgamma.c @@ -24,6 +24,8 @@ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ +#undef GTK_DISABLE_DEPRECATED + #include "config.h" #include <string.h> #include <stdlib.h> diff --git a/gtk/gtkgamma.h b/gtk/gtkgamma.h index bde3d88df4..0173f960ac 100644 --- a/gtk/gtkgamma.h +++ b/gtk/gtkgamma.h @@ -24,19 +24,7 @@ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ -/* - * NOTE this widget is considered too specialized/little-used for - * GTK+, and will in the future be moved to some other package. If - * your application needs this widget, feel free to use it, as the - * widget does work and is useful in some applications; it's just not - * of general interest. However, we are not accepting new features for - * the widget, and it will eventually move out of the GTK+ - * distribution. - */ - -#if defined(GTK_DISABLE_SINGLE_INCLUDES) && !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only <gtk/gtk.h> can be included directly." -#endif +#ifndef GTK_DISABLE_DEPRECATED #ifndef __GTK_GAMMA_CURVE_H__ #define __GTK_GAMMA_CURVE_H__ @@ -90,3 +78,5 @@ GtkWidget* gtk_gamma_curve_new (void); G_END_DECLS #endif /* __GTK_GAMMA_CURVE_H__ */ + +#endif /* GTK_DISABLE_DEPRECATED */ diff --git a/gtk/gtkiconfactory.c b/gtk/gtkiconfactory.c index cced1108e5..145d935e8e 100644 --- a/gtk/gtkiconfactory.c +++ b/gtk/gtkiconfactory.c @@ -21,7 +21,7 @@ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS * file for a list of people on the GTK+ Team. See the ChangeLog * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ #include "config.h" @@ -54,7 +54,7 @@ typedef enum { struct _GtkIconSource { GtkIconSourceType type; - + union { gchar *icon_name; gchar *filename; @@ -126,7 +126,7 @@ static void gtk_icon_factory_class_init (GtkIconFactoryClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - + object_class->finalize = gtk_icon_factory_finalize; } @@ -150,11 +150,11 @@ gtk_icon_factory_finalize (GObject *object) GtkIconFactory *factory = GTK_ICON_FACTORY (object); all_icon_factories = g_slist_remove (all_icon_factories, factory); - + g_hash_table_foreach (factory->icons, free_icon_set, NULL); - + g_hash_table_destroy (factory->icons); - + G_OBJECT_CLASS (gtk_icon_factory_parent_class)->finalize (object); } @@ -174,9 +174,9 @@ gtk_icon_factory_finalize (GObject *object) * gtk_icon_factory_remove_default(). Applications with icons should * add a default icon factory with their icons, which will allow * themes to override the icons for the application. - * + * * Return value: a new #GtkIconFactory - **/ + */ GtkIconFactory* gtk_icon_factory_new (void) { @@ -199,8 +199,7 @@ gtk_icon_factory_new (void) * override your application's default icons. If an icon already * existed in @factory for @stock_id, it is unreferenced and replaced * with the new @icon_set. - * - **/ + */ void gtk_icon_factory_add (GtkIconFactory *factory, const gchar *stock_id, @@ -211,14 +210,14 @@ gtk_icon_factory_add (GtkIconFactory *factory, g_return_if_fail (GTK_IS_ICON_FACTORY (factory)); g_return_if_fail (stock_id != NULL); - g_return_if_fail (icon_set != NULL); + g_return_if_fail (icon_set != NULL); g_hash_table_lookup_extended (factory->icons, stock_id, &old_key, &old_value); if (old_value == icon_set) return; - + gtk_icon_set_ref (icon_set); /* GHashTable key memory management is so fantastically broken. */ @@ -235,22 +234,22 @@ gtk_icon_factory_add (GtkIconFactory *factory, * gtk_icon_factory_lookup: * @factory: a #GtkIconFactory * @stock_id: an icon name - * + * * Looks up @stock_id in the icon factory, returning an icon set * if found, otherwise %NULL. For display to the user, you should * use gtk_style_lookup_icon_set() on the #GtkStyle for the * widget that will display the icon, instead of using this * function directly, so that themes are taken into account. - * + * * Return value: icon set of @stock_id. - **/ + */ GtkIconSet * gtk_icon_factory_lookup (GtkIconFactory *factory, const gchar *stock_id) { g_return_val_if_fail (GTK_IS_ICON_FACTORY (factory), NULL); g_return_val_if_fail (stock_id != NULL, NULL); - + return g_hash_table_lookup (factory->icons, stock_id); } @@ -260,22 +259,21 @@ static GSList *default_factories = NULL; /** * gtk_icon_factory_add_default: * @factory: a #GtkIconFactory - * + * * Adds an icon factory to the list of icon factories searched by * gtk_style_lookup_icon_set(). This means that, for example, * gtk_image_new_from_stock() will be able to find icons in @factory. * There will normally be an icon factory added for each library or * application that comes with icons. The default icon factories * can be overridden by themes. - * - **/ + */ void gtk_icon_factory_add_default (GtkIconFactory *factory) { g_return_if_fail (GTK_IS_ICON_FACTORY (factory)); g_object_ref (factory); - + default_factories = g_slist_prepend (default_factories, factory); } @@ -286,8 +284,7 @@ gtk_icon_factory_add_default (GtkIconFactory *factory) * Removes an icon factory from the list of default icon * factories. Not normally used; you might use it for a library that * can be unloaded or shut down. - * - **/ + */ void gtk_icon_factory_remove_default (GtkIconFactory *factory) { @@ -318,17 +315,16 @@ _gtk_icon_factory_ensure_default_icons (void) * the #GtkStyle for the widget that will display the icon, instead of * using this function directly, so that themes are taken into * account. - * - * + * * Return value: a #GtkIconSet, or %NULL - **/ + */ GtkIconSet * gtk_icon_factory_lookup_default (const gchar *stock_id) { GSList *tmp_list; g_return_val_if_fail (stock_id != NULL, NULL); - + tmp_list = default_factories; while (tmp_list != NULL) { @@ -338,12 +334,12 @@ gtk_icon_factory_lookup_default (const gchar *stock_id) if (icon_set) return icon_set; - + tmp_list = g_slist_next (tmp_list); } _gtk_icon_factory_ensure_default_icons (); - + return gtk_icon_factory_lookup (gtk_default_icons, stock_id); } @@ -357,7 +353,7 @@ register_stock_icon (GtkIconFactory *factory, source.type = GTK_ICON_SOURCE_STATIC_ICON_NAME; source.source.icon_name = (gchar *)stock_id; gtk_icon_set_add_source (set, &source); - + gtk_icon_factory_add (factory, stock_id, set); gtk_icon_set_unref (set); } @@ -375,12 +371,12 @@ register_bidi_stock_icon (GtkIconFactory *factory, source.source.icon_name = (gchar *)stock_id_ltr; source.direction = GTK_TEXT_DIR_LTR; gtk_icon_set_add_source (set, &source); - + source.type = GTK_ICON_SOURCE_STATIC_ICON_NAME; source.source.icon_name = (gchar *)stock_id_rtl; source.direction = GTK_TEXT_DIR_RTL; gtk_icon_set_add_source (set, &source); - + gtk_icon_factory_add (factory, stock_id, set); gtk_icon_set_unref (set); } @@ -416,9 +412,9 @@ get_default_icons (GtkIconFactory *factory) register_stock_icon (factory, GTK_STOCK_GO_DOWN); register_stock_icon (factory, GTK_STOCK_EXECUTE); register_stock_icon (factory, GTK_STOCK_QUIT); - register_bidi_stock_icon (factory, - GTK_STOCK_GOTO_FIRST, - GTK_STOCK_GOTO_FIRST "-ltr", + register_bidi_stock_icon (factory, + GTK_STOCK_GOTO_FIRST, + GTK_STOCK_GOTO_FIRST "-ltr", GTK_STOCK_GOTO_FIRST "-rtl"); register_stock_icon (factory, GTK_STOCK_SELECT_FONT); register_stock_icon (factory, GTK_STOCK_FULLSCREEN); @@ -427,15 +423,15 @@ get_default_icons (GtkIconFactory *factory) register_stock_icon (factory, GTK_STOCK_HELP); register_stock_icon (factory, GTK_STOCK_HOME); register_stock_icon (factory, GTK_STOCK_INFO); - register_bidi_stock_icon (factory, + register_bidi_stock_icon (factory, GTK_STOCK_JUMP_TO, GTK_STOCK_JUMP_TO "-ltr", GTK_STOCK_JUMP_TO "-rtl"); - register_bidi_stock_icon (factory, + register_bidi_stock_icon (factory, GTK_STOCK_GOTO_LAST, GTK_STOCK_GOTO_LAST "-ltr", GTK_STOCK_GOTO_LAST "-rtl"); - register_bidi_stock_icon (factory, + register_bidi_stock_icon (factory, GTK_STOCK_GO_BACK, GTK_STOCK_GO_BACK "-ltr", GTK_STOCK_GO_BACK "-rtl"); @@ -457,17 +453,17 @@ get_default_icons (GtkIconFactory *factory) register_stock_icon (factory, GTK_STOCK_PRINT_REPORT); register_stock_icon (factory, GTK_STOCK_PRINT_WARNING); register_stock_icon (factory, GTK_STOCK_PROPERTIES); - register_bidi_stock_icon (factory, + register_bidi_stock_icon (factory, GTK_STOCK_REDO, GTK_STOCK_REDO "-ltr", GTK_STOCK_REDO "-rtl"); register_stock_icon (factory, GTK_STOCK_REMOVE); register_stock_icon (factory, GTK_STOCK_REFRESH); - register_bidi_stock_icon (factory, + register_bidi_stock_icon (factory, GTK_STOCK_REVERT_TO_SAVED, GTK_STOCK_REVERT_TO_SAVED "-ltr", GTK_STOCK_REVERT_TO_SAVED "-rtl"); - register_bidi_stock_icon (factory, + register_bidi_stock_icon (factory, GTK_STOCK_GO_FORWARD, GTK_STOCK_GO_FORWARD "-ltr", GTK_STOCK_GO_FORWARD "-rtl"); @@ -484,21 +480,21 @@ get_default_icons (GtkIconFactory *factory) register_stock_icon (factory, GTK_STOCK_ITALIC); register_stock_icon (factory, GTK_STOCK_STRIKETHROUGH); register_stock_icon (factory, GTK_STOCK_UNDERLINE); - register_bidi_stock_icon (factory, + register_bidi_stock_icon (factory, GTK_STOCK_INDENT, GTK_STOCK_INDENT "-ltr", GTK_STOCK_INDENT "-rtl"); - register_bidi_stock_icon (factory, + register_bidi_stock_icon (factory, GTK_STOCK_UNINDENT, GTK_STOCK_UNINDENT "-ltr", GTK_STOCK_UNINDENT "-rtl"); register_stock_icon (factory, GTK_STOCK_GOTO_TOP); register_stock_icon (factory, GTK_STOCK_DELETE); - register_bidi_stock_icon (factory, + register_bidi_stock_icon (factory, GTK_STOCK_UNDELETE, GTK_STOCK_UNDELETE "-ltr", GTK_STOCK_UNDELETE "-rtl"); - register_bidi_stock_icon (factory, + register_bidi_stock_icon (factory, GTK_STOCK_UNDO, GTK_STOCK_UNDO "-ltr", GTK_STOCK_UNDO "-rtl"); @@ -510,25 +506,25 @@ get_default_icons (GtkIconFactory *factory) register_stock_icon (factory, GTK_STOCK_DISCONNECT); register_stock_icon (factory, GTK_STOCK_EDIT); register_stock_icon (factory, GTK_STOCK_CAPS_LOCK_WARNING); - register_bidi_stock_icon (factory, + register_bidi_stock_icon (factory, GTK_STOCK_MEDIA_FORWARD, GTK_STOCK_MEDIA_FORWARD "-ltr", GTK_STOCK_MEDIA_FORWARD "-rtl"); - register_bidi_stock_icon (factory, + register_bidi_stock_icon (factory, GTK_STOCK_MEDIA_NEXT, GTK_STOCK_MEDIA_NEXT "-ltr", GTK_STOCK_MEDIA_NEXT "-rtl"); register_stock_icon (factory, GTK_STOCK_MEDIA_PAUSE); - register_bidi_stock_icon (factory, + register_bidi_stock_icon (factory, GTK_STOCK_MEDIA_PLAY, GTK_STOCK_MEDIA_PLAY "-ltr", GTK_STOCK_MEDIA_PLAY "-rtl"); - register_bidi_stock_icon (factory, + register_bidi_stock_icon (factory, GTK_STOCK_MEDIA_PREVIOUS, GTK_STOCK_MEDIA_PREVIOUS "-ltr", GTK_STOCK_MEDIA_PREVIOUS "-rtl"); register_stock_icon (factory, GTK_STOCK_MEDIA_RECORD); - register_bidi_stock_icon (factory, + register_bidi_stock_icon (factory, GTK_STOCK_MEDIA_REWIND, GTK_STOCK_MEDIA_REWIND "-ltr", GTK_STOCK_MEDIA_REWIND "-rtl"); @@ -554,7 +550,7 @@ struct _IconSize { gint size; gchar *name; - + gint width; gint height; }; @@ -589,7 +585,7 @@ init_icon_sizes (void) gint i; icon_aliases = g_hash_table_new (g_str_hash, g_str_equal); - + icon_sizes = g_new (IconSize, NUM_BUILTIN_SIZES); icon_sizes_allocated = NUM_BUILTIN_SIZES; icon_sizes_used = NUM_BUILTIN_SIZES; @@ -604,7 +600,7 @@ init_icon_sizes (void) * Even if we did I suppose removing the builtin sizes would be * disallowed. */ - + icon_sizes[GTK_ICON_SIZE_MENU].size = GTK_ICON_SIZE_MENU; icon_sizes[GTK_ICON_SIZE_MENU].name = "gtk-menu"; icon_sizes[GTK_ICON_SIZE_MENU].width = 16; @@ -619,7 +615,7 @@ init_icon_sizes (void) icon_sizes[GTK_ICON_SIZE_SMALL_TOOLBAR].name = "gtk-small-toolbar"; icon_sizes[GTK_ICON_SIZE_SMALL_TOOLBAR].width = 18; icon_sizes[GTK_ICON_SIZE_SMALL_TOOLBAR].height = 18; - + icon_sizes[GTK_ICON_SIZE_LARGE_TOOLBAR].size = GTK_ICON_SIZE_LARGE_TOOLBAR; icon_sizes[GTK_ICON_SIZE_LARGE_TOOLBAR].name = "gtk-large-toolbar"; icon_sizes[GTK_ICON_SIZE_LARGE_TOOLBAR].width = 24; @@ -642,10 +638,10 @@ init_icon_sizes (void) while (i < NUM_BUILTIN_SIZES) { gtk_icon_size_register_alias (icon_sizes[i].name, icon_sizes[i].size); - + ++i; } - + #undef NUM_BUILTIN_SIZES } } @@ -695,7 +691,7 @@ icon_size_set_for_settings (GtkSettings *settings, if (size == GTK_ICON_SIZE_INVALID) /* Reserve a place */ size = icon_size_register_intern (size_name, -1, -1); - + settings_sizes = get_settings_sizes (settings, NULL); if (size >= settings_sizes->len) { @@ -707,7 +703,7 @@ icon_size_set_for_settings (GtkSettings *settings, } settings_size = &g_array_index (settings_sizes, SettingsIconSize, size); - + settings_size->width = width; settings_size->height = height; } @@ -721,7 +717,7 @@ scan_icon_size_name (const char **pos, GString *out) while (g_ascii_isspace (*p)) p++; - + if (!((*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') || *p == '_' || *p == '-')) @@ -755,7 +751,7 @@ icon_size_setting_parse (GtkSettings *settings, while (pango_skip_space (&p)) { gint width, height; - + if (!scan_icon_size_name (&p, name_buf)) goto err; @@ -845,10 +841,10 @@ icon_sizes_init_for_settings (GtkSettings *settings) "notify::gtk-icon-sizes", G_CALLBACK (icon_size_settings_changed), NULL); - + icon_size_set_all_from_settings (settings); } - + static gboolean icon_size_lookup_intern (GtkSettings *settings, GtkIconSize size, @@ -858,7 +854,7 @@ icon_size_lookup_intern (GtkSettings *settings, GArray *settings_sizes; gint width_for_settings = -1; gint height_for_settings = -1; - + init_icon_sizes (); if (size == (GtkIconSize)-1) @@ -873,17 +869,18 @@ icon_size_lookup_intern (GtkSettings *settings, if (settings) { gboolean initial = FALSE; - + settings_sizes = get_settings_sizes (settings, &initial); + if (initial) icon_sizes_init_for_settings (settings); - + if (size < settings_sizes->len) { SettingsIconSize *settings_size; - + settings_size = &g_array_index (settings_sizes, SettingsIconSize, size); - + width_for_settings = settings_size->width; height_for_settings = settings_size->height; } @@ -907,7 +904,7 @@ icon_size_lookup_intern (GtkSettings *settings, * @height: location to store icon height * * Obtains the pixel size of a semantic icon size, possibly - * modified by user preferences for a particular + * modified by user preferences for a particular * #GtkSettings. Normally @size would be * #GTK_ICON_SIZE_MENU, #GTK_ICON_SIZE_BUTTON, etc. This function * isn't normally needed, gtk_widget_render_icon() is the usual @@ -916,11 +913,11 @@ icon_size_lookup_intern (GtkSettings *settings, * the width/height returned by gtk_icon_size_lookup(), because themes * are free to render the pixbuf however they like, including changing * the usual size. - * + * * Return value: %TRUE if @size was a valid size * * Since: 2.2 - **/ + */ gboolean gtk_icon_size_lookup_for_settings (GtkSettings *settings, GtkIconSize size, @@ -949,9 +946,9 @@ gtk_icon_size_lookup_for_settings (GtkSettings *settings, * the width/height returned by gtk_icon_size_lookup(), because themes * are free to render the pixbuf however they like, including changing * the usual size. - * + * * Return value: %TRUE if @size was a valid size - **/ + */ gboolean gtk_icon_size_lookup (GtkIconSize size, gint *widthp, @@ -971,7 +968,7 @@ icon_size_register_intern (const gchar *name, { IconAlias *old_alias; GtkIconSize size; - + init_icon_sizes (); old_alias = g_hash_table_lookup (icon_aliases, name); @@ -1018,8 +1015,7 @@ icon_size_register_intern (const gchar *name, * etc. Returns the integer value for the size. * * Returns: integer value representing the size - * - **/ + */ GtkIconSize gtk_icon_size_register (const gchar *name, gint width, @@ -1028,7 +1024,7 @@ gtk_icon_size_register (const gchar *name, g_return_val_if_fail (name != NULL, 0); g_return_val_if_fail (width > 0, 0); g_return_val_if_fail (height > 0, 0); - + return icon_size_register_intern (name, width, height); } @@ -1040,14 +1036,13 @@ gtk_icon_size_register (const gchar *name, * Registers @alias as another name for @target. * So calling gtk_icon_size_from_name() with @alias as argument * will return @target. - * - **/ + */ void gtk_icon_size_register_alias (const gchar *alias, GtkIconSize target) { IconAlias *ia; - + g_return_if_fail (alias != NULL); init_icon_sizes (); @@ -1077,20 +1072,20 @@ gtk_icon_size_register_alias (const gchar *alias, } } -/** +/** * gtk_icon_size_from_name: * @name: the name to look up. * @returns: the icon size with the given name. - * + * * Looks up the icon size associated with @name. - **/ + */ GtkIconSize gtk_icon_size_from_name (const gchar *name) { IconAlias *ia; init_icon_sizes (); - + ia = g_hash_table_lookup (icon_aliases, name); if (ia && icon_sizes[ia->target].width > 0) @@ -1103,10 +1098,10 @@ gtk_icon_size_from_name (const gchar *name) * gtk_icon_size_get_name: * @size: a #GtkIconSize. * @returns: the name of the given icon size. - * - * Gets the canonical name of the given icon size. The returned string + * + * Gets the canonical name of the given icon size. The returned string * is statically allocated and should not be freed. - **/ + */ G_CONST_RETURN gchar* gtk_icon_size_get_name (GtkIconSize size) { @@ -1164,7 +1159,7 @@ static guint cache_serial = 0; /** * gtk_icon_set_new: - * + * * Creates a new #GtkIconSet. A #GtkIconSet represents a single icon * in various sizes and widget states. It can provide a #GdkPixbuf * for a given size and state on request, and automatically caches @@ -1174,9 +1169,9 @@ static guint cache_serial = 0; * using #GtkIconSet directly. The one case where you'd use * #GtkIconSet is to create application-specific icon sets to place in * a #GtkIconFactory. - * + * * Return value: a new #GtkIconSet - **/ + */ GtkIconSet* gtk_icon_set_new (void) { @@ -1189,22 +1184,22 @@ gtk_icon_set_new (void) icon_set->cache = NULL; icon_set->cache_size = 0; icon_set->cache_serial = cache_serial; - + return icon_set; } /** * gtk_icon_set_new_from_pixbuf: * @pixbuf: a #GdkPixbuf - * + * * Creates a new #GtkIconSet with @pixbuf as the default/fallback * source image. If you don't add any additional #GtkIconSource to the * icon set, all variants of the icon will be created from @pixbuf, * using scaling, pixelation, etc. as required to adjust the icon size * or make the icon look insensitive/prelighted. - * + * * Return value: a new #GtkIconSet - **/ + */ GtkIconSet * gtk_icon_set_new_from_pixbuf (GdkPixbuf *pixbuf) { @@ -1219,7 +1214,7 @@ gtk_icon_set_new_from_pixbuf (GdkPixbuf *pixbuf) gtk_icon_source_set_pixbuf (&source, pixbuf); gtk_icon_set_add_source (set, &source); gtk_icon_source_set_pixbuf (&source, NULL); - + return set; } @@ -1227,11 +1222,11 @@ gtk_icon_set_new_from_pixbuf (GdkPixbuf *pixbuf) /** * gtk_icon_set_ref: * @icon_set: a #GtkIconSet. - * + * * Increments the reference count on @icon_set. - * + * * Return value: @icon_set. - **/ + */ GtkIconSet* gtk_icon_set_ref (GtkIconSet *icon_set) { @@ -1246,10 +1241,10 @@ gtk_icon_set_ref (GtkIconSet *icon_set) /** * gtk_icon_set_unref: * @icon_set: a #GtkIconSet - * + * * Decrements the reference count on @icon_set, and frees memory * if the reference count reaches 0. - **/ + */ void gtk_icon_set_unref (GtkIconSet *icon_set) { @@ -1279,7 +1274,7 @@ GType gtk_icon_set_get_type (void) { static GType our_type = 0; - + if (our_type == 0) our_type = g_boxed_type_register_static (I_("GtkIconSet"), (GBoxedCopyFunc) gtk_icon_set_ref, @@ -1291,9 +1286,9 @@ gtk_icon_set_get_type (void) /** * gtk_icon_set_copy: * @icon_set: a #GtkIconSet - * - * Copies @icon_set by value. - * + * + * Copies @icon_set by value. + * * Return value: a new #GtkIconSet identical to the first. **/ GtkIconSet* @@ -1301,7 +1296,7 @@ gtk_icon_set_copy (GtkIconSet *icon_set) { GtkIconSet *copy; GSList *tmp_list; - + copy = gtk_icon_set_new (); tmp_list = icon_set->sources; @@ -1318,7 +1313,7 @@ gtk_icon_set_copy (GtkIconSet *icon_set) copy->cache = copy_cache (icon_set, copy); copy->cache_size = icon_set->cache_size; copy->cache_serial = icon_set->cache_serial; - + return copy; } @@ -1334,8 +1329,8 @@ sizes_equivalent (GtkIconSize lhs, */ #if 1 return lhs == rhs; -#else - +#else + gint r_w, r_h, l_w, l_h; icon_size_lookup_intern (NULL, rhs, &r_w, &r_h); @@ -1354,7 +1349,7 @@ find_best_matching_source (GtkIconSet *icon_set, { GtkIconSource *source; GSList *tmp_list; - + /* We need to find the best icon source. Direction matters more * than state, state matters more than size. icon_set->sources * is sorted according to wildness, so if we take the first @@ -1362,13 +1357,13 @@ find_best_matching_source (GtkIconSet *icon_set, * multiple matches for a given "wildness" then the RC file contained * dumb stuff, and we end up with an arbitrary matching source) */ - + source = NULL; tmp_list = icon_set->sources; while (tmp_list != NULL) { GtkIconSource *s = tmp_list->data; - + if ((s->any_direction || (s->direction == direction)) && (s->any_state || (s->state == state)) && (s->any_size || size == (GtkIconSize)-1 || (sizes_equivalent (size, s->size)))) @@ -1379,13 +1374,13 @@ find_best_matching_source (GtkIconSet *icon_set, break; } } - + tmp_list = g_slist_next (tmp_list); } return source; } - + static gboolean ensure_filename_pixbuf (GtkIconSet *icon_set, GtkIconSource *source) @@ -1393,9 +1388,9 @@ ensure_filename_pixbuf (GtkIconSet *icon_set, if (source->filename_pixbuf == NULL) { GError *error = NULL; - + source->filename_pixbuf = gdk_pixbuf_new_from_file (source->source.filename, &error); - + if (source->filename_pixbuf == NULL) { /* Remove this icon source so we don't keep trying to @@ -1403,18 +1398,18 @@ ensure_filename_pixbuf (GtkIconSet *icon_set, */ g_warning (_("Error loading icon: %s"), error->message); g_error_free (error); - + icon_set->sources = g_slist_remove (icon_set->sources, source); - + gtk_icon_source_free (source); return FALSE; } } - + return TRUE; } - + static GdkPixbuf * render_icon_name_pixbuf (GtkIconSource *icon_source, GtkStyle *style, @@ -1433,7 +1428,7 @@ render_icon_name_pixbuf (GtkIconSource *icon_source, gint width, height, pixel_size; gint *sizes, *s, dist; GError *error = NULL; - + if (widget && gtk_widget_has_screen (widget)) screen = gtk_widget_get_screen (widget); else if (style && style->colormap) @@ -1452,8 +1447,7 @@ render_icon_name_pixbuf (GtkIconSource *icon_source, { if (size == (GtkIconSize)-1) { - /* Find an available size close to 48 - */ + /* Find an available size close to 48 */ sizes = gtk_icon_theme_get_icon_sizes (icon_theme, icon_source->source.icon_name); dist = 1000; width = height = 48; @@ -1472,7 +1466,7 @@ render_icon_name_pixbuf (GtkIconSource *icon_source, dist = 48 - *s; } } - else + else { if (*s - 48 < dist) { @@ -1481,7 +1475,7 @@ render_icon_name_pixbuf (GtkIconSource *icon_source, } } } - + g_free (sizes); } else @@ -1500,12 +1494,13 @@ render_icon_name_pixbuf (GtkIconSource *icon_source, if (!tmp_pixbuf) { - g_warning ("Error loading theme icon '%s' for stock: %s", - icon_source->source.icon_name, error->message); - g_error_free (error); + g_warning ("Error loading theme icon '%s' for stock: %s", + icon_source->source.icon_name, error ? error->message : ""); + if (error) + g_error_free (error); return NULL; } - + tmp_source = *icon_source; tmp_source.type = GTK_ICON_SOURCE_PIXBUF; tmp_source.source.pixbuf = tmp_pixbuf; @@ -1547,7 +1542,7 @@ find_and_render_icon_source (GtkIconSet *icon_set, while (pixbuf == NULL) { GtkIconSource *source = find_best_matching_source (icon_set, direction, state, size, failed); - + if (source == NULL) break; @@ -1612,7 +1607,7 @@ render_fallback_image (GtkStyle *style, gtk_icon_source_set_pixbuf (&fallback_source, pixbuf); g_object_unref (pixbuf); } - + return gtk_style_render_icon (style, &fallback_source, direction, @@ -1636,16 +1631,16 @@ render_fallback_image (GtkStyle *style, * @detail: detail to pass to the theme engine, or %NULL. * Note that passing a detail of anything but %NULL * will disable caching. - * + * * Renders an icon using gtk_style_render_icon(). In most cases, * gtk_widget_render_icon() is better, since it automatically provides * most of the arguments from the current widget settings. This * function never returns %NULL; if the icon can't be rendered * (perhaps because an image file fails to load), a default "missing * image" icon will be returned instead. - * + * * Return value: a #GdkPixbuf to be displayed - **/ + */ GdkPixbuf* gtk_icon_set_render_icon (GtkIconSet *icon_set, GtkStyle *style, @@ -1656,7 +1651,7 @@ gtk_icon_set_render_icon (GtkIconSet *icon_set, const char *detail) { GdkPixbuf *icon; - + g_return_val_if_fail (icon_set != NULL, NULL); g_return_val_if_fail (style == NULL || GTK_IS_STYLE (style), NULL); @@ -1667,7 +1662,7 @@ gtk_icon_set_render_icon (GtkIconSet *icon_set, { icon = find_in_cache (icon_set, style, direction, state, size); - + if (icon) { g_object_ref (icon); @@ -1684,7 +1679,7 @@ gtk_icon_set_render_icon (GtkIconSet *icon_set, if (detail == NULL) add_to_cache (icon_set, style, direction, state, size, icon); - + return icon; } @@ -1726,7 +1721,7 @@ icon_source_compare (gconstpointer ap, gconstpointer bp) * gtk_icon_set_render_icon(), but #GtkIconSet needs base images to * work with. The base images and when to use them are described by * a #GtkIconSource. - * + * * This function copies @source, so you can reuse the same source immediately * without affecting the icon set. * @@ -1745,10 +1740,9 @@ icon_source_compare (gconstpointer ap, gconstpointer bp) * * gtk_icon_set_new_from_pixbuf() creates a new icon set with a * default icon source based on the given pixbuf. - * - **/ + */ void -gtk_icon_set_add_source (GtkIconSet *icon_set, +gtk_icon_set_add_source (GtkIconSet *icon_set, const GtkIconSource *source) { g_return_if_fail (icon_set != NULL); @@ -1759,7 +1753,7 @@ gtk_icon_set_add_source (GtkIconSet *icon_set, g_warning ("Useless empty GtkIconSource"); return; } - + icon_set->sources = g_slist_insert_sorted (icon_set->sources, gtk_icon_source_copy (source), icon_source_compare); @@ -1773,8 +1767,7 @@ gtk_icon_set_add_source (GtkIconSet *icon_set, * * Obtains a list of icon sizes this icon set can render. The returned * array must be freed with g_free(). - * - **/ + */ void gtk_icon_set_get_sizes (GtkIconSet *icon_set, GtkIconSize **sizes, @@ -1783,11 +1776,11 @@ gtk_icon_set_get_sizes (GtkIconSet *icon_set, GSList *tmp_list; gboolean all_sizes = FALSE; GSList *specifics = NULL; - + g_return_if_fail (icon_set != NULL); g_return_if_fail (sizes != NULL); g_return_if_fail (n_sizes != NULL); - + tmp_list = icon_set->sources; while (tmp_list != NULL) { @@ -1802,7 +1795,7 @@ gtk_icon_set_get_sizes (GtkIconSet *icon_set, } else specifics = g_slist_prepend (specifics, GINT_TO_POINTER (source->size)); - + tmp_list = g_slist_next (tmp_list); } @@ -1812,11 +1805,11 @@ gtk_icon_set_get_sizes (GtkIconSet *icon_set, gint i; init_icon_sizes (); - + *sizes = g_new (GtkIconSize, icon_sizes_used); *n_sizes = icon_sizes_used - 1; - - i = 1; + + i = 1; while (i < icon_sizes_used) { (*sizes)[i - 1] = icon_sizes[i].size; @@ -1826,7 +1819,7 @@ gtk_icon_set_get_sizes (GtkIconSet *icon_set, else { gint i; - + *n_sizes = g_slist_length (specifics); *sizes = g_new (GtkIconSize, *n_sizes); @@ -1847,21 +1840,21 @@ gtk_icon_set_get_sizes (GtkIconSet *icon_set, /** * gtk_icon_source_new: - * + * * Creates a new #GtkIconSource. A #GtkIconSource contains a #GdkPixbuf (or * image filename) that serves as the base image for one or more of the * icons in a #GtkIconSet, along with a specification for which icons in the * icon set will be based on that pixbuf or image file. An icon set contains * a set of icons that represent "the same" logical concept in different states, * different global text directions, and different sizes. - * + * * So for example a web browser's "Back to Previous Page" icon might * point in a different direction in Hebrew and in English; it might * look different when insensitive; and it might change size depending * on toolbar mode (small/large icons). So a single icon set would * contain all those variants of the icon. #GtkIconSet contains a list * of #GtkIconSource from which it can derive specific icon variants in - * the set. + * the set. * * In the simplest case, #GtkIconSet contains one source pixbuf from * which it derives all variants. The convenience function @@ -1876,46 +1869,46 @@ gtk_icon_set_get_sizes (GtkIconSet *icon_set, * By default, the icon source has all parameters wildcarded. That is, * the icon source will be used as the base icon for any desired text * direction, widget state, or icon size. - * + * * Return value: a new #GtkIconSource - **/ + */ GtkIconSource* gtk_icon_source_new (void) { GtkIconSource *src; - + src = g_new0 (GtkIconSource, 1); src->direction = GTK_TEXT_DIR_NONE; src->size = GTK_ICON_SIZE_INVALID; src->state = GTK_STATE_NORMAL; - + src->any_direction = TRUE; src->any_state = TRUE; src->any_size = TRUE; - + return src; } /** * gtk_icon_source_copy: * @source: a #GtkIconSource - * + * * Creates a copy of @source; mostly useful for language bindings. - * + * * Return value: a new #GtkIconSource - **/ + */ GtkIconSource* gtk_icon_source_copy (const GtkIconSource *source) { GtkIconSource *copy; - + g_return_val_if_fail (source != NULL, NULL); copy = g_new (GtkIconSource, 1); *copy = *source; - + switch (copy->type) { case GTK_ICON_SOURCE_EMPTY: @@ -1945,10 +1938,10 @@ gtk_icon_source_copy (const GtkIconSource *source) /** * gtk_icon_source_free: * @source: a #GtkIconSource - * + * * Frees a dynamically-allocated icon source, along with its * filename, size, and pixbuf fields if those are not %NULL. - **/ + */ void gtk_icon_source_free (GtkIconSource *source) { @@ -1962,7 +1955,7 @@ GType gtk_icon_source_get_type (void) { static GType our_type = 0; - + if (our_type == 0) our_type = g_boxed_type_register_static (I_("GtkIconSource"), (GBoxedCopyFunc) gtk_icon_source_copy, @@ -2012,8 +2005,8 @@ icon_source_clear (GtkIconSource *source) * @filename: image file to use * * Sets the name of an image file to use as a base image when creating - * icon variants for #GtkIconSet. The filename must be absolute. - **/ + * icon variants for #GtkIconSet. The filename must be absolute. + */ void gtk_icon_source_set_filename (GtkIconSource *source, const gchar *filename) @@ -2024,9 +2017,9 @@ gtk_icon_source_set_filename (GtkIconSource *source, if (source->type == GTK_ICON_SOURCE_FILENAME && source->source.filename == filename) return; - + icon_source_clear (source); - + if (filename != NULL) { source->type = GTK_ICON_SOURCE_FILENAME; @@ -2044,7 +2037,7 @@ gtk_icon_source_set_filename (GtkIconSource *source, * * Sets the name of an icon to look up in the current icon theme * to use as a base image when creating icon variants for #GtkIconSet. - **/ + */ void gtk_icon_source_set_icon_name (GtkIconSource *source, const gchar *icon_name) @@ -2056,7 +2049,7 @@ gtk_icon_source_set_icon_name (GtkIconSource *source, return; icon_source_clear (source); - + if (icon_name != NULL) { source->type = GTK_ICON_SOURCE_ICON_NAME; @@ -2071,20 +2064,20 @@ gtk_icon_source_set_icon_name (GtkIconSource *source, * * Sets a pixbuf to use as a base image when creating icon variants * for #GtkIconSet. - **/ + */ void gtk_icon_source_set_pixbuf (GtkIconSource *source, GdkPixbuf *pixbuf) { g_return_if_fail (source != NULL); g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf)); - + if (source->type == GTK_ICON_SOURCE_PIXBUF && source->source.pixbuf == pixbuf) return; icon_source_clear (source); - + if (pixbuf != NULL) { source->type = GTK_ICON_SOURCE_PIXBUF; @@ -2095,14 +2088,14 @@ gtk_icon_source_set_pixbuf (GtkIconSource *source, /** * gtk_icon_source_get_filename: * @source: a #GtkIconSource - * + * * Retrieves the source filename, or %NULL if none is set. The * filename is not a copy, and should not be modified or expected to * persist beyond the lifetime of the icon source. - * + * * Return value: image filename. This string must not be modified * or freed. - **/ + */ G_CONST_RETURN gchar* gtk_icon_source_get_filename (const GtkIconSource *source) { @@ -2117,13 +2110,13 @@ gtk_icon_source_get_filename (const GtkIconSource *source) /** * gtk_icon_source_get_icon_name: * @source: a #GtkIconSource - * + * * Retrieves the source icon name, or %NULL if none is set. The * icon_name is not a copy, and should not be modified or expected to * persist beyond the lifetime of the icon source. - * + * * Return value: icon name. This string must not be modified or freed. - **/ + */ G_CONST_RETURN gchar* gtk_icon_source_get_icon_name (const GtkIconSource *source) { @@ -2139,7 +2132,7 @@ gtk_icon_source_get_icon_name (const GtkIconSource *source) /** * gtk_icon_source_get_pixbuf: * @source: a #GtkIconSource - * + * * Retrieves the source pixbuf, or %NULL if none is set. * In addition, if a filename source is in use, this * function in some cases will return the pixbuf from @@ -2147,14 +2140,14 @@ gtk_icon_source_get_icon_name (const GtkIconSource *source) * for the GtkIconSource passed to the GtkStyle::render_icon() * virtual function. The reference count on the pixbuf is * not incremented. - * + * * Return value: source pixbuf - **/ + */ GdkPixbuf* gtk_icon_source_get_pixbuf (const GtkIconSource *source) { g_return_val_if_fail (source != NULL, NULL); - + if (source->type == GTK_ICON_SOURCE_PIXBUF) return source->source.pixbuf; else if (source->type == GTK_ICON_SOURCE_FILENAME) @@ -2177,8 +2170,7 @@ gtk_icon_source_get_pixbuf (const GtkIconSource *source) * * #GtkIconSet prefers non-wildcarded sources (exact matches) over * wildcarded sources, and will use an exact match when possible. - * - **/ + */ void gtk_icon_source_set_direction_wildcarded (GtkIconSource *source, gboolean setting) @@ -2206,7 +2198,7 @@ gtk_icon_source_set_direction_wildcarded (GtkIconSource *source, * produce an appropriate icon for a given state, for example * lightening an image on prelight, but will not modify source images * that match exactly. - **/ + */ void gtk_icon_source_set_state_wildcarded (GtkIconSource *source, gboolean setting) @@ -2234,40 +2226,40 @@ gtk_icon_source_set_state_wildcarded (GtkIconSource *source, * #GtkIconSet will normally scale wildcarded source images to produce * an appropriate icon at a given size, but will not change the size * of source images that match exactly. - **/ + */ void gtk_icon_source_set_size_wildcarded (GtkIconSource *source, gboolean setting) { g_return_if_fail (source != NULL); - source->any_size = setting != FALSE; + source->any_size = setting != FALSE; } /** * gtk_icon_source_get_size_wildcarded: * @source: a #GtkIconSource - * + * * Gets the value set by gtk_icon_source_set_size_wildcarded(). - * + * * Return value: %TRUE if this icon source is a base for any icon size variant - **/ + */ gboolean gtk_icon_source_get_size_wildcarded (const GtkIconSource *source) { g_return_val_if_fail (source != NULL, TRUE); - + return source->any_size; } /** * gtk_icon_source_get_state_wildcarded: * @source: a #GtkIconSource - * + * * Gets the value set by gtk_icon_source_set_state_wildcarded(). - * + * * Return value: %TRUE if this icon source is a base for any widget state variant - **/ + */ gboolean gtk_icon_source_get_state_wildcarded (const GtkIconSource *source) { @@ -2279,11 +2271,11 @@ gtk_icon_source_get_state_wildcarded (const GtkIconSource *source) /** * gtk_icon_source_get_direction_wildcarded: * @source: a #GtkIconSource - * + * * Gets the value set by gtk_icon_source_set_direction_wildcarded(). - * + * * Return value: %TRUE if this icon source is a base for any text direction variant - **/ + */ gboolean gtk_icon_source_get_direction_wildcarded (const GtkIconSource *source) { @@ -2299,13 +2291,12 @@ gtk_icon_source_get_direction_wildcarded (const GtkIconSource *source) * * Sets the text direction this icon source is intended to be used * with. - * + * * Setting the text direction on an icon source makes no difference * if the text direction is wildcarded. Therefore, you should usually * call gtk_icon_source_set_direction_wildcarded() to un-wildcard it * in addition to calling this function. - * - **/ + */ void gtk_icon_source_set_direction (GtkIconSource *source, GtkTextDirection direction) @@ -2322,13 +2313,12 @@ gtk_icon_source_set_direction (GtkIconSource *source, * * Sets the widget state this icon source is intended to be used * with. - * + * * Setting the widget state on an icon source makes no difference * if the state is wildcarded. Therefore, you should usually * call gtk_icon_source_set_state_wildcarded() to un-wildcard it * in addition to calling this function. - * - **/ + */ void gtk_icon_source_set_state (GtkIconSource *source, GtkStateType state) @@ -2345,13 +2335,12 @@ gtk_icon_source_set_state (GtkIconSource *source, * * Sets the icon size this icon source is intended to be used * with. - * + * * Setting the icon size on an icon source makes no difference * if the size is wildcarded. Therefore, you should usually * call gtk_icon_source_set_size_wildcarded() to un-wildcard it * in addition to calling this function. - * - **/ + */ void gtk_icon_source_set_size (GtkIconSource *source, GtkIconSize size) @@ -2364,13 +2353,13 @@ gtk_icon_source_set_size (GtkIconSource *source, /** * gtk_icon_source_get_direction: * @source: a #GtkIconSource - * + * * Obtains the text direction this icon source applies to. The return - * value is only useful/meaningful if the text direction is <emphasis>not</emphasis> + * value is only useful/meaningful if the text direction is <emphasis>not</emphasis> * wildcarded. - * + * * Return value: text direction this source matches - **/ + */ GtkTextDirection gtk_icon_source_get_direction (const GtkIconSource *source) { @@ -2382,13 +2371,13 @@ gtk_icon_source_get_direction (const GtkIconSource *source) /** * gtk_icon_source_get_state: * @source: a #GtkIconSource - * + * * Obtains the widget state this icon source applies to. The return * value is only useful/meaningful if the widget state is <emphasis>not</emphasis> * wildcarded. - * + * * Return value: widget state this source matches - **/ + */ GtkStateType gtk_icon_source_get_state (const GtkIconSource *source) { @@ -2400,12 +2389,12 @@ gtk_icon_source_get_state (const GtkIconSource *source) /** * gtk_icon_source_get_size: * @source: a #GtkIconSource - * + * * Obtains the icon size this source applies to. The return value * is only useful/meaningful if the icon size is <emphasis>not</emphasis> wildcarded. - * + * * Return value: icon size this source matches. - **/ + */ GtkIconSize gtk_icon_source_get_size (const GtkIconSource *source) { @@ -2463,7 +2452,7 @@ find_in_cache (GtkIconSet *icon_set, GSList *prev; ensure_cache_up_to_date (icon_set); - + prev = NULL; tmp_list = icon_set->cache; while (tmp_list != NULL) @@ -2482,10 +2471,10 @@ find_in_cache (GtkIconSet *icon_set, tmp_list->next = icon_set->cache; icon_set->cache = tmp_list; } - + return icon->pixbuf; } - + prev = tmp_list; tmp_list = g_slist_next (tmp_list); } @@ -2511,7 +2500,7 @@ add_to_cache (GtkIconSet *icon_set, * its address could be reused by another style, creating a * really weird bug */ - + if (style) g_object_ref (style); @@ -2527,12 +2516,12 @@ add_to_cache (GtkIconSet *icon_set, if (icon->style) attach_to_style (icon_set, icon->style); - + if (icon_set->cache_size >= NUM_CACHED_ICONS) { /* Remove oldest item in the cache */ GSList *tmp_list; - + tmp_list = icon_set->cache; /* Find next-to-last link */ @@ -2582,9 +2571,9 @@ clear_cache (GtkIconSet *icon_set, last_style = icon->style; } } - - cached_icon_free (icon); - + + cached_icon_free (icon); + tmp_list = g_slist_next (tmp_list); } @@ -2599,7 +2588,7 @@ copy_cache (GtkIconSet *icon_set, GSList *copy = NULL; ensure_cache_up_to_date (icon_set); - + tmp_list = icon_set->cache; while (tmp_list != NULL) { @@ -2613,13 +2602,13 @@ copy_cache (GtkIconSet *icon_set, attach_to_style (copy_recipient, icon_copy->style); g_object_ref (icon_copy->style); } - + g_object_ref (icon_copy->pixbuf); icon_copy->size = icon->size; - - copy = g_slist_prepend (copy, icon_copy); - + + copy = g_slist_prepend (copy, icon_copy); + tmp_list = g_slist_next (tmp_list); } @@ -2673,7 +2662,7 @@ iconsets_foreach (gpointer key, * time all cache entries will have the same style, * so this is faster anyway. */ - + clear_cache (icon_set, FALSE); } @@ -2681,7 +2670,7 @@ static void style_dnotify (gpointer data) { GHashTable *table = data; - + g_hash_table_foreach (table, iconsets_foreach, NULL); g_hash_table_destroy (table); @@ -2696,13 +2685,13 @@ _gtk_icon_set_invalidate_caches (void) /** * _gtk_icon_factory_list_ids: - * + * * Gets all known IDs stored in an existing icon factory. * The strings in the returned list aren't copied. * The list itself should be freed. - * + * * Return value: List of ids in icon factories - **/ + */ GList* _gtk_icon_factory_list_ids (void) { @@ -2712,18 +2701,18 @@ _gtk_icon_factory_list_ids (void) ids = NULL; _gtk_icon_factory_ensure_default_icons (); - + tmp_list = all_icon_factories; while (tmp_list != NULL) { GList *these_ids; - + GtkIconFactory *factory = GTK_ICON_FACTORY (tmp_list->data); these_ids = g_hash_table_get_keys (factory->icons); - + ids = g_list_concat (ids, these_ids); - + tmp_list = g_slist_next (tmp_list); } @@ -2733,7 +2722,7 @@ _gtk_icon_factory_list_ids (void) typedef struct { GSList *sources; gboolean in_source; - + } IconFactoryParserData; typedef struct { @@ -2764,7 +2753,7 @@ icon_source_start_element (GMarkupParseContext *context, IconSourceParserData *source_data; gchar *error_msg; GQuark error_domain; - + parser_data = (IconFactoryParserData*)user_data; if (!parser_data->in_source) @@ -2787,7 +2776,7 @@ icon_source_start_element (GMarkupParseContext *context, goto error; } } - + for (i = 0; names[i]; i++) { if (strcmp (names[i], "stock-id") == 0) @@ -2851,7 +2840,7 @@ icon_source_start_element (GMarkupParseContext *context, { gchar *tmp; gint line_number, char_number; - + g_markup_parse_context_get_position (context, &line_number, &char_number); @@ -2865,7 +2854,7 @@ icon_source_start_element (GMarkupParseContext *context, tmp); #else g_warning ("%s", tmp); -#endif +#endif g_free (tmp); g_free (stock_id); g_free (filename); @@ -2909,7 +2898,7 @@ gtk_icon_factory_buildable_custom_tag_end (GtkBuildable *buildable, gpointer *user_data) { GtkIconFactory *icon_factory; - + icon_factory = GTK_ICON_FACTORY (buildable); if (strcmp (tagname, "sources") == 0) diff --git a/gtk/gtkicontheme.c b/gtk/gtkicontheme.c index 6df05ef80e..db183310d7 100644 --- a/gtk/gtkicontheme.c +++ b/gtk/gtkicontheme.c @@ -3508,7 +3508,7 @@ gtk_icon_theme_lookup_by_gicon (GtkIconTheme *icon_theme, * @icon_theme: a #GtkIconTheme * @pixbuf: the pixbuf to wrap in a #GtkIconInfo * - * Creates a #GtkIconInfo for a #GtkPixbuf. + * Creates a #GtkIconInfo for a #GdkPixbuf. * * Returns: a #GtkIconInfo * diff --git a/gtk/gtkiconview.c b/gtk/gtkiconview.c index 865b8d56be..bb6ba29100 100644 --- a/gtk/gtkiconview.c +++ b/gtk/gtkiconview.c @@ -46,7 +46,6 @@ #undef DEBUG_ICON_VIEW #define SCROLL_EDGE_SIZE 15 -#define ITEM_PADDING 6 #define GTK_ICON_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_ICON_VIEW, GtkIconViewPrivate)) @@ -150,6 +149,7 @@ struct _GtkIconViewPrivate gint row_spacing; gint column_spacing; gint margin; + gint item_padding; gint text_column; gint markup_column; @@ -160,8 +160,6 @@ struct _GtkIconViewPrivate gint tooltip_column; - guint draw_focus : 1; - /* Drag-and-drop. */ GdkModifierType start_button_mask; gint pressed_button; @@ -186,7 +184,9 @@ struct _GtkIconViewPrivate guint empty_view_drop :1; guint ctrl_pressed : 1; - guint shift_pressed : 1; + guint shift_pressed : 1; + + guint draw_focus : 1; }; /* Signals */ @@ -220,7 +220,8 @@ enum PROP_COLUMN_SPACING, PROP_MARGIN, PROP_REORDERABLE, - PROP_TOOLTIP_COLUMN + PROP_TOOLTIP_COLUMN, + PROP_ITEM_PADDING }; /* GObject vfuncs */ @@ -714,7 +715,6 @@ gtk_icon_view_class_init (GtkIconViewClass *klass) 0, G_MAXINT, 6, GTK_PARAM_READWRITE)); - /** * GtkIconView:orientation: * @@ -758,6 +758,22 @@ gtk_icon_view_class_init (GtkIconViewClass *klass) -1, GTK_PARAM_READWRITE)); + /** + * GtkIconView:item-padding: + * + * The item-padding property specifies the padding around each + * of the icon view's item. + * + * Since: 2.18 + */ + g_object_class_install_property (gobject_class, + PROP_ITEM_PADDING, + g_param_spec_int ("item-padding", + P_("Item Padding"), + P_("Padding around icon view items"), + 0, G_MAXINT, 6, + GTK_PARAM_READWRITE)); + /* Style properties */ @@ -1106,6 +1122,7 @@ gtk_icon_view_init (GtkIconView *icon_view) icon_view->priv->row_spacing = 6; icon_view->priv->column_spacing = 6; icon_view->priv->margin = 6; + icon_view->priv->item_padding = 6; icon_view->priv->draw_focus = TRUE; } @@ -1216,6 +1233,10 @@ gtk_icon_view_set_property (GObject *object, gtk_icon_view_set_tooltip_column (icon_view, g_value_get_int (value)); break; + case PROP_ITEM_PADDING: + gtk_icon_view_set_item_padding (icon_view, g_value_get_int (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1277,6 +1298,10 @@ gtk_icon_view_get_property (GObject *object, g_value_set_int (value, icon_view->priv->tooltip_column); break; + case PROP_ITEM_PADDING: + g_value_set_int (value, icon_view->priv->item_padding); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1418,10 +1443,10 @@ gtk_icon_view_allocate_children (GtkIconView *icon_view) /* totally ignore our child's requisition */ if (child->cell < 0) { - allocation.x = child->item->x + ITEM_PADDING; - allocation.y = child->item->y + ITEM_PADDING; - allocation.width = child->item->width - ITEM_PADDING * 2; - allocation.height = child->item->height - ITEM_PADDING * 2; + allocation.x = child->item->x + icon_view->priv->item_padding; + allocation.y = child->item->y + icon_view->priv->item_padding; + allocation.width = child->item->width - icon_view->priv->item_padding * 2; + allocation.height = child->item->height - icon_view->priv->item_padding * 2; } else { @@ -1520,6 +1545,11 @@ gtk_icon_view_expose (GtkWidget *widget, if (expose->window != icon_view->priv->bin_window) return FALSE; + /* If a layout has been scheduled, do it now so that all + * cell view items have valid sizes before we proceed. */ + if (icon_view->priv->layout_idle_id != 0) + gtk_icon_view_layout (icon_view); + cr = gdk_cairo_create (icon_view->priv->bin_window); cairo_set_line_width (cr, 1.); @@ -2858,16 +2888,16 @@ gtk_icon_view_get_cell_area (GtkIconView *icon_view, if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL) { cell_area->x = item->box[info->position].x - item->before[info->position]; - cell_area->y = item->y + ITEM_PADDING; + cell_area->y = item->y + icon_view->priv->item_padding; cell_area->width = item->box[info->position].width + item->before[info->position] + item->after[info->position]; - cell_area->height = item->height - ITEM_PADDING * 2; + cell_area->height = item->height - icon_view->priv->item_padding * 2; } else { - cell_area->x = item->x + ITEM_PADDING; + cell_area->x = item->x + icon_view->priv->item_padding; cell_area->y = item->box[info->position].y - item->before[info->position]; - cell_area->width = item->width - ITEM_PADDING * 2; + cell_area->width = item->width - icon_view->priv->item_padding * 2; cell_area->height = item->box[info->position].height + item->before[info->position] + item->after[info->position]; } @@ -2925,7 +2955,7 @@ adjust_wrap_width (GtkIconView *icon_view, wrap_width = item_width - pixbuf_width - icon_view->priv->spacing; } - wrap_width -= ITEM_PADDING * 2; + wrap_width -= icon_view->priv->item_padding * 2; g_object_set (text_info->cell, "wrap-width", wrap_width, NULL); g_object_set (text_info->cell, "width", wrap_width, NULL); @@ -2986,8 +3016,8 @@ gtk_icon_view_calculate_item_size (GtkIconView *icon_view, } } - item->width += ITEM_PADDING * 2; - item->height += ITEM_PADDING * 2; + item->width += icon_view->priv->item_padding * 2; + item->height += icon_view->priv->item_padding * 2; } static void @@ -3016,8 +3046,8 @@ gtk_icon_view_calculate_item_size2 (GtkIconView *icon_view, item->height += max_height[i] + (i > 0 ? spacing : 0); } - cell_area.x = item->x + ITEM_PADDING; - cell_area.y = item->y + ITEM_PADDING; + cell_area.x = item->x + icon_view->priv->item_padding; + cell_area.y = item->y + icon_view->priv->item_padding; for (k = 0; k < 2; k++) for (l = icon_view->priv->cell_list, i = 0; l; l = l->next, i++) @@ -3032,7 +3062,7 @@ gtk_icon_view_calculate_item_size2 (GtkIconView *icon_view, if (icon_view->priv->orientation == GTK_ORIENTATION_HORIZONTAL) { - /* We should not subtract ITEM_PADDING from item->height, + /* We should not subtract icon_view->priv->item_padding from item->height, * because item->height is recalculated above using * max_height which does not contain item padding. */ @@ -3044,7 +3074,7 @@ gtk_icon_view_calculate_item_size2 (GtkIconView *icon_view, /* item->width is not recalculated and thus needs to be * corrected for the padding. */ - cell_area.width = item->width - 2 * ITEM_PADDING; + cell_area.width = item->width - 2 * icon_view->priv->item_padding; cell_area.height = max_height[i]; } @@ -3063,9 +3093,9 @@ gtk_icon_view_calculate_item_size2 (GtkIconView *icon_view, } else { - if (item->box[info->position].width > item->width - ITEM_PADDING * 2) + if (item->box[info->position].width > item->width - icon_view->priv->item_padding * 2) { - item->width = item->box[info->position].width + ITEM_PADDING * 2; + item->width = item->box[info->position].width + icon_view->priv->item_padding * 2; cell_area.width = item->width; } item->before[info->position] = item->box[info->position].y - cell_area.y; @@ -3083,7 +3113,7 @@ gtk_icon_view_calculate_item_size2 (GtkIconView *icon_view, } } - item->height += ITEM_PADDING * 2; + item->height += icon_view->priv->item_padding * 2; } static void @@ -3444,6 +3474,9 @@ gtk_icon_view_get_item_at_coords (GtkIconView *icon_view, GList *items, *l; GdkRectangle box; + if (cell_at_pos) + *cell_at_pos = NULL; + for (items = icon_view->priv->items; items; items = items->next) { GtkIconViewItem *item = items->data; @@ -3458,12 +3491,12 @@ gtk_icon_view_get_item_at_coords (GtkIconView *icon_view, for (l = icon_view->priv->cell_list; l; l = l->next) { GtkIconViewCellInfo *info = (GtkIconViewCellInfo *)l->data; - + if (!info->cell->visible) continue; - + gtk_icon_view_get_cell_box (icon_view, item, info, &box); - + if ((x >= box.x && x <= box.x + box.width && y >= box.y && y <= box.y + box.height) || (x >= box.x && @@ -3473,16 +3506,13 @@ gtk_icon_view_get_item_at_coords (GtkIconView *icon_view, { if (cell_at_pos) *cell_at_pos = info; - + return item; } } if (only_in_cell) return NULL; - - if (cell_at_pos) - *cell_at_pos = NULL; } return item; @@ -6239,6 +6269,51 @@ gtk_icon_view_get_margin (GtkIconView *icon_view) return icon_view->priv->margin; } +/** + * gtk_icon_view_set_item_padding: + * @icon_view: a #GtkIconView + * @column_spacing: the item padding + * + * Sets the ::item-padding property which specifies the padding + * around each of the icon view's items. + * + * Since: 2.18 + */ +void +gtk_icon_view_set_item_padding (GtkIconView *icon_view, + gint item_padding) +{ + g_return_if_fail (GTK_IS_ICON_VIEW (icon_view)); + + if (icon_view->priv->item_padding != item_padding) + { + icon_view->priv->item_padding = item_padding; + + gtk_icon_view_stop_editing (icon_view, TRUE); + gtk_icon_view_invalidate_sizes (icon_view); + gtk_icon_view_queue_layout (icon_view); + + g_object_notify (G_OBJECT (icon_view), "item-padding"); + } +} + +/** + * gtk_icon_view_get_item_padding: + * @icon_view: a #GtkIconView + * + * Returns the value of the ::item-padding property. + * + * Return value: the padding around items + * + * Since: 2.18 + */ +gint +gtk_icon_view_get_item_padding (GtkIconView *icon_view) +{ + g_return_val_if_fail (GTK_IS_ICON_VIEW (icon_view), -1); + + return icon_view->priv->item_padding; +} /* Get/set whether drag_motion requested the drag data and * drag_data_received should thus not actually insert the data, @@ -7155,7 +7230,7 @@ gtk_icon_view_set_drag_dest_item (GtkIconView *icon_view, /* special case a drop on an empty model */ icon_view->priv->empty_view_drop = FALSE; - if (pos == GTK_TREE_VIEW_DROP_BEFORE && path + if (pos == GTK_ICON_VIEW_DROP_ABOVE && path && gtk_tree_path_get_depth (path) == 1 && gtk_tree_path_get_indices (path)[0] == 0) { diff --git a/gtk/gtkiconview.h b/gtk/gtkiconview.h index 65b0588d24..7b9e51d227 100644 --- a/gtk/gtkiconview.h +++ b/gtk/gtkiconview.h @@ -125,6 +125,9 @@ gint gtk_icon_view_get_column_spacing (GtkIconView *icon_view); void gtk_icon_view_set_margin (GtkIconView *icon_view, gint margin); gint gtk_icon_view_get_margin (GtkIconView *icon_view); +void gtk_icon_view_set_item_padding (GtkIconView *icon_view, + gint item_padding); +gint gtk_icon_view_get_item_padding (GtkIconView *icon_view); GtkTreePath * gtk_icon_view_get_path_at_pos (GtkIconView *icon_view, diff --git a/gtk/gtkimage.c b/gtk/gtkimage.c index 036560f377..4d80267108 100644 --- a/gtk/gtkimage.c +++ b/gtk/gtkimage.c @@ -45,6 +45,7 @@ struct _GtkImagePrivate gchar *filename; gint pixel_size; + guint need_calc_size : 1; }; #define GTK_IMAGE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_IMAGE, GtkImagePrivate)) @@ -1710,6 +1711,7 @@ gtk_image_expose (GtkWidget *widget, { GtkImage *image; GtkMisc *misc; + GtkImagePrivate *priv; GdkRectangle area, image_bound; gfloat xalign; gint x, y, mask_x, mask_y; @@ -1719,6 +1721,7 @@ gtk_image_expose (GtkWidget *widget, image = GTK_IMAGE (widget); misc = GTK_MISC (widget); + priv = GTK_IMAGE_GET_PRIVATE (image); area = event->area; @@ -1727,7 +1730,7 @@ gtk_image_expose (GtkWidget *widget, * and size_request() if something explicitely forces * a redraw. */ - if (widget->requisition.width == 0 && widget->requisition.height == 0) + if (priv->need_calc_size) gtk_image_calc_size (image); if (!gdk_rectangle_intersect (&area, &widget->allocation, &area)) @@ -1737,7 +1740,7 @@ gtk_image_expose (GtkWidget *widget, xalign = misc->xalign; else xalign = 1.0 - misc->xalign; - + x = floor (widget->allocation.x + misc->xpad + ((widget->allocation.width - widget->requisition.width) * xalign)); y = floor (widget->allocation.y + misc->ypad @@ -2149,8 +2152,13 @@ gtk_image_reset (GtkImage *image) void gtk_image_clear (GtkImage *image) { - gtk_image_reset (image); + GtkImagePrivate *priv; + + priv = GTK_IMAGE_GET_PRIVATE (image); + + priv->need_calc_size = 1; + gtk_image_reset (image); gtk_image_update_size (image, 0, 0); } @@ -2159,7 +2167,12 @@ gtk_image_calc_size (GtkImage *image) { GtkWidget *widget = GTK_WIDGET (image); GdkPixbuf *pixbuf = NULL; - + GtkImagePrivate *priv; + + priv = GTK_IMAGE_GET_PRIVATE (image); + + priv->need_calc_size = 0; + /* We update stock/icon set on every size request, because * the theme could have affected the size; for other kinds of * image, we just update the requisition when the image data diff --git a/gtk/gtkimagemenuitem.c b/gtk/gtkimagemenuitem.c index 8948c9312a..7f0785d64e 100644 --- a/gtk/gtkimagemenuitem.c +++ b/gtk/gtkimagemenuitem.c @@ -615,7 +615,6 @@ gtk_image_menu_item_update (GtkActivatable *activatable, const gchar *property_name) { GtkImageMenuItem *image_menu_item; - GtkWidget *image; gboolean use_appearance; image_menu_item = GTK_IMAGE_MENU_ITEM (activatable); diff --git a/gtk/gtkimcontextsimple.c b/gtk/gtkimcontextsimple.c index 62bab7c0c9..523f771dab 100644 --- a/gtk/gtkimcontextsimple.c +++ b/gtk/gtkimcontextsimple.c @@ -22,6 +22,7 @@ #include <string.h> #include <gdk/gdkkeysyms.h> +#include "gtkprivate.h" #include "gtkaccelgroup.h" #include "gtkimcontextsimple.h" #include "gtksettings.h" @@ -61,7 +62,7 @@ struct _GtkComposeTableCompact static const GtkComposeTableCompact gtk_compose_table_compact = { gtk_compose_seqs_compact, 5, - 23, + 24, 6 }; @@ -595,7 +596,7 @@ check_algorithmically (GtkIMContextSimple *context_simple, * with Ctrl-Shift-U, then release the modifiers before typing any * digits, and enter the digits without modifiers. */ -#define HEX_MOD_MASK (GDK_CONTROL_MASK | GDK_SHIFT_MASK) +#define HEX_MOD_MASK (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK) static gboolean check_hex (GtkIMContextSimple *context_simple, diff --git a/gtk/gtkimcontextsimpleseqs.h b/gtk/gtkimcontextsimpleseqs.h index 9e3fc29e99..131b3db6e1 100644 --- a/gtk/gtkimcontextsimpleseqs.h +++ b/gtk/gtkimcontextsimpleseqs.h @@ -18,10 +18,9 @@ */ /* - * File auto-generated from script found at gtk/compose-parse.py, with the --gtk parameter, + * File auto-generated from script found at http://bugzilla.gnome.org/show_bug.cgi?id=321896 * using the input files * Input : http://gitweb.freedesktop.org/?p=xorg/lib/libX11.git;a=blob_plain;f=nls/en_US.UTF-8/Compose.pre - * Input : http://svn.gnome.org/viewcvs/gtk%2B/trunk/gtk/gtk-compose-lookaside.txt * Input : http://www.cl.cam.ac.uk/~mgk25/ucs/keysyms.txt * Input : http://www.unicode.org/Public/UNIDATA/UnicodeData.txt * @@ -68,29 +67,30 @@ */ static const guint16 gtk_compose_seqs_compact[] = { -GDK_dead_stroke, 138, 226, 235, 235, 235, -GDK_Greek_accentdieresis, 235, 239, 239, 239, 239, -GDK_dead_grave, 239, 299, 386, 598, 598, -GDK_dead_acute, 598, 664, 760, 1036, 1036, -GDK_dead_circumflex, 1036, 1176, 1176, 1376, 1376, -GDK_dead_tilde, 1376, 1458, 1521, 1661, 1661, -GDK_dead_macron, 1661, 1707, 1707, 1779, 1779, -GDK_dead_breve, 1779, 1833, 1833, 1857, 1857, -GDK_dead_abovedot, 1857, 1887, 1890, 1922, 1922, -GDK_dead_diaeresis, 1922, 2010, 2019, 2043, 2043, -GDK_dead_abovering, 2043, 2053, 2053, 2053, 2053, -GDK_dead_doubleacute, 2053, 2063, 2063, 2063, 2063, -GDK_dead_caron, 2063, 2089, 2089, 2097, 2097, -GDK_dead_cedilla, 2097, 2111, 2111, 2111, 2111, -GDK_dead_ogonek, 2111, 2121, 2121, 2121, 2121, -GDK_dead_iota, 2121, 2143, 2242, 2674, 3334, -GDK_dead_voiced_sound, 3334, 3380, 3380, 3380, 3380, -GDK_dead_semivoiced_sound, 3380, 3390, 3390, 3390, 3390, -GDK_dead_belowdot, 3390, 3400, 3400, 3416, 3416, -GDK_dead_hook, 3416, 3494, 3494, 3550, 3550, -GDK_dead_psili, 3550, 3578, 3578, 3578, 3578, -GDK_dead_dasia, 3578, 3610, 3610, 3610, 3610, -GDK_Multi_key, 3610, 3610, 9589, 13297, 15157, +GDK_dead_stroke, 144, 232, 241, 241, 241, +GDK_Greek_accentdieresis, 241, 245, 245, 245, 245, +GDK_dead_grave, 245, 307, 394, 606, 606, +GDK_dead_acute, 606, 674, 770, 1046, 1046, +GDK_dead_circumflex, 1046, 1186, 1186, 1386, 1386, +GDK_dead_tilde, 1386, 1470, 1533, 1673, 1673, +GDK_dead_macron, 1673, 1719, 1719, 1791, 1791, +GDK_dead_breve, 1791, 1845, 1845, 1869, 1869, +GDK_dead_abovedot, 1869, 1899, 1902, 1934, 1934, +GDK_dead_diaeresis, 1934, 2022, 2031, 2055, 2055, +GDK_dead_abovering, 2055, 2065, 2065, 2065, 2065, +GDK_dead_doubleacute, 2065, 2075, 2075, 2075, 2075, +GDK_dead_caron, 2075, 2121, 2121, 2129, 2129, +GDK_dead_cedilla, 2129, 2143, 2143, 2143, 2143, +GDK_dead_ogonek, 2143, 2153, 2153, 2153, 2153, +GDK_dead_iota, 2153, 2175, 2274, 2706, 3366, +GDK_dead_voiced_sound, 3366, 3412, 3412, 3412, 3412, +GDK_dead_semivoiced_sound, 3412, 3422, 3422, 3422, 3422, +GDK_dead_belowdot, 3422, 3438, 3438, 3454, 3454, +GDK_dead_hook, 3454, 3532, 3532, 3588, 3588, +GDK_dead_horn, 3588, 3598, 3598, 3598, 3598, +GDK_dead_psili, 3598, 3626, 3626, 3626, 3626, +GDK_dead_dasia, 3626, 3658, 3658, 3658, 3658, +GDK_Multi_key, 3658, 3658, 9658, 13366, 15231, GDK_space, 0x002F, GDK_2, 0x01BB, GDK_A, 0x023A, @@ -143,6 +143,7 @@ GDK_Greek_upsilon, 0x03B0, GDK_space, 0x0060, GDK_V, 0x01DB, GDK_v, 0x01DC, +GDK_nobreakspace, 0x0300, GDK_Abreve, 0x1EB0, GDK_abreve, 0x1EB1, GDK_Emacron, 0x1E14, @@ -255,6 +256,7 @@ GDK_Multi_key, GDK_macron, GDK_o, 0x1E51, GDK_space, 0x0027, GDK_V, 0x01D7, GDK_v, 0x01D8, +GDK_nobreakspace, 0x0301, GDK_Abreve, 0x1EAE, GDK_abreve, 0x1EAF, GDK_Emacron, 0x1E16, @@ -510,6 +512,7 @@ GDK_space, 0x007E, GDK_less, 0x2272, GDK_equal, 0x2243, GDK_greater, 0x2273, +GDK_nobreakspace, 0x0303, GDK_Oacute, 0x1E4C, GDK_Odiaeresis, 0x1E4E, GDK_Uacute, 0x1E78, @@ -769,6 +772,16 @@ GDK_parenleft, 0x208D, GDK_parenright, 0x208E, GDK_plus, 0x208A, GDK_minus, 0x208B, +GDK_0, 0x2080, +GDK_1, 0x2081, +GDK_2, 0x2082, +GDK_3, 0x2083, +GDK_4, 0x2084, +GDK_5, 0x2085, +GDK_6, 0x2086, +GDK_7, 0x2087, +GDK_8, 0x2088, +GDK_9, 0x2088, GDK_equal, 0x208C, GDK_V, 0x01D9, GDK_v, 0x01DA, @@ -1103,11 +1116,14 @@ GDK_kana_HI, 0x30D4, GDK_kana_FU, 0x30D7, GDK_kana_HE, 0x30DA, GDK_kana_HO, 0x30DD, +GDK_space, 0x0323, GDK_plus, 0x2A25, GDK_minus, 0x2A2A, GDK_equal, 0x2A66, +GDK_nobreakspace, 0x0323, GDK_Abreve, 0x1EB6, GDK_abreve, 0x1EB7, +GDK_dead_belowdot, 0x0323, GDK_Multi_key, GDK_plus, GDK_O, 0x1EE2, GDK_Multi_key, GDK_plus, GDK_U, 0x1EF0, GDK_Multi_key, GDK_plus, GDK_o, 0x1EE3, @@ -1165,6 +1181,11 @@ GDK_Multi_key, GDK_asciicircum, GDK_e, 0x1EC3, GDK_Multi_key, GDK_asciicircum, GDK_o, 0x1ED5, GDK_Multi_key, GDK_b, GDK_A, 0x1EB2, GDK_Multi_key, GDK_b, GDK_a, 0x1EB3, +GDK_space, 0x031B, +GDK_nobreakspace, 0x031B, +GDK_Utilde, 0x1EEE, +GDK_utilde, 0x1EEF, +GDK_dead_horn, 0x031B, GDK_Greek_ALPHA, 0x1F08, GDK_Greek_EPSILON, 0x1F18, GDK_Greek_ETA, 0x1F28, @@ -1368,7 +1389,7 @@ GDK_apostrophe, GDK_Idiaeresis, 0x1E2E, GDK_apostrophe, GDK_Ocircumflex, 0x1ED0, GDK_apostrophe, GDK_Otilde, 0x1E4C, GDK_apostrophe, GDK_Ooblique, 0x01FE, -GDK_apostrophe, 0x00DC, 0x01D7, +GDK_apostrophe, GDK_Udiaeresis, 0x01D7, GDK_apostrophe, GDK_acircumflex, 0x1EA5, GDK_apostrophe, GDK_aring, 0x01FB, GDK_apostrophe, GDK_ae, 0x01FD, @@ -1529,6 +1550,7 @@ GDK_minus, GDK_parenright, 0x007D, GDK_minus, GDK_plus, 0x00B1, GDK_minus, GDK_comma, 0x00AC, GDK_minus, GDK_colon, 0x00F7, +GDK_minus, GDK_greater, 0x2192, GDK_minus, GDK_A, 0x00C3, GDK_minus, GDK_D, 0x0110, GDK_minus, GDK_E, 0x0112, @@ -1603,6 +1625,7 @@ GDK_period, 0x1E62, 0x1E68, GDK_period, 0x1E63, 0x1E69, GDK_slash, GDK_slash, 0x005C, GDK_slash, GDK_less, 0x005C, +GDK_slash, GDK_equal, 0x2260, GDK_slash, GDK_C, 0x00A2, GDK_slash, GDK_D, 0x0110, GDK_slash, GDK_G, 0x01E4, @@ -1682,8 +1705,11 @@ GDK_semicolon, GDK_u, 0x0173, GDK_less, GDK_space, 0x02C7, GDK_less, GDK_quotedbl, 0x201C, GDK_less, GDK_apostrophe, 0x2018, +GDK_less, GDK_minus, 0x2190, GDK_less, GDK_slash, 0x005C, +GDK_less, GDK_3, 0x2665, GDK_less, GDK_less, 0x00AB, +GDK_less, GDK_equal, 0x2264, GDK_less, GDK_C, 0x010C, GDK_less, GDK_D, 0x010E, GDK_less, GDK_E, 0x011A, @@ -1703,6 +1729,7 @@ GDK_less, GDK_s, 0x0161, GDK_less, GDK_t, 0x0165, GDK_less, GDK_z, 0x017E, GDK_less, 0x0338, 0x226E, +GDK_equal, GDK_slash, 0x2260, GDK_equal, GDK_C, 0x20AC, GDK_equal, GDK_E, 0x20AC, GDK_equal, GDK_L, 0x00A3, @@ -1725,6 +1752,7 @@ GDK_equal, GDK_Cyrillic_U, 0x04F2, GDK_greater, GDK_space, 0x005E, GDK_greater, GDK_quotedbl, 0x201D, GDK_greater, GDK_apostrophe, 0x2019, +GDK_greater, GDK_equal, 0x2265, GDK_greater, GDK_greater, 0x00BB, GDK_greater, GDK_A, 0x00C2, GDK_greater, GDK_E, 0x00CA, @@ -2068,7 +2096,7 @@ GDK_underscore, GDK_Adiaeresis, 0x01DE, GDK_underscore, GDK_AE, 0x01E2, GDK_underscore, GDK_Otilde, 0x022C, GDK_underscore, GDK_Odiaeresis, 0x022A, -GDK_underscore, 0x00DC, 0x01D5, +GDK_underscore, GDK_Udiaeresis, 0x01D5, GDK_underscore, GDK_adiaeresis, 0x01DF, GDK_underscore, GDK_ae, 0x01E3, GDK_underscore, GDK_otilde, 0x022D, @@ -2128,7 +2156,7 @@ GDK_grave, GDK_y, 0x1EF3, GDK_grave, GDK_Acircumflex, 0x1EA6, GDK_grave, GDK_Ecircumflex, 0x1EC0, GDK_grave, GDK_Ocircumflex, 0x1ED2, -GDK_grave, 0x00DC, 0x01DB, +GDK_grave, GDK_Udiaeresis, 0x01DB, GDK_grave, GDK_acircumflex, 0x1EA7, GDK_grave, GDK_ecircumflex, 0x1EC1, GDK_grave, GDK_ocircumflex, 0x1ED3, @@ -2279,7 +2307,7 @@ GDK_c, GDK_t, 0x0165, GDK_c, GDK_u, 0x01D4, GDK_c, GDK_z, 0x017E, GDK_c, GDK_bar, 0x00A2, -GDK_c, 0x00DC, 0x01D9, +GDK_c, GDK_Udiaeresis, 0x01D9, GDK_c, GDK_udiaeresis, 0x01DA, GDK_c, 0x01B7, 0x01EE, GDK_c, 0x0292, 0x01EF, @@ -2517,7 +2545,7 @@ GDK_macron, GDK_Adiaeresis, 0x01DE, GDK_macron, GDK_AE, 0x01E2, GDK_macron, GDK_Otilde, 0x022C, GDK_macron, GDK_Odiaeresis, 0x022A, -GDK_macron, 0x00DC, 0x01D5, +GDK_macron, GDK_Udiaeresis, 0x01D5, GDK_macron, GDK_adiaeresis, 0x01DF, GDK_macron, GDK_ae, 0x01E3, GDK_macron, GDK_otilde, 0x022D, @@ -2586,7 +2614,7 @@ GDK_acute, GDK_Idiaeresis, 0x1E2E, GDK_acute, GDK_Ocircumflex, 0x1ED0, GDK_acute, GDK_Otilde, 0x1E4C, GDK_acute, GDK_Ooblique, 0x01FE, -GDK_acute, 0x00DC, 0x01D7, +GDK_acute, GDK_Udiaeresis, 0x01D7, GDK_acute, GDK_acircumflex, 0x1EA5, GDK_acute, GDK_aring, 0x01FB, GDK_acute, GDK_ae, 0x01FD, @@ -4320,6 +4348,7 @@ GDK_parenleft, GDK_KP_4, GDK_KP_7, GDK_parenright, 0x32BC, GDK_parenleft, GDK_KP_4, GDK_KP_8, GDK_parenright, 0x32BD, GDK_parenleft, GDK_KP_4, GDK_KP_9, GDK_parenright, 0x32BE, GDK_parenleft, GDK_KP_5, GDK_KP_0, GDK_parenright, 0x32BF, +GDK_C, GDK_C, GDK_C, GDK_P, 0x262D, GDK_Greek_iota, GDK_apostrophe, GDK_parenleft, GDK_Greek_ALPHA, 0x1F8D, GDK_Greek_iota, GDK_apostrophe, GDK_parenleft, GDK_Greek_ETA, 0x1F9D, GDK_Greek_iota, GDK_apostrophe, GDK_parenleft, GDK_Greek_OMEGA, 0x1FAD, diff --git a/gtk/gtkimmodule.c b/gtk/gtkimmodule.c index 58ed584116..49eb2abc00 100644 --- a/gtk/gtkimmodule.c +++ b/gtk/gtkimmodule.c @@ -685,17 +685,13 @@ _gtk_im_module_get_default_context_id (GdkWindow *client_window) if (GDK_IS_DRAWABLE (client_window)) { screen = gdk_drawable_get_screen (GDK_DRAWABLE (client_window)); - if (screen) - settings = gtk_settings_get_for_screen (screen); - else - settings = gtk_settings_get_default (); - + settings = gtk_settings_get_for_screen (screen); g_object_get (G_OBJECT (settings), "gtk-im-module", &tmp, NULL); if (tmp) { if (strcmp (tmp, SIMPLE_ID) == 0) context_id = SIMPLE_ID; - else + else { GtkIMModule *module; module = g_hash_table_lookup (contexts_hash, tmp); @@ -704,7 +700,7 @@ _gtk_im_module_get_default_context_id (GdkWindow *client_window) } g_free (tmp); - if (context_id) + if (context_id) return context_id; } } diff --git a/gtk/gtkimmulticontext.c b/gtk/gtkimmulticontext.c index 034582053d..2ec8b32582 100644 --- a/gtk/gtkimmulticontext.c +++ b/gtk/gtkimmulticontext.c @@ -220,23 +220,28 @@ gtk_im_multicontext_set_slave (GtkIMMulticontext *multicontext, g_signal_emit_by_name (multicontext, "preedit-changed"); } +static const gchar * +get_effective_context_id (GtkIMMulticontext *multicontext) +{ + if (multicontext->priv->context_id) + return multicontext->priv->context_id; + + if (!global_context_id) + global_context_id = _gtk_im_module_get_default_context_id (multicontext->priv->client_window); + + return global_context_id; +} + static GtkIMContext * gtk_im_multicontext_get_slave (GtkIMMulticontext *multicontext) { if (!multicontext->slave) { GtkIMContext *slave; - + g_free (multicontext->context_id); - - if (multicontext->priv->context_id) - multicontext->context_id = g_strdup (multicontext->priv->context_id); - else - { - if (!global_context_id) - global_context_id = _gtk_im_module_get_default_context_id (multicontext->priv->client_window); - multicontext->context_id = g_strdup (global_context_id); - } + + multicontext->context_id = g_strdup (get_effective_context_id (multicontext)); slave = _gtk_im_module_create (multicontext->context_id); gtk_im_multicontext_set_slave (multicontext, slave, FALSE); g_object_unref (slave); @@ -264,28 +269,29 @@ gtk_im_multicontext_set_client_window (GtkIMContext *context, multicontext->priv->client_window = window; - gtk_im_multicontext_set_slave (multicontext, NULL, FALSE); - - if (window == NULL) - return; - - screen = gdk_drawable_get_screen (GDK_DRAWABLE (window)); - if (screen) - settings = gtk_settings_get_for_screen (screen); - else - settings = gtk_settings_get_default (); - - connected = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (settings), - "gtk-im-module-connected")); - if (!connected) + if (window) { - g_signal_connect (settings, "notify::gtk-im-module", - G_CALLBACK (im_module_setting_changed), NULL); - g_object_set_data (G_OBJECT (settings), "gtk-im-module-connected", - GINT_TO_POINTER (TRUE)); + screen = gdk_drawable_get_screen (GDK_DRAWABLE (window)); + settings = gtk_settings_get_for_screen (screen); - global_context_id = NULL; + connected = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (settings), + "gtk-im-module-connected")); + if (!connected) + { + g_signal_connect (settings, "notify::gtk-im-module", + G_CALLBACK (im_module_setting_changed), NULL); + g_object_set_data (G_OBJECT (settings), "gtk-im-module-connected", + GINT_TO_POINTER (TRUE)); + + global_context_id = NULL; + } } + + if (g_strcmp0 (multicontext->context_id, get_effective_context_id (multicontext)) != 0) + gtk_im_multicontext_set_slave (multicontext, NULL, FALSE); + + if (multicontext->slave) + gtk_im_context_set_client_window (multicontext->slave, window); } static void @@ -327,16 +333,7 @@ gtk_im_multicontext_focus_in (GtkIMContext *context) GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context); GtkIMContext *slave; - /* If the global context type is different from the context we were - * using before, get rid of the old slave and create a new one - * for the new global context type. - */ - if (multicontext->context_id == NULL || - (multicontext->priv->context_id != NULL && - strcmp (multicontext->priv->context_id, multicontext->context_id) != 0) || - (multicontext->priv->context_id == NULL && - (global_context_id == NULL || - strcmp (global_context_id, multicontext->context_id) != 0))) + if (g_strcmp0 (multicontext->context_id, get_effective_context_id (multicontext)) != 0) gtk_im_multicontext_set_slave (multicontext, NULL, FALSE); slave = gtk_im_multicontext_get_slave (multicontext); diff --git a/gtk/gtkinfobar.c b/gtk/gtkinfobar.c index 9ada2c93a3..be1e875269 100644 --- a/gtk/gtkinfobar.c +++ b/gtk/gtkinfobar.c @@ -569,6 +569,11 @@ gtk_info_bar_update_colors (GtkInfoBar *info_bar) fg = &other_default_border_color; bg = &other_default_fill_color; break; + + default: + g_assert_not_reached(); + fg = NULL; + bg = NULL; } } diff --git a/gtk/gtkinputdialog.h b/gtk/gtkinputdialog.h index 4c1647b992..6e95356bea 100644 --- a/gtk/gtkinputdialog.h +++ b/gtk/gtkinputdialog.h @@ -34,9 +34,7 @@ * distribution. */ -#if defined(GTK_DISABLE_SINGLE_INCLUDES) && !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) -#error "Only <gtk/gtk.h> can be included directly." -#endif +#ifndef GTK_DISABLE_DEPRECATED #ifndef __GTK_INPUTDIALOG_H__ #define __GTK_INPUTDIALOG_H__ @@ -92,11 +90,11 @@ struct _GtkInputDialogClass void (*_gtk_reserved4) (void); }; - GType gtk_input_dialog_get_type (void) G_GNUC_CONST; GtkWidget* gtk_input_dialog_new (void); - G_END_DECLS #endif /* __GTK_INPUTDIALOG_H__ */ + +#endif /* GTK_DISABLE_DEPRECATED */ diff --git a/gtk/gtkkeyhash.c b/gtk/gtkkeyhash.c index 3de8830ebe..298f94c5d6 100644 --- a/gtk/gtkkeyhash.c +++ b/gtk/gtkkeyhash.c @@ -420,8 +420,8 @@ _gtk_key_hash_lookup (GtkKeyHash *key_hash, xmods = GDK_MOD2_MASK|GDK_MOD3_MASK|GDK_MOD4_MASK|GDK_MOD5_MASK; vmods = GDK_SUPER_MASK|GDK_HYPER_MASK|GDK_META_MASK; - if ((entry->modifiers & ~consumed_modifiers & mask & ~vmods) == (state & ~consumed_modifiers & mask & ~vmods) || - (entry->modifiers & ~consumed_modifiers & mask & ~xmods) == (state & ~consumed_modifiers & mask & ~xmods)) + if ((entry->modifiers & ~consumed_modifiers & mask) == (state & ~consumed_modifiers & mask & ~vmods) || + (entry->modifiers & ~consumed_modifiers & mask) == (state & ~consumed_modifiers & mask & ~xmods)) { gint i; @@ -430,7 +430,7 @@ _gtk_key_hash_lookup (GtkKeyHash *key_hash, GTK_NOTE (KEYBINDINGS, g_message (" found exact match, keyval = %u, modifiers = 0x%04x", entry->keyval, entry->modifiers)); - + if (!have_exact) { g_slist_free (results); diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c index 4577eec3b1..acf7f53bfa 100644 --- a/gtk/gtklabel.c +++ b/gtk/gtklabel.c @@ -3841,6 +3841,7 @@ get_layout_index (GtkLabel *label, gint trailing = 0; const gchar *cluster; const gchar *cluster_end; + gboolean inside; *index = 0; @@ -3851,24 +3852,21 @@ get_layout_index (GtkLabel *label, x *= PANGO_SCALE; y *= PANGO_SCALE; - if (pango_layout_xy_to_index (label->layout, - x, y, - index, &trailing)) - { - cluster = label->text + *index; - cluster_end = cluster; - while (trailing) - { - cluster_end = g_utf8_next_char (cluster_end); - --trailing; - } - - *index += (cluster_end - cluster); + inside = pango_layout_xy_to_index (label->layout, + x, y, + index, &trailing); - return TRUE; + cluster = label->text + *index; + cluster_end = cluster; + while (trailing) + { + cluster_end = g_utf8_next_char (cluster_end); + --trailing; } - return FALSE; + *index += (cluster_end - cluster); + + return inside; } static void diff --git a/gtk/gtkliststore.c b/gtk/gtkliststore.c index 470c9f82b1..8ab1f18fbf 100644 --- a/gtk/gtkliststore.c +++ b/gtk/gtkliststore.c @@ -337,7 +337,6 @@ static void gtk_list_store_set_n_columns (GtkListStore *list_store, gint n_columns) { - GType *new_columns; int i; if (list_store->n_columns == n_columns) @@ -494,10 +493,16 @@ static gboolean gtk_list_store_iter_next (GtkTreeModel *tree_model, GtkTreeIter *iter) { + gboolean retval; + g_return_val_if_fail (GTK_LIST_STORE (tree_model)->stamp == iter->stamp, FALSE); iter->user_data = g_sequence_iter_next (iter->user_data); - return !g_sequence_iter_is_end (iter->user_data); + retval = g_sequence_iter_is_end (iter->user_data); + if (retval) + iter->stamp = 0; + + return !retval; } static gboolean @@ -509,7 +514,10 @@ gtk_list_store_iter_children (GtkTreeModel *tree_model, /* this is a list, nodes have no children */ if (parent) - return FALSE; + { + iter->stamp = 0; + return FALSE; + } if (g_sequence_get_length (list_store->seq) > 0) { @@ -518,7 +526,10 @@ gtk_list_store_iter_children (GtkTreeModel *tree_model, return TRUE; } else - return FALSE; + { + iter->stamp = 0; + return FALSE; + } } static gboolean @@ -551,6 +562,8 @@ gtk_list_store_iter_nth_child (GtkTreeModel *tree_model, GtkListStore *list_store = (GtkListStore *) tree_model; GSequenceIter *child; + iter->stamp = 0; + if (parent) return FALSE; @@ -570,6 +583,7 @@ gtk_list_store_iter_parent (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child) { + iter->stamp = 0; return FALSE; } diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c index 11aee08fbc..bfee6da93b 100644 --- a/gtk/gtkmain.c +++ b/gtk/gtkmain.c @@ -686,6 +686,10 @@ do_post_parse_initialization (int *argc, gettext_initialization (); +#ifdef SIGPIPE + signal (SIGPIPE, SIG_IGN); +#endif + if (g_fatal_warnings) { GLogLevelFlags fatal_mask; @@ -845,9 +849,9 @@ gtk_get_option_group (gboolean open_default_display) gboolean gtk_init_with_args (int *argc, char ***argv, - char *parameter_string, + const char *parameter_string, GOptionEntry *entries, - char *translation_domain, + const char *translation_domain, GError **error) { GOptionContext *context; @@ -985,6 +989,15 @@ gtk_init_check (int *argc, * the GUI for some reason. If you want your program to fall back to a * textual interface you want to call gtk_init_check() instead. * </para></note> + * + * <note><para> + * Since 2.18, GTK+ calls <literal>signal (SIGPIPE, SIG_IGN)</literal> + * during initialization, to ignore SIGPIPE signals, since these are + * almost never wanted in graphical applications. If you do need to + * handle SIGPIPE for some reason, reset the handler after gtk_init(), + * but notice that other libraries (e.g. libdbus or gvfs) might do + * similar things. + * </para></note> **/ void gtk_init (int *argc, char ***argv) @@ -1559,7 +1572,15 @@ gtk_main_do_event (GdkEvent *event) gdk_window_end_paint (event->any.window); } else - gtk_widget_send_expose (event_widget, event); + { + /* The app may paint with a previously allocated cairo_t, + which will draw directly to the window. We can't catch cairo + drap operatoins to automatically flush the window, thus we + need to explicitly flush any outstanding moves or double + buffering */ + gdk_window_flush (event->any.window); + gtk_widget_send_expose (event_widget, event); + } break; case GDK_PROPERTY_NOTIFY: diff --git a/gtk/gtkmain.h b/gtk/gtkmain.h index 975057e864..21de92ed42 100644 --- a/gtk/gtkmain.h +++ b/gtk/gtkmain.h @@ -99,9 +99,9 @@ gboolean gtk_init_check (int *argc, gboolean gtk_init_with_args (int *argc, char ***argv, - char *parameter_string, + const char *parameter_string, GOptionEntry *entries, - char *translation_domain, + const char *translation_domain, GError **error); GOptionGroup *gtk_get_option_group (gboolean open_default_display); diff --git a/gtk/gtkmarshalers.list b/gtk/gtkmarshalers.list index 533a266ced..77873cb022 100644 --- a/gtk/gtkmarshalers.list +++ b/gtk/gtkmarshalers.list @@ -111,3 +111,4 @@ VOID:UINT,STRING,UINT VOID:UINT,UINT VOID:VOID OBJECT:OBJECT,INT,INT +VOID:POINTER,POINTER,POINTER,POINTER,STRING diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c index 0fd5d3d4ad..0bdf1dea76 100644 --- a/gtk/gtkmenu.c +++ b/gtk/gtkmenu.c @@ -2730,6 +2730,11 @@ get_arrows_visible_area (GtkMenu *menu, lower->width = (border->width - 2 * border->x) / 2; lower->height = scroll_arrow_height; break; + + default: + g_assert_not_reached(); + upper->x = upper->y = upper->width = upper->height = 0; + lower->x = lower->y = lower->width = lower->height = 0; } *arrow_space = scroll_arrow_height - 2 * widget->style->ythickness; @@ -4172,7 +4177,6 @@ gtk_menu_position (GtkMenu *menu) GtkRequisition requisition; GtkMenuPrivate *private; gint x, y; - gboolean initially_pushed_in; gint scroll_offset; gint menu_height; GdkScreen *screen; diff --git a/gtk/gtkmountoperation-x11.c b/gtk/gtkmountoperation-x11.c index d4efdaf84c..a20c8cf428 100644 --- a/gtk/gtkmountoperation-x11.c +++ b/gtk/gtkmountoperation-x11.c @@ -665,7 +665,7 @@ pid_get_env (GPid pid, ret = g_strdup (env + n + key_len + 1); /* skip invalid UTF-8 */ - if (!g_utf8_validate (ret, -1, (const gchar *) &end)) + if (!g_utf8_validate (ret, -1, (const gchar **) &end)) *end = '\0'; break; } @@ -707,7 +707,7 @@ pid_get_command_line (GPid pid) } /* skip invalid UTF-8 */ - if (!g_utf8_validate (cmdline_contents, -1, (const gchar *) &end)) + if (!g_utf8_validate (cmdline_contents, -1, (const gchar **) &end)) *end = '\0'; out: @@ -861,6 +861,7 @@ get_pixbuf_for_window_with_pid (GtkMountOperationLookupContext *context, static const gchar *well_known_commands[] = { + /* translators: this string is a name for the 'less' command */ "less", N_("Terminal Pager"), "top", N_("Top Command"), "bash", N_("Bourne Again Shell"), @@ -951,6 +952,8 @@ _gtk_mount_operation_kill_process (GPid pid, if (kill ((pid_t) pid, SIGTERM) != 0) { + int errsv = errno; + /* TODO: On EPERM, we could use a setuid helper using polkit (very easy to implement * via pkexec(1)) to allow the user to e.g. authenticate to gain the authorization * to kill the process. But that's not how things currently work. @@ -959,10 +962,10 @@ _gtk_mount_operation_kill_process (GPid pid, ret = FALSE; g_set_error (error, G_IO_ERROR, - g_io_error_from_errno (errno), + g_io_error_from_errno (errsv), _("Cannot end process with pid %d: %s"), pid, - strerror (errno)); + g_strerror (errsv)); } return ret; diff --git a/gtk/gtkmountoperation.c b/gtk/gtkmountoperation.c index 6137388c0b..4a1c9f8a8b 100644 --- a/gtk/gtkmountoperation.c +++ b/gtk/gtkmountoperation.c @@ -981,21 +981,22 @@ update_process_list_store (GtkMountOperation *mount_operation, diff_sorted_arrays (current_pids, processes, pid_equal, pid_indices_to_add, pid_indices_to_remove); - if (pid_indices_to_add->len > 0) - lookup_context = _gtk_mount_operation_lookup_context_get (gtk_widget_get_display (mount_operation->priv->process_tree_view)); - for (n = 0; n < pid_indices_to_add->len; n++) - { - pid = g_array_index (processes, GPid, n); - add_pid_to_process_list_store (mount_operation, lookup_context, list_store, pid); - } - for (n = 0; n < pid_indices_to_remove->len; n++) { pid = g_array_index (current_pids, GPid, n); remove_pid_from_process_list_store (mount_operation, list_store, pid); } + if (pid_indices_to_add->len > 0) - _gtk_mount_operation_lookup_context_free (lookup_context); + { + lookup_context = _gtk_mount_operation_lookup_context_get (gtk_widget_get_display (mount_operation->priv->process_tree_view)); + for (n = 0; n < pid_indices_to_add->len; n++) + { + pid = g_array_index (processes, GPid, n); + add_pid_to_process_list_store (mount_operation, lookup_context, list_store, pid); + } + _gtk_mount_operation_lookup_context_free (lookup_context); + } /* select the first item, if we went from a zero to a non-zero amount of processes */ if (current_pids->len == 0 && pid_indices_to_add->len > 0) diff --git a/gtk/gtknotebook.c b/gtk/gtknotebook.c index aa1a5ade06..e42e2fa7dc 100644 --- a/gtk/gtknotebook.c +++ b/gtk/gtknotebook.c @@ -3094,8 +3094,6 @@ static gint gtk_notebook_focus_in (GtkWidget *widget, GdkEventFocus *event) { - GTK_NOTEBOOK (widget)->child_has_focus = FALSE; - gtk_notebook_redraw_tabs (GTK_NOTEBOOK (widget)); return FALSE; @@ -3978,6 +3976,8 @@ gtk_notebook_set_focus_child (GtkContainer *container, } } } + else + notebook->child_has_focus = FALSE; GTK_CONTAINER_CLASS (gtk_notebook_parent_class)->set_focus_child (container, child); } @@ -5799,9 +5799,14 @@ gtk_notebook_real_switch_page (GtkNotebook *notebook, GtkNotebookPage *page, guint page_num) { + gboolean child_has_focus; + if (notebook->cur_page == page || !GTK_WIDGET_VISIBLE (page->child)) return; + /* save the value here, changing visibility changes focus */ + child_has_focus = notebook->child_has_focus; + if (notebook->cur_page) gtk_widget_set_child_visible (notebook->cur_page->child, FALSE); @@ -5818,7 +5823,7 @@ gtk_notebook_real_switch_page (GtkNotebook *notebook, * element on the new page, if possible, or if not, to the * notebook itself. */ - if (notebook->child_has_focus) + if (child_has_focus) { if (notebook->cur_page->last_focus_child && gtk_widget_is_ancestor (notebook->cur_page->last_focus_child, notebook->cur_page->child)) @@ -7193,6 +7198,8 @@ gtk_notebook_child_reordered (GtkNotebook *notebook, * Sets the packing parameters for the tab label of the page * containing @child. See gtk_box_pack_start() for the exact meaning * of the parameters. + * + * Deprecated: 2.20: Modify the "expand" and "fill" child properties instead. **/ void gtk_notebook_set_tab_label_packing (GtkNotebook *notebook, @@ -7244,6 +7251,8 @@ gtk_notebook_set_tab_label_packing (GtkNotebook *notebook, * * Query the packing attributes for the tab label of the page * containing @child. + * + * Deprecated: 2.20: Modify the "expand" and "fill" child properties instead. **/ void gtk_notebook_query_tab_label_packing (GtkNotebook *notebook, diff --git a/gtk/gtknotebook.h b/gtk/gtknotebook.h index 0659cc92d2..41bbe66979 100644 --- a/gtk/gtknotebook.h +++ b/gtk/gtknotebook.h @@ -261,6 +261,7 @@ void gtk_notebook_set_menu_label_text (GtkNotebook *notebook, const gchar *menu_text); G_CONST_RETURN gchar *gtk_notebook_get_menu_label_text (GtkNotebook *notebook, GtkWidget *child); +#ifndef GTK_DISABLE_DEPRECATED void gtk_notebook_query_tab_label_packing (GtkNotebook *notebook, GtkWidget *child, gboolean *expand, @@ -271,6 +272,7 @@ void gtk_notebook_set_tab_label_packing (GtkNotebook *notebook, gboolean expand, gboolean fill, GtkPackType pack_type); +#endif void gtk_notebook_reorder_child (GtkNotebook *notebook, GtkWidget *child, gint position); diff --git a/gtk/gtkpagesetupunixdialog.c b/gtk/gtkpagesetupunixdialog.c index 0fae189579..1e903391d9 100644 --- a/gtk/gtkpagesetupunixdialog.c +++ b/gtk/gtkpagesetupunixdialog.c @@ -146,7 +146,7 @@ gtk_page_setup_unix_dialog_class_init (GtkPageSetupUnixDialogClass *class) object_class->finalize = gtk_page_setup_unix_dialog_finalize; - g_type_class_add_private (class, sizeof (GtkPageSetupUnixDialogPrivate)); + g_type_class_add_private (class, sizeof (GtkPageSetupUnixDialogPrivate)); } static void @@ -154,21 +154,24 @@ gtk_page_setup_unix_dialog_init (GtkPageSetupUnixDialog *dialog) { GtkPageSetupUnixDialogPrivate *priv; GtkTreeIter iter; + gchar *tmp; priv = dialog->priv = GTK_PAGE_SETUP_UNIX_DIALOG_GET_PRIVATE (dialog); priv->print_backends = NULL; priv->printer_list = gtk_list_store_new (PRINTER_LIST_N_COLS, - G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_OBJECT); gtk_list_store_append (priv->printer_list, &iter); + tmp = g_strdup_printf ("<b>%s</b>\n%s", _("Any Printer"), _("For portable documents")); gtk_list_store_set (priv->printer_list, &iter, - PRINTER_LIST_COL_NAME, _("<b>Any Printer</b>\nFor portable documents"), + PRINTER_LIST_COL_NAME, tmp, PRINTER_LIST_COL_PRINTER, NULL, -1); - + g_free (tmp); + priv->page_setup_list = gtk_list_store_new (PAGE_SETUP_LIST_N_COLS, G_TYPE_OBJECT, G_TYPE_BOOLEAN); @@ -260,7 +263,7 @@ printer_added_cb (GtkPrintBackend *backend, GtkPageSetupUnixDialogPrivate *priv = dialog->priv; GtkTreeIter iter; gchar *str; - const gchar *location;; + const gchar *location; if (gtk_printer_is_virtual (printer)) return; @@ -271,18 +274,17 @@ printer_added_cb (GtkPrintBackend *backend, str = g_strdup_printf ("<b>%s</b>\n%s", gtk_printer_get_name (printer), location); - + gtk_list_store_append (priv->printer_list, &iter); gtk_list_store_set (priv->printer_list, &iter, PRINTER_LIST_COL_NAME, str, PRINTER_LIST_COL_PRINTER, printer, -1); - g_object_set_data_full (G_OBJECT (printer), - "gtk-print-tree-iter", + g_object_set_data_full (G_OBJECT (printer), + "gtk-print-tree-iter", gtk_tree_iter_copy (&iter), (GDestroyNotify) gtk_tree_iter_free); - g_free (str); if (priv->waiting_for_printer != NULL && @@ -316,7 +318,7 @@ printer_status_cb (GtkPrintBackend *backend, GtkPageSetupUnixDialogPrivate *priv = dialog->priv; GtkTreeIter *iter; gchar *str; - const gchar *location;; + const gchar *location; iter = g_object_get_data (G_OBJECT (printer), "gtk-print-tree-iter"); diff --git a/gtk/gtkprintbackend.c b/gtk/gtkprintbackend.c index 567273bdd7..3c64823ebe 100644 --- a/gtk/gtkprintbackend.c +++ b/gtk/gtkprintbackend.c @@ -50,9 +50,8 @@ struct _GtkPrintBackendPrivate guint printer_list_requested : 1; guint printer_list_done : 1; GtkPrintBackendStatus status; - char *hostname; - char *username; - char *password; + char **auth_info_required; + char **auth_info; }; enum { @@ -359,8 +358,10 @@ static GList * fallback_printer_list_papers (GtkPrinter static GtkPageSetup * fallback_printer_get_default_page_size (GtkPrinter *printer); static GtkPrintCapabilities fallback_printer_get_capabilities (GtkPrinter *printer); static void request_password (GtkPrintBackend *backend, - const gchar *hostname, - const gchar *username, + gpointer auth_info_required, + gpointer auth_info_default, + gpointer auth_info_display, + gpointer auth_info_visible, const gchar *prompt); static void @@ -441,8 +442,8 @@ gtk_print_backend_class_init (GtkPrintBackendClass *class) G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GtkPrintBackendClass, request_password), NULL, NULL, - _gtk_marshal_VOID__STRING_STRING_STRING, - G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); + _gtk_marshal_VOID__POINTER_POINTER_POINTER_POINTER_STRING, + G_TYPE_NONE, 5, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_STRING); } static void @@ -455,9 +456,8 @@ gtk_print_backend_init (GtkPrintBackend *backend) priv->printers = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) g_object_unref); - priv->hostname = NULL; - priv->username = NULL; - priv->password = NULL; + priv->auth_info_required = NULL; + priv->auth_info = NULL; } static void @@ -662,40 +662,29 @@ gtk_print_backend_print_stream (GtkPrintBackend *backend, } void -gtk_print_backend_set_password (GtkPrintBackend *backend, - const gchar *hostname, - const gchar *username, - const gchar *password) +gtk_print_backend_set_password (GtkPrintBackend *backend, + gchar **auth_info_required, + gchar **auth_info) { g_return_if_fail (GTK_IS_PRINT_BACKEND (backend)); if (GTK_PRINT_BACKEND_GET_CLASS (backend)->set_password) - GTK_PRINT_BACKEND_GET_CLASS (backend)->set_password (backend, hostname, username, password); + GTK_PRINT_BACKEND_GET_CLASS (backend)->set_password (backend, auth_info_required, auth_info); } static void -store_password (GtkEntry *entry, - GtkPrintBackend *backend) +store_entry (GtkEntry *entry, + gpointer user_data) { - GtkPrintBackendPrivate *priv = backend->priv; + gchar **data = (gchar **) user_data; - if (priv->password != NULL) + if (*data != NULL) { - memset (priv->password, 0, strlen (priv->password)); - g_free (priv->password); + memset (*data, 0, strlen (*data)); + g_free (*data); } - priv->password = g_strdup (gtk_entry_get_text (entry)); -} - -static void -store_username (GtkEntry *entry, - GtkPrintBackend *backend) -{ - GtkPrintBackendPrivate *priv = backend->priv; - - g_free (priv->username); - priv->username = g_strdup (gtk_entry_get_text (entry)); + *data = g_strdup (gtk_entry_get_text (entry)); } static void @@ -704,21 +693,24 @@ password_dialog_response (GtkWidget *dialog, GtkPrintBackend *backend) { GtkPrintBackendPrivate *priv = backend->priv; + gint i; if (response_id == GTK_RESPONSE_OK) - gtk_print_backend_set_password (backend, priv->hostname, priv->username, priv->password); + gtk_print_backend_set_password (backend, priv->auth_info_required, priv->auth_info); else - gtk_print_backend_set_password (backend, priv->hostname, priv->username, NULL); + gtk_print_backend_set_password (backend, priv->auth_info_required, NULL); - if (priv->password != NULL) - { - memset (priv->password, 0, strlen (priv->password)); - g_free (priv->password); - priv->password = NULL; - } + for (i = 0; i < g_strv_length (priv->auth_info_required); i++) + if (priv->auth_info[i] != NULL) + { + memset (priv->auth_info[i], 0, strlen (priv->auth_info[i])); + g_free (priv->auth_info[i]); + priv->auth_info[i] = NULL; + } + g_free (priv->auth_info); + priv->auth_info = NULL; - g_free (priv->username); - priv->username = NULL; + g_strfreev (priv->auth_info_required); gtk_widget_destroy (dialog); @@ -726,16 +718,27 @@ password_dialog_response (GtkWidget *dialog, } static void -request_password (GtkPrintBackend *backend, - const gchar *hostname, - const gchar *username, - const gchar *prompt) +request_password (GtkPrintBackend *backend, + gpointer auth_info_required, + gpointer auth_info_default, + gpointer auth_info_display, + gpointer auth_info_visible, + const gchar *prompt) { GtkPrintBackendPrivate *priv = backend->priv; - GtkWidget *dialog, *username_box, *password_box, *main_box, *label, *icon, *vbox, - *password_prompt, *username_prompt, - *password_entry, *username_entry; + GtkWidget *dialog, *box, *main_box, *label, *icon, *vbox, *entry; + GtkWidget *focus = NULL; gchar *markup; + gint length; + gint i; + gchar **ai_required = (gchar **) auth_info_required; + gchar **ai_default = (gchar **) auth_info_default; + gchar **ai_display = (gchar **) auth_info_display; + gboolean *ai_visible = (gboolean *) auth_info_visible; + + priv->auth_info_required = g_strdupv (ai_required); + length = g_strv_length (ai_required); + priv->auth_info = g_new0 (gchar *, length); dialog = gtk_dialog_new_with_buttons ( _("Authentication"), NULL, GTK_DIALOG_MODAL, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, @@ -766,27 +769,6 @@ request_password (GtkPrintBackend *backend, g_free (markup); - /* Right - 2. */ - username_box = gtk_hbox_new (TRUE, 0); - - username_prompt = gtk_label_new (_("Username:")); - gtk_misc_set_alignment (GTK_MISC (username_prompt), 0.0, 0.5); - - username_entry = gtk_entry_new (); - gtk_entry_set_text (GTK_ENTRY (username_entry), username); - - - /* Right - 3. */ - password_box = gtk_hbox_new (TRUE, 0); - - password_prompt = gtk_label_new (_("Password:")); - gtk_misc_set_alignment (GTK_MISC (password_prompt), 0.0, 0.5); - - password_entry = gtk_entry_new (); - gtk_entry_set_visibility (GTK_ENTRY (password_entry), FALSE); - gtk_entry_set_activates_default (GTK_ENTRY (password_entry), TRUE); - - /* Packing */ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), main_box, TRUE, FALSE, 0); @@ -794,26 +776,42 @@ request_password (GtkPrintBackend *backend, gtk_box_pack_start (GTK_BOX (main_box), vbox, FALSE, FALSE, 6); gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, TRUE, 6); - gtk_box_pack_start (GTK_BOX (vbox), username_box, FALSE, TRUE, 6); - gtk_box_pack_start (GTK_BOX (vbox), password_box, FALSE, TRUE, 6); + + /* Right - 2. */ + for (i = 0; i < length; i++) + { + priv->auth_info[i] = g_strdup (ai_default[i]); + if (ai_display[i] != NULL) + { + box = gtk_hbox_new (TRUE, 0); + + label = gtk_label_new (ai_display[i]); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); - gtk_box_pack_start (GTK_BOX (username_box), username_prompt, TRUE, TRUE, 0); - gtk_box_pack_start (GTK_BOX (username_box), username_entry, TRUE, TRUE, 0); + entry = gtk_entry_new (); + focus = entry; - gtk_box_pack_start (GTK_BOX (password_box), password_prompt, TRUE, TRUE, 0); - gtk_box_pack_start (GTK_BOX (password_box), password_entry, TRUE, TRUE, 0); + if (ai_default[i] != NULL) + gtk_entry_set_text (GTK_ENTRY (entry), ai_default[i]); + gtk_entry_set_visibility (GTK_ENTRY (entry), ai_visible[i]); + gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE); - gtk_widget_grab_focus (password_entry); + gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, TRUE, 6); - priv->hostname = g_strdup (hostname); - priv->username = g_strdup (username); + gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0); + gtk_box_pack_start (GTK_BOX (box), entry, TRUE, TRUE, 0); - g_signal_connect (password_entry, "changed", - G_CALLBACK (store_password), backend); + g_signal_connect (entry, "changed", + G_CALLBACK (store_entry), &(priv->auth_info[i])); + } + } - g_signal_connect (username_entry, "changed", - G_CALLBACK (store_username), backend); + if (focus != NULL) + { + gtk_widget_grab_focus (focus); + focus = NULL; + } g_object_ref (backend); g_signal_connect (G_OBJECT (dialog), "response", diff --git a/gtk/gtkprintbackend.h b/gtk/gtkprintbackend.h index 7d75f8e417..c4b43b10d2 100644 --- a/gtk/gtkprintbackend.h +++ b/gtk/gtkprintbackend.h @@ -121,15 +121,16 @@ struct _GtkPrintBackendClass void (*printer_status_changed) (GtkPrintBackend *backend, GtkPrinter *printer); void (*request_password) (GtkPrintBackend *backend, - const gchar *hostname, - const gchar *username, + gpointer auth_info_required, + gpointer auth_info_default, + gpointer auth_info_display, + gpointer auth_info_visible, const gchar *prompt); /* not a signal */ void (*set_password) (GtkPrintBackend *backend, - const gchar *hostname, - const gchar *username, - const gchar *password); + gchar **auth_info_required, + gchar **auth_info); /* Padding for future expansion */ void (*_gtk_reserved1) (void); @@ -153,9 +154,8 @@ void gtk_print_backend_print_stream (GtkPrintBackend *pri GList * gtk_print_backend_load_modules (void); void gtk_print_backend_destroy (GtkPrintBackend *print_backend); void gtk_print_backend_set_password (GtkPrintBackend *backend, - const gchar *hostname, - const gchar *username, - const gchar *password); + gchar **auth_info_required, + gchar **auth_info); /* Backend-only functions for GtkPrintBackend */ diff --git a/gtk/gtkprintcontext.c b/gtk/gtkprintcontext.c index c280881f80..80eac79fa7 100644 --- a/gtk/gtkprintcontext.c +++ b/gtk/gtkprintcontext.c @@ -177,11 +177,11 @@ _gtk_print_context_rotate_according_to_orientation (GtkPrintContext *context) case GTK_PAGE_ORIENTATION_PORTRAIT: break; case GTK_PAGE_ORIENTATION_LANDSCAPE: - cairo_translate (cr, width, 0); + cairo_translate (cr, 0, height); cairo_matrix_init (&matrix, - 0, 1, - -1, 0, - 0, 0); + 0, -1, + 1, 0, + 0, 0); cairo_transform (cr, &matrix); break; case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT: @@ -193,11 +193,11 @@ _gtk_print_context_rotate_according_to_orientation (GtkPrintContext *context) cairo_transform (cr, &matrix); break; case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE: - cairo_translate (cr, 0, height); + cairo_translate (cr, width, 0); cairo_matrix_init (&matrix, - 0, -1, - 1, 0, - 0, 0); + 0, 1, + -1, 0, + 0, 0); cairo_transform (cr, &matrix); break; } diff --git a/gtk/gtkprinteroption.c b/gtk/gtkprinteroption.c index 983dc65d56..47fcd32755 100644 --- a/gtk/gtkprinteroption.c +++ b/gtk/gtkprinteroption.c @@ -63,6 +63,7 @@ static void gtk_printer_option_init (GtkPrinterOption *option) { option->value = g_strdup (""); + option->activates_default = FALSE; } static void @@ -215,6 +216,23 @@ gtk_printer_option_has_choice (GtkPrinterOption *option, return FALSE; } +void +gtk_printer_option_set_activates_default (GtkPrinterOption *option, + gboolean activates) +{ + g_return_if_fail (GTK_IS_PRINTER_OPTION (option)); + + option->activates_default = activates; +} + +gboolean +gtk_printer_option_get_activates_default (GtkPrinterOption *option) +{ + g_return_val_if_fail (GTK_IS_PRINTER_OPTION (option), FALSE); + + return option->activates_default; +} + #define __GTK_PRINTER_OPTION_C__ #include "gtkaliasdef.c" diff --git a/gtk/gtkprinteroption.h b/gtk/gtkprinteroption.h index cce6986960..f1831a31c2 100644 --- a/gtk/gtkprinteroption.h +++ b/gtk/gtkprinteroption.h @@ -70,6 +70,8 @@ struct _GtkPrinterOption char **choices; char **choices_display; + gboolean activates_default; + gboolean has_conflict; char *group; }; @@ -92,24 +94,27 @@ struct _GtkPrinterOptionClass GType gtk_printer_option_get_type (void) G_GNUC_CONST; -GtkPrinterOption *gtk_printer_option_new (const char *name, - const char *display_text, - GtkPrinterOptionType type); -void gtk_printer_option_set (GtkPrinterOption *option, - const char *value); -void gtk_printer_option_set_has_conflict (GtkPrinterOption *option, - gboolean has_conflict); -void gtk_printer_option_clear_has_conflict (GtkPrinterOption *option); -void gtk_printer_option_set_boolean (GtkPrinterOption *option, - gboolean value); -void gtk_printer_option_allocate_choices (GtkPrinterOption *option, - int num); -void gtk_printer_option_choices_from_array (GtkPrinterOption *option, - int num_choices, - char *choices[], - char *choices_display[]); -gboolean gtk_printer_option_has_choice (GtkPrinterOption *option, - const char *choice); +GtkPrinterOption *gtk_printer_option_new (const char *name, + const char *display_text, + GtkPrinterOptionType type); +void gtk_printer_option_set (GtkPrinterOption *option, + const char *value); +void gtk_printer_option_set_has_conflict (GtkPrinterOption *option, + gboolean has_conflict); +void gtk_printer_option_clear_has_conflict (GtkPrinterOption *option); +void gtk_printer_option_set_boolean (GtkPrinterOption *option, + gboolean value); +void gtk_printer_option_allocate_choices (GtkPrinterOption *option, + int num); +void gtk_printer_option_choices_from_array (GtkPrinterOption *option, + int num_choices, + char *choices[], + char *choices_display[]); +gboolean gtk_printer_option_has_choice (GtkPrinterOption *option, + const char *choice); +void gtk_printer_option_set_activates_default (GtkPrinterOption *option, + gboolean activates); +gboolean gtk_printer_option_get_activates_default (GtkPrinterOption *option); G_END_DECLS diff --git a/gtk/gtkprinteroptionwidget.c b/gtk/gtkprinteroptionwidget.c index 38cdf00801..468768fb33 100644 --- a/gtk/gtkprinteroptionwidget.c +++ b/gtk/gtkprinteroptionwidget.c @@ -767,6 +767,8 @@ construct_widgets (GtkPrinterOptionWidget *widget) case GTK_PRINTER_OPTION_TYPE_STRING: priv->entry = gtk_entry_new (); + gtk_entry_set_activates_default (GTK_ENTRY (priv->entry), + gtk_printer_option_get_activates_default (source)); gtk_widget_show (priv->entry); gtk_box_pack_start (GTK_BOX (widget), priv->entry, TRUE, TRUE, 0); g_signal_connect (priv->entry, "changed", G_CALLBACK (entry_changed_cb), widget); @@ -792,6 +794,8 @@ construct_widgets (GtkPrinterOptionWidget *widget) GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER); g_object_set (priv->combo, "local-only", FALSE, NULL); + gtk_entry_set_activates_default (GTK_ENTRY (priv->entry), + gtk_printer_option_get_activates_default (source)); label = gtk_label_new_with_mnemonic (_("_Name:")); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); @@ -850,7 +854,7 @@ update_widgets (GtkPrinterOptionWidget *widget) switch (source->type) { case GTK_PRINTER_OPTION_TYPE_BOOLEAN: - if (strcmp (source->value, "True") == 0) + if (g_ascii_strcasecmp (source->value, "True") == 0) gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->check), TRUE); else gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->check), FALSE); diff --git a/gtk/gtkprintoperation-unix.c b/gtk/gtkprintoperation-unix.c index c31f324acb..6b5aa15fc5 100644 --- a/gtk/gtkprintoperation-unix.c +++ b/gtk/gtkprintoperation-unix.c @@ -91,9 +91,26 @@ unix_start_page (GtkPrintOperation *op, (op->priv->page_position % op->priv->manual_number_up == 0)) { if (type == CAIRO_SURFACE_TYPE_PS) - cairo_ps_surface_set_size (op_unix->surface, w, h); + { + cairo_ps_surface_set_size (op_unix->surface, w, h); + cairo_ps_surface_dsc_begin_page_setup (op_unix->surface); + switch (gtk_page_setup_get_orientation (page_setup)) + { + case GTK_PAGE_ORIENTATION_PORTRAIT: + case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT: + cairo_ps_surface_dsc_comment (op_unix->surface, "%%PageOrientation: Portrait"); + break; + + case GTK_PAGE_ORIENTATION_LANDSCAPE: + case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE: + cairo_ps_surface_dsc_comment (op_unix->surface, "%%PageOrientation: Landscape"); + break; + } + } else if (type == CAIRO_SURFACE_TYPE_PDF) - cairo_pdf_surface_set_size (op_unix->surface, w, h); + { + cairo_pdf_surface_set_size (op_unix->surface, w, h); + } } } @@ -190,7 +207,7 @@ _gtk_print_operation_platform_backend_launch_preview (GtkPrintOperation *op, gchar *cmd; gchar *preview_cmd; GtkSettings *settings; - GtkPrintSettings *print_settings; + GtkPrintSettings *print_settings = NULL; GtkPageSetup *page_setup; GKeyFile *key_file = NULL; gchar *data = NULL; @@ -218,8 +235,28 @@ _gtk_print_operation_platform_backend_launch_preview (GtkPrintOperation *op, key_file = g_key_file_new (); - print_settings = gtk_print_operation_get_print_settings (op); - gtk_print_settings_to_key_file (print_settings, key_file, NULL); + print_settings = gtk_print_settings_copy (gtk_print_operation_get_print_settings (op)); + + if (print_settings != NULL) + { + gtk_print_settings_set_reverse (print_settings, FALSE); + gtk_print_settings_set_page_set (print_settings, GTK_PAGE_SET_ALL); + gtk_print_settings_set_scale (print_settings, 1.0); + gtk_print_settings_set_number_up (print_settings, 1); + gtk_print_settings_set_number_up_layout (print_settings, GTK_NUMBER_UP_LAYOUT_LEFT_TO_RIGHT_TOP_TO_BOTTOM); + + /* These removals are neccessary because cups-* settings have higher priority + * than normal settings. + */ + gtk_print_settings_unset (print_settings, "cups-reverse"); + gtk_print_settings_unset (print_settings, "cups-page-set"); + gtk_print_settings_unset (print_settings, "cups-scale"); + gtk_print_settings_unset (print_settings, "cups-number-up"); + gtk_print_settings_unset (print_settings, "cups-number-up-layout"); + + gtk_print_settings_to_key_file (print_settings, key_file, NULL); + g_object_unref (print_settings); + } page_setup = gtk_print_context_get_page_setup (op->priv->print_context); gtk_page_setup_to_key_file (page_setup, key_file, NULL); @@ -372,9 +409,9 @@ job_status_changed_cb (GtkPrintJob *job, static void -printer_changed_cb (GtkPrintUnixDialog *print_dialog, - GParamSpec *pspec, - gpointer user_data) +print_setup_changed_cb (GtkPrintUnixDialog *print_dialog, + GParamSpec *pspec, + gpointer user_data) { GtkPageSetup *page_setup; GtkPrintSettings *print_settings; @@ -450,7 +487,8 @@ get_print_dialog (GtkPrintOperation *op, gtk_print_unix_dialog_add_custom_tab (GTK_PRINT_UNIX_DIALOG (pd), priv->custom_widget, label); - g_signal_connect (pd, "notify::selected-printer", (GCallback) printer_changed_cb, op); + g_signal_connect (pd, "notify::selected-printer", (GCallback) print_setup_changed_cb, op); + g_signal_connect (pd, "notify::page-setup", (GCallback) print_setup_changed_cb, op); } return pd; diff --git a/gtk/gtkprintoperation-win32.c b/gtk/gtkprintoperation-win32.c index 3da48cb708..b7210f0d8b 100644 --- a/gtk/gtkprintoperation-win32.c +++ b/gtk/gtkprintoperation-win32.c @@ -444,6 +444,7 @@ win32_start_page (GtkPrintOperation *op, GtkPrintOperationWin32 *op_win32 = op->priv->platform_data; LPDEVMODEW devmode; GtkPaperSize *paper_size; + double x_off, y_off; devmode = GlobalLock (op_win32->devmode); @@ -468,6 +469,10 @@ win32_start_page (GtkPrintOperation *op, ResetDCW (op_win32->hdc, devmode); GlobalUnlock (op_win32->devmode); + + x_off = GetDeviceCaps (op_win32->hdc, PHYSICALOFFSETX); + y_off = GetDeviceCaps (op_win32->hdc, PHYSICALOFFSETY); + cairo_surface_set_device_offset (op_win32->surface, -x_off, -y_off); StartPage (op_win32->hdc); } diff --git a/gtk/gtkprintoperation.c b/gtk/gtkprintoperation.c index 765e7d0b25..9e7b58137c 100644 --- a/gtk/gtkprintoperation.c +++ b/gtk/gtkprintoperation.c @@ -70,16 +70,23 @@ enum PROP_STATUS, PROP_STATUS_STRING, PROP_CUSTOM_TAB_LABEL, - PROP_EMBED_PAGE_SETUP + PROP_EMBED_PAGE_SETUP, + PROP_HAS_SELECTION, + PROP_SUPPORT_SELECTION, + PROP_N_PAGES_TO_PRINT }; static guint signals[LAST_SIGNAL] = { 0 }; static int job_nr = 0; +typedef struct _PrintPagesData PrintPagesData; -static void preview_iface_init (GtkPrintOperationPreviewIface *iface); -static GtkPageSetup *create_page_setup (GtkPrintOperation *op); -static void common_render_page (GtkPrintOperation *op, - gint page_nr); +static void preview_iface_init (GtkPrintOperationPreviewIface *iface); +static GtkPageSetup *create_page_setup (GtkPrintOperation *op); +static void common_render_page (GtkPrintOperation *op, + gint page_nr); +static void increment_page_sequence (PrintPagesData *data); +static void prepare_data (PrintPagesData *data); +static void clamp_page_ranges (PrintPagesData *data); G_DEFINE_TYPE_WITH_CODE (GtkPrintOperation, gtk_print_operation, G_TYPE_OBJECT, @@ -257,13 +264,23 @@ preview_start_page (GtkPrintOperation *op, GtkPrintContext *print_context, GtkPageSetup *page_setup) { - g_signal_emit_by_name (op, "got-page-size", print_context, page_setup); + if ((op->priv->manual_number_up < 2) || + (op->priv->page_position % op->priv->manual_number_up == 0)) + g_signal_emit_by_name (op, "got-page-size", print_context, page_setup); } static void preview_end_page (GtkPrintOperation *op, GtkPrintContext *print_context) { + cairo_t *cr; + + cr = gtk_print_context_get_cairo_context (print_context); + + if ((op->priv->manual_number_up < 2) || + ((op->priv->page_position + 1) % op->priv->manual_number_up == 0) || + (op->priv->page_position == op->priv->nr_of_pages_to_print - 1)) + cairo_show_page (cr); } static void @@ -325,6 +342,12 @@ gtk_print_operation_set_property (GObject *object, case PROP_EMBED_PAGE_SETUP: gtk_print_operation_set_embed_page_setup (op, g_value_get_boolean (value)); break; + case PROP_HAS_SELECTION: + gtk_print_operation_set_has_selection (op, g_value_get_boolean (value)); + break; + case PROP_SUPPORT_SELECTION: + gtk_print_operation_set_support_selection (op, g_value_get_boolean (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -387,12 +410,46 @@ gtk_print_operation_get_property (GObject *object, case PROP_EMBED_PAGE_SETUP: g_value_set_boolean (value, priv->embed_page_setup); break; + case PROP_HAS_SELECTION: + g_value_set_boolean (value, priv->has_selection); + break; + case PROP_SUPPORT_SELECTION: + g_value_set_boolean (value, priv->support_selection); + break; + case PROP_N_PAGES_TO_PRINT: + g_value_set_int (value, priv->nr_of_pages_to_print); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } +struct _PrintPagesData +{ + GtkPrintOperation *op; + gint uncollated_copies; + gint collated_copies; + gint uncollated, collated, total; + + gint range, num_ranges; + GtkPageRange *ranges; + GtkPageRange one_range; + + gint page; + gint sheet; + gint first_position, last_position; + gint first_sheet; + gint num_of_sheets; + gint *pages; + + GtkWidget *progress; + + gboolean initialized; + gboolean is_preview; + gboolean done; +}; + typedef struct { GtkPrintOperationPreview *preview; @@ -400,8 +457,8 @@ typedef struct GtkWindow *parent; cairo_surface_t *surface; gchar *filename; - guint page_nr; gboolean wait; + PrintPagesData *pages_data; } PreviewOp; static void @@ -423,6 +480,10 @@ preview_print_idle_done (gpointer data) gtk_print_operation_preview_end_preview (pop->preview); + g_object_unref (pop->pages_data->op); + g_free (pop->pages_data->pages); + g_free (pop->pages_data); + g_object_unref (op); g_free (pop); } @@ -432,9 +493,8 @@ preview_print_idle (gpointer data) { PreviewOp *pop; GtkPrintOperation *op; - gboolean retval = TRUE; - cairo_t *cr; GtkPrintOperationPrivate *priv; + gboolean done = FALSE; pop = (PreviewOp *) data; op = GTK_PRINT_OPERATION (pop->preview); @@ -443,24 +503,23 @@ preview_print_idle (gpointer data) if (priv->page_drawing_state == GTK_PAGE_DRAWING_STATE_READY) { - /* TODO: print out sheets not pages and follow ranges */ - if (pop->page_nr >= op->priv->nr_of_pages) - retval = FALSE; - - if (pop->page_nr > 0) + if (!pop->pages_data->initialized) { - cr = gtk_print_context_get_cairo_context (pop->print_context); - _gtk_print_operation_platform_backend_preview_end_page (op, pop->surface, cr); + pop->pages_data->initialized = TRUE; + prepare_data (pop->pages_data); } - - if (retval) + else { - gtk_print_operation_preview_render_page (pop->preview, pop->page_nr); - pop->page_nr++; + increment_page_sequence (pop->pages_data); + + if (!pop->pages_data->done) + gtk_print_operation_preview_render_page (pop->preview, pop->pages_data->page); + else + done = priv->page_drawing_state == GTK_PAGE_DRAWING_STATE_READY; } } - return retval; + return !done; } static void @@ -484,7 +543,6 @@ preview_ready (GtkPrintOperationPreview *preview, GtkPrintContext *context, PreviewOp *pop) { - pop->page_nr = 0; pop->print_context = context; g_object_ref (preview); @@ -511,6 +569,9 @@ gtk_print_operation_preview_handler (GtkPrintOperation *op, pop->filename = NULL; pop->preview = preview; pop->parent = parent; + pop->pages_data = g_new0 (PrintPagesData, 1); + pop->pages_data->op = g_object_ref (GTK_PRINT_OPERATION (preview)); + pop->pages_data->is_preview = TRUE; page_setup = gtk_print_context_get_page_setup (context); @@ -1213,7 +1274,7 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) * Since: 2.18 */ g_object_class_install_property (gobject_class, - PROP_TRACK_PRINT_STATUS, + PROP_SUPPORT_SELECTION, g_param_spec_boolean ("support-selection", P_("Support Selection"), P_("TRUE if the print operation will support print of selection."), @@ -1230,7 +1291,7 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) * Since: 2.18 */ g_object_class_install_property (gobject_class, - PROP_TRACK_PRINT_STATUS, + PROP_HAS_SELECTION, g_param_spec_boolean ("has-selection", P_("Has Selection"), P_("TRUE if a selecion exists."), @@ -1252,6 +1313,30 @@ gtk_print_operation_class_init (GtkPrintOperationClass *class) P_("TRUE if page setup combos are embedded in GtkPrintDialog"), FALSE, GTK_PARAM_READWRITE)); + /** + * GtkPrintOperation:n-pages-to-print: + * + * The number of pages that will be printed. + * + * Note that this value is set during print preparation phase + * (%GTK_PRINT_STATUS_PREPARING), so this value should never be + * get before the data generation phase (%GTK_PRINT_STATUS_GENERATING_DATA). + * You can connect to the #GtkPrintOperation::status-changed signal + * and call gtk_print_operation_get_n_pages_to_print() when + * print status is %GTK_PRINT_STATUS_GENERATING_DATA. + * This is typically used to track the progress of print operation. + * + * Since: 2.18 + */ + g_object_class_install_property (gobject_class, + PROP_N_PAGES_TO_PRINT, + g_param_spec_int ("n-pages-to-print", + P_("Number of Pages To Print"), + P_("The number of pages that will be printed."), + -1, + G_MAXINT, + -1, + GTK_PARAM_READABLE)); } /** @@ -1984,30 +2069,6 @@ run_pdf (GtkPrintOperation *op, return GTK_PRINT_OPERATION_RESULT_APPLY; } -typedef struct -{ - GtkPrintOperation *op; - gint uncollated_copies; - gint collated_copies; - gint uncollated, collated, total; - - gint range, num_ranges; - GtkPageRange *ranges; - GtkPageRange one_range; - - gint page; - gint sheet; - gint first_position, last_position; - gint first_sheet; - gint num_of_sheets; - gint *pages; - - GtkWidget *progress; - - gboolean initialized; - gboolean is_preview; - gboolean done; -} PrintPagesData; static void clamp_page_ranges (PrintPagesData *data) @@ -2055,6 +2116,12 @@ increment_page_sequence (PrintPagesData *data) GtkPrintOperationPrivate *priv = data->op->priv; gint inc; + if (data->total == -1) + { + data->total = 0; + return; + } + /* check whether we reached last position */ if (priv->page_position == data->last_position && !(data->collated_copies > 1 && data->collated < (data->collated_copies - 1))) @@ -2079,7 +2146,8 @@ increment_page_sequence (PrintPagesData *data) inc = 1; /* changing sheet */ - if ((priv->page_position + 1) % priv->manual_number_up == 0 || + if (priv->manual_number_up < 2 || + (priv->page_position + 1) % priv->manual_number_up == 0 || priv->page_position == data->last_position || priv->page_position == priv->nr_of_pages_to_print - 1) { @@ -2188,7 +2256,7 @@ update_progress (PrintPagesData *data) text = g_strdup (_("Preparing")); } else if (priv->status == GTK_PRINT_STATUS_GENERATING_DATA) - text = g_strdup_printf (_("Printing %d"), data->total - 1); + text = g_strdup_printf (_("Printing %d"), data->total); if (text) { @@ -2448,6 +2516,10 @@ common_render_page (GtkPrintOperation *op, x = columns - 1 - (priv->page_position / rows) % columns; y = rows - 1 - priv->page_position % rows; break; + default: + g_assert_not_reached(); + x = 0; + y = 0; } if (priv->manual_number_up == 4 || priv->manual_number_up == 9 || priv->manual_number_up == 16) @@ -2531,27 +2603,27 @@ prepare_data (PrintPagesData *data) priv = data->op->priv; + if (priv->manual_collation) + { + data->uncollated_copies = priv->manual_num_copies; + data->collated_copies = 1; + } + else + { + data->uncollated_copies = 1; + data->collated_copies = priv->manual_num_copies; + } + if (!data->initialized) { data->initialized = TRUE; page_setup = create_page_setup (data->op); - _gtk_print_context_set_page_setup (priv->print_context, - page_setup); + _gtk_print_context_set_page_setup (priv->print_context, + page_setup); g_object_unref (page_setup); g_signal_emit (data->op, signals[BEGIN_PRINT], 0, priv->print_context); - if (priv->manual_collation) - { - data->uncollated_copies = priv->manual_num_copies; - data->collated_copies = 1; - } - else - { - data->uncollated_copies = 1; - data->collated_copies = priv->manual_num_copies; - } - return; } @@ -2618,14 +2690,19 @@ prepare_data (PrintPagesData *data) counter++; } - data->total = 0; + data->total = -1; data->collated = 0; data->uncollated = 0; - if (priv->nr_of_pages_to_print % priv->manual_number_up == 0) - data->num_of_sheets = priv->nr_of_pages_to_print / priv->manual_number_up; + if (priv->manual_number_up > 1) + { + if (priv->nr_of_pages_to_print % priv->manual_number_up == 0) + data->num_of_sheets = priv->nr_of_pages_to_print / priv->manual_number_up; + else + data->num_of_sheets = priv->nr_of_pages_to_print / priv->manual_number_up + 1; + } else - data->num_of_sheets = priv->nr_of_pages_to_print / priv->manual_number_up + 1; + data->num_of_sheets = priv->nr_of_pages_to_print; if (priv->manual_reverse) { @@ -2716,11 +2793,10 @@ print_pages_idle (gpointer user_data) goto out; } + increment_page_sequence (data); + if (!data->done) - { - common_render_page (data->op, data->page); - increment_page_sequence (data); - } + common_render_page (data->op, data->page); else done = priv->page_drawing_state == GTK_PAGE_DRAWING_STATE_READY; @@ -2859,12 +2935,12 @@ print_pages (GtkPrintOperation *op, &priv->num_page_ranges); priv->manual_num_copies = 1; priv->manual_collation = FALSE; - priv->manual_reverse = FALSE; - priv->manual_page_set = GTK_PAGE_SET_ALL; - priv->manual_scale = 1.0; + priv->manual_reverse = gtk_print_settings_get_reverse (priv->print_settings); + priv->manual_page_set = gtk_print_settings_get_page_set (priv->print_settings); + priv->manual_scale = gtk_print_settings_get_scale (priv->print_settings) / 100.0; priv->manual_orientation = TRUE; - priv->manual_number_up = 1; - priv->manual_number_up_layout = GTK_NUMBER_UP_LAYOUT_LEFT_TO_RIGHT_TOP_TO_BOTTOM; + priv->manual_number_up = gtk_print_settings_get_number_up (priv->print_settings); + priv->manual_number_up_layout = gtk_print_settings_get_number_up_layout (priv->print_settings); } priv->print_pages_idle_id = gdk_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE + 10, @@ -3175,5 +3251,31 @@ gtk_print_operation_get_has_selection (GtkPrintOperation *op) return op->priv->has_selection; } +/** + * gtk_print_operation_get_n_pages_to_print: + * @op: a #GtkPrintOperation + * + * Returns the number of pages that will be printed. + * + * Note that this value is set during print preparation phase + * (%GTK_PRINT_STATUS_PREPARING), so this function should never be + * called before the data generation phase (%GTK_PRINT_STATUS_GENERATING_DATA). + * You can connect to the #GtkPrintOperation::status-changed signal + * and call gtk_print_operation_get_n_pages_to_print() when + * print status is %GTK_PRINT_STATUS_GENERATING_DATA. + * This is typically used to track the progress of print operation. + * + * Returns: the number of pages that will be printed + * + * Since: 2.18 + **/ +gint +gtk_print_operation_get_n_pages_to_print (GtkPrintOperation *op) +{ + g_return_val_if_fail (GTK_IS_PRINT_OPERATION (op), -1); + + return op->priv->nr_of_pages_to_print; +} + #define __GTK_PRINT_OPERATION_C__ #include "gtkaliasdef.c" diff --git a/gtk/gtkprintoperation.h b/gtk/gtkprintoperation.h index 7ac38d4c2a..ef9a2879f3 100644 --- a/gtk/gtkprintoperation.h +++ b/gtk/gtkprintoperation.h @@ -187,6 +187,7 @@ gboolean gtk_print_operation_get_has_selection (GtkPrintOper void gtk_print_operation_set_embed_page_setup (GtkPrintOperation *op, gboolean embed); gboolean gtk_print_operation_get_embed_page_setup (GtkPrintOperation *op); +gint gtk_print_operation_get_n_pages_to_print (GtkPrintOperation *op); GtkPageSetup *gtk_print_run_page_setup_dialog (GtkWindow *parent, GtkPageSetup *page_setup, diff --git a/gtk/gtkprintsettings.c b/gtk/gtkprintsettings.c index e50208017b..f35552a198 100644 --- a/gtk/gtkprintsettings.c +++ b/gtk/gtkprintsettings.c @@ -1152,7 +1152,7 @@ gtk_print_settings_set_n_copies (GtkPrintSettings *settings, gint gtk_print_settings_get_number_up (GtkPrintSettings *settings) { - return gtk_print_settings_get_int (settings, GTK_PRINT_SETTINGS_NUMBER_UP); + return gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_NUMBER_UP, 1); } /** @@ -1185,7 +1185,7 @@ gtk_print_settings_set_number_up (GtkPrintSettings *settings, gint gtk_print_settings_get_resolution (GtkPrintSettings *settings) { - return gtk_print_settings_get_int (settings, GTK_PRINT_SETTINGS_RESOLUTION); + return gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION, 300); } /** @@ -1224,7 +1224,7 @@ gtk_print_settings_set_resolution (GtkPrintSettings *settings, gint gtk_print_settings_get_resolution_x (GtkPrintSettings *settings) { - return gtk_print_settings_get_int (settings, GTK_PRINT_SETTINGS_RESOLUTION_X); + return gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION_X, 300); } /** @@ -1240,7 +1240,7 @@ gtk_print_settings_get_resolution_x (GtkPrintSettings *settings) gint gtk_print_settings_get_resolution_y (GtkPrintSettings *settings) { - return gtk_print_settings_get_int (settings, GTK_PRINT_SETTINGS_RESOLUTION_Y); + return gtk_print_settings_get_int_with_default (settings, GTK_PRINT_SETTINGS_RESOLUTION_Y, 300); } /** @@ -1281,7 +1281,7 @@ gtk_print_settings_set_resolution_xy (GtkPrintSettings *settings, gdouble gtk_print_settings_get_printer_lpi (GtkPrintSettings *settings) { - return gtk_print_settings_get_double (settings, GTK_PRINT_SETTINGS_PRINTER_LPI); + return gtk_print_settings_get_double_with_default (settings, GTK_PRINT_SETTINGS_PRINTER_LPI, 150.0); } /** diff --git a/gtk/gtkprintunixdialog.c b/gtk/gtkprintunixdialog.c index 38b43a1a5c..5e6f771be5 100644 --- a/gtk/gtkprintunixdialog.c +++ b/gtk/gtkprintunixdialog.c @@ -380,6 +380,32 @@ get_toplevel (GtkWidget *widget) } static void +set_busy_cursor (GtkPrintUnixDialog *dialog, + gboolean busy) +{ + GtkWindow *toplevel; + GdkDisplay *display; + GdkCursor *cursor; + + toplevel = get_toplevel (GTK_WIDGET (dialog)); + if (!toplevel || !GTK_WIDGET_REALIZED (toplevel)) + return; + + display = gtk_widget_get_display (GTK_WIDGET (toplevel)); + + if (busy) + cursor = gdk_cursor_new_for_display (display, GDK_WATCH); + else + cursor = NULL; + + gdk_window_set_cursor (GTK_WIDGET (toplevel)->window, cursor); + gdk_display_flush (display); + + if (cursor) + gdk_cursor_unref (cursor); +} + +static void add_custom_button_to_dialog (GtkDialog *dialog, const gchar *mnemonic_label, const gchar *stock_id, @@ -418,72 +444,81 @@ error_dialogs (GtkPrintUnixDialog *print_dialog, { printer = gtk_print_unix_dialog_get_selected_printer (print_dialog); - /* Shows overwrite confirmation dialog in the case of printing to file which - * already exists. */ - if (printer != NULL && gtk_printer_is_virtual (printer)) + if (printer != NULL) { - option = gtk_printer_option_set_lookup (priv->options, - "gtk-main-page-custom-input"); + if (priv->request_details_tag || !gtk_printer_is_accepting_jobs (printer)) + { + g_signal_stop_emission_by_name (print_dialog, "response"); + return TRUE; + } - if (option != NULL && - option->type == GTK_PRINTER_OPTION_TYPE_FILESAVE) + /* Shows overwrite confirmation dialog in the case of printing to file which + * already exists. */ + if (gtk_printer_is_virtual (printer)) { - file = g_file_new_for_uri (option->value); + option = gtk_printer_option_set_lookup (priv->options, + "gtk-main-page-custom-input"); - if (file != NULL && - g_file_query_exists (file, NULL)) + if (option != NULL && + option->type == GTK_PRINTER_OPTION_TYPE_FILESAVE) { - toplevel = get_toplevel (GTK_WIDGET (print_dialog)); - - basename = g_file_get_basename (file); - dirname = g_file_get_parse_name (g_file_get_parent (file)); - - dialog = gtk_message_dialog_new (toplevel, - GTK_DIALOG_MODAL | - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_QUESTION, - GTK_BUTTONS_NONE, - _("A file named \"%s\" already exists. Do you want to replace it?"), - basename); - - gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), - _("The file already exists in \"%s\". Replacing it will " - "overwrite its contents."), - dirname); - - gtk_dialog_add_button (GTK_DIALOG (dialog), - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); - add_custom_button_to_dialog (GTK_DIALOG (dialog), - _("_Replace"), - GTK_STOCK_PRINT, - GTK_RESPONSE_ACCEPT); - gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), - GTK_RESPONSE_ACCEPT, - GTK_RESPONSE_CANCEL, - -1); - gtk_dialog_set_default_response (GTK_DIALOG (dialog), - GTK_RESPONSE_ACCEPT); - - if (toplevel->group) - gtk_window_group_add_window (toplevel->group, - GTK_WINDOW (dialog)); + file = g_file_new_for_uri (option->value); - response = gtk_dialog_run (GTK_DIALOG (dialog)); - - gtk_widget_destroy (dialog); - - g_free (dirname); - g_free (basename); - - if (response != GTK_RESPONSE_ACCEPT) + if (file != NULL && + g_file_query_exists (file, NULL)) { - g_signal_stop_emission_by_name (print_dialog, "response"); - g_object_unref (file); - return TRUE; + toplevel = get_toplevel (GTK_WIDGET (print_dialog)); + + basename = g_file_get_basename (file); + dirname = g_file_get_parse_name (g_file_get_parent (file)); + + dialog = gtk_message_dialog_new (toplevel, + GTK_DIALOG_MODAL | + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_NONE, + _("A file named \"%s\" already exists. Do you want to replace it?"), + basename); + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("The file already exists in \"%s\". Replacing it will " + "overwrite its contents."), + dirname); + + gtk_dialog_add_button (GTK_DIALOG (dialog), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + add_custom_button_to_dialog (GTK_DIALOG (dialog), + _("_Replace"), + GTK_STOCK_PRINT, + GTK_RESPONSE_ACCEPT); + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_ACCEPT, + GTK_RESPONSE_CANCEL, + -1); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), + GTK_RESPONSE_ACCEPT); + + if (toplevel->group) + gtk_window_group_add_window (toplevel->group, + GTK_WINDOW (dialog)); + + response = gtk_dialog_run (GTK_DIALOG (dialog)); + + gtk_widget_destroy (dialog); + + g_free (dirname); + g_free (basename); + + if (response != GTK_RESPONSE_ACCEPT) + { + g_signal_stop_emission_by_name (print_dialog, "response"); + g_object_unref (file); + return TRUE; + } } - } - g_object_unref (file); + g_object_unref (file); + } } } } @@ -561,7 +596,7 @@ gtk_print_unix_dialog_destroy (GtkPrintUnixDialog *dialog) } static void -disconnect_printer_details_request (GtkPrintUnixDialog *dialog) +disconnect_printer_details_request (GtkPrintUnixDialog *dialog, gboolean details_failed) { GtkPrintUnixDialogPrivate *priv = dialog->priv; @@ -570,6 +605,21 @@ disconnect_printer_details_request (GtkPrintUnixDialog *dialog) g_signal_handler_disconnect (priv->request_details_printer, priv->request_details_tag); priv->request_details_tag = 0; + set_busy_cursor (dialog, FALSE); + if (details_failed) + gtk_list_store_set (GTK_LIST_STORE (priv->printer_list), + g_object_get_data (G_OBJECT (priv->request_details_printer), + "gtk-print-tree-iter"), + PRINTER_LIST_COL_STATE, + _("Getting printer information failed"), + -1); + else + gtk_list_store_set (GTK_LIST_STORE (priv->printer_list), + g_object_get_data (G_OBJECT (priv->request_details_printer), + "gtk-print-tree-iter"), + PRINTER_LIST_COL_STATE, + gtk_printer_get_state_message (priv->request_details_printer), + -1); g_object_unref (priv->request_details_printer); priv->request_details_printer = NULL; } @@ -584,7 +634,7 @@ gtk_print_unix_dialog_finalize (GObject *object) GList *node; unschedule_idle_mark_conflicts (dialog); - disconnect_printer_details_request (dialog); + disconnect_printer_details_request (dialog, FALSE); if (priv->current_printer) { @@ -1739,7 +1789,7 @@ printer_details_acquired (GtkPrinter *printer, { GtkPrintUnixDialogPrivate *priv = dialog->priv; - disconnect_printer_details_request (dialog); + disconnect_printer_details_request (dialog, !success); if (success) { @@ -1767,7 +1817,7 @@ selected_printer_changed (GtkTreeSelection *selection, priv->waiting_for_printer = NULL; } - disconnect_printer_details_request (dialog); + disconnect_printer_details_request (dialog, FALSE); printer = NULL; if (gtk_tree_selection_get_selected (selection, NULL, &filter_iter)) @@ -1806,6 +1856,11 @@ selected_printer_changed (GtkTreeSelection *selection, /* take the reference */ priv->request_details_printer = printer; gtk_printer_request_details (printer); + set_busy_cursor (dialog, TRUE); + gtk_list_store_set (GTK_LIST_STORE (priv->printer_list), + g_object_get_data (G_OBJECT (printer), "gtk-print-tree-iter"), + PRINTER_LIST_COL_STATE, _("Getting printer information..."), + -1); return; } @@ -2537,6 +2592,7 @@ draw_page_cb (GtkWidget *widget, gdouble paper_width, paper_height; gdouble pos_x, pos_y; gint pages_per_sheet; + gboolean ltr = TRUE; orientation = gtk_page_setup_get_orientation (priv->page_setup); landscape = @@ -2835,8 +2891,14 @@ draw_page_cb (GtkWidget *widget, g_free (text); pango_layout_get_size (layout, &layout_w, &layout_h); - cairo_translate (cr, pos_x - layout_w / PANGO_SCALE - 2 * RULER_DISTANCE, - widget->allocation.y + (widget->allocation.height - layout_h / PANGO_SCALE) / 2); + ltr = gtk_widget_get_direction (GTK_WIDGET (dialog)) == GTK_TEXT_DIR_LTR; + + if (ltr) + cairo_translate (cr, pos_x - layout_w / PANGO_SCALE - 2 * RULER_DISTANCE, + widget->allocation.y + (widget->allocation.height - layout_h / PANGO_SCALE) / 2); + else + cairo_translate (cr, pos_x + w + shadow_offset + 2 * RULER_DISTANCE, + widget->allocation.y + (widget->allocation.height - layout_h / PANGO_SCALE) / 2); pango_cairo_show_layout (cr, layout); @@ -2863,17 +2925,34 @@ draw_page_cb (GtkWidget *widget, cairo_set_line_width (cr, 1); - cairo_move_to (cr, pos_x - RULER_DISTANCE, pos_y); - cairo_line_to (cr, pos_x - RULER_DISTANCE, pos_y + h); - cairo_stroke (cr); + if (ltr) + { + cairo_move_to (cr, pos_x - RULER_DISTANCE, pos_y); + cairo_line_to (cr, pos_x - RULER_DISTANCE, pos_y + h); + cairo_stroke (cr); - cairo_move_to (cr, pos_x - RULER_DISTANCE - RULER_RADIUS, pos_y - 0.5); - cairo_line_to (cr, pos_x - RULER_DISTANCE + RULER_RADIUS, pos_y - 0.5); - cairo_stroke (cr); + cairo_move_to (cr, pos_x - RULER_DISTANCE - RULER_RADIUS, pos_y - 0.5); + cairo_line_to (cr, pos_x - RULER_DISTANCE + RULER_RADIUS, pos_y - 0.5); + cairo_stroke (cr); - cairo_move_to (cr, pos_x - RULER_DISTANCE - RULER_RADIUS, pos_y + h + 0.5); - cairo_line_to (cr, pos_x - RULER_DISTANCE + RULER_RADIUS, pos_y + h + 0.5); - cairo_stroke (cr); + cairo_move_to (cr, pos_x - RULER_DISTANCE - RULER_RADIUS, pos_y + h + 0.5); + cairo_line_to (cr, pos_x - RULER_DISTANCE + RULER_RADIUS, pos_y + h + 0.5); + cairo_stroke (cr); + } + else + { + cairo_move_to (cr, pos_x + w + shadow_offset + RULER_DISTANCE, pos_y); + cairo_line_to (cr, pos_x + w + shadow_offset + RULER_DISTANCE, pos_y + h); + cairo_stroke (cr); + + cairo_move_to (cr, pos_x + w + shadow_offset + RULER_DISTANCE - RULER_RADIUS, pos_y - 0.5); + cairo_line_to (cr, pos_x + w + shadow_offset + RULER_DISTANCE + RULER_RADIUS, pos_y - 0.5); + cairo_stroke (cr); + + cairo_move_to (cr, pos_x + w + shadow_offset + RULER_DISTANCE - RULER_RADIUS, pos_y + h + 0.5); + cairo_line_to (cr, pos_x + w + shadow_offset + RULER_DISTANCE + RULER_RADIUS, pos_y + h + 0.5); + cairo_stroke (cr); + } cairo_move_to (cr, pos_x, pos_y + h + shadow_offset + RULER_DISTANCE); cairo_line_to (cr, pos_x + w, pos_y + h + shadow_offset + RULER_DISTANCE); @@ -2998,21 +3077,32 @@ update_number_up_layout (GtkPrintUnixDialog *dialog) { option = priv->number_up_layout_2_option; - if (layout == GTK_NUMBER_UP_LAYOUT_LEFT_TO_RIGHT_TOP_TO_BOTTOM || - layout == GTK_NUMBER_UP_LAYOUT_TOP_TO_BOTTOM_LEFT_TO_RIGHT) - enum_value = g_enum_get_value (enum_class, GTK_NUMBER_UP_LAYOUT_LEFT_TO_RIGHT_TOP_TO_BOTTOM); - - if (layout == GTK_NUMBER_UP_LAYOUT_LEFT_TO_RIGHT_BOTTOM_TO_TOP || - layout == GTK_NUMBER_UP_LAYOUT_BOTTOM_TO_TOP_LEFT_TO_RIGHT) - enum_value = g_enum_get_value (enum_class, GTK_NUMBER_UP_LAYOUT_LEFT_TO_RIGHT_BOTTOM_TO_TOP); - - if (layout == GTK_NUMBER_UP_LAYOUT_RIGHT_TO_LEFT_TOP_TO_BOTTOM || - layout == GTK_NUMBER_UP_LAYOUT_TOP_TO_BOTTOM_RIGHT_TO_LEFT) - enum_value = g_enum_get_value (enum_class, GTK_NUMBER_UP_LAYOUT_RIGHT_TO_LEFT_TOP_TO_BOTTOM); - - if (layout == GTK_NUMBER_UP_LAYOUT_RIGHT_TO_LEFT_BOTTOM_TO_TOP || - layout == GTK_NUMBER_UP_LAYOUT_BOTTOM_TO_TOP_RIGHT_TO_LEFT) - enum_value = g_enum_get_value (enum_class, GTK_NUMBER_UP_LAYOUT_RIGHT_TO_LEFT_BOTTOM_TO_TOP); + switch (layout) + { + case GTK_NUMBER_UP_LAYOUT_LEFT_TO_RIGHT_TOP_TO_BOTTOM: + case GTK_NUMBER_UP_LAYOUT_TOP_TO_BOTTOM_LEFT_TO_RIGHT: + enum_value = g_enum_get_value (enum_class, GTK_NUMBER_UP_LAYOUT_LEFT_TO_RIGHT_TOP_TO_BOTTOM); + break; + + case GTK_NUMBER_UP_LAYOUT_LEFT_TO_RIGHT_BOTTOM_TO_TOP: + case GTK_NUMBER_UP_LAYOUT_BOTTOM_TO_TOP_LEFT_TO_RIGHT: + enum_value = g_enum_get_value (enum_class, GTK_NUMBER_UP_LAYOUT_LEFT_TO_RIGHT_BOTTOM_TO_TOP); + break; + + case GTK_NUMBER_UP_LAYOUT_RIGHT_TO_LEFT_TOP_TO_BOTTOM: + case GTK_NUMBER_UP_LAYOUT_TOP_TO_BOTTOM_RIGHT_TO_LEFT: + enum_value = g_enum_get_value (enum_class, GTK_NUMBER_UP_LAYOUT_RIGHT_TO_LEFT_TOP_TO_BOTTOM); + break; + + case GTK_NUMBER_UP_LAYOUT_RIGHT_TO_LEFT_BOTTOM_TO_TOP: + case GTK_NUMBER_UP_LAYOUT_BOTTOM_TO_TOP_RIGHT_TO_LEFT: + enum_value = g_enum_get_value (enum_class, GTK_NUMBER_UP_LAYOUT_RIGHT_TO_LEFT_BOTTOM_TO_TOP); + break; + + default: + g_assert_not_reached(); + enum_value = NULL; + } } else { @@ -3700,12 +3790,16 @@ populate_dialog (GtkPrintUnixDialog *print_dialog) create_main_page (print_dialog); create_page_setup_page (print_dialog); create_job_page (print_dialog); + /* Translators: this will appear as tab label in print dialog. */ create_optional_page (print_dialog, _("Image Quality"), &priv->image_quality_table, &priv->image_quality_page); + /* Translators: this will appear as tab label in print dialog. */ create_optional_page (print_dialog, _("Color"), &priv->color_table, &priv->color_page); + /* Translators: this will appear as tab label in print dialog. */ + /* It's a typographical term, as in "Binding and finishing" */ create_optional_page (print_dialog, _("Finishing"), &priv->finishing_table, &priv->finishing_page); diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h index 5e51844c5b..7ba5a5db62 100644 --- a/gtk/gtkprivate.h +++ b/gtk/gtkprivate.h @@ -103,6 +103,15 @@ gboolean _gtk_fnmatch (const char *pattern, #define GTK_PARAM_WRITABLE G_PARAM_WRITABLE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB #define GTK_PARAM_READWRITE G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB +/* Many keyboard shortcuts for Mac are the same as for X + * except they use Command key instead of Control (e.g. Cut, + * Copy, Paste). This symbol is for those simple cases. */ +#ifndef GDK_WINDOWING_QUARTZ +#define GTK_DEFAULT_ACCEL_MOD_MASK GDK_CONTROL_MASK +#else +#define GTK_DEFAULT_ACCEL_MOD_MASK GDK_META_MASK +#endif + G_END_DECLS #endif /* __GTK_PRIVATE_H__ */ diff --git a/gtk/gtkradiobutton.c b/gtk/gtkradiobutton.c index 0d7277846b..f07113fe22 100644 --- a/gtk/gtkradiobutton.c +++ b/gtk/gtkradiobutton.c @@ -486,7 +486,7 @@ gtk_radio_button_focus (GtkWidget *widget, { GtkWidget *child = tmp_list->data; - if (GTK_WIDGET_REALIZED (child) && GTK_WIDGET_IS_SENSITIVE (child)) + if (GTK_WIDGET_MAPPED (child) && GTK_WIDGET_IS_SENSITIVE (child)) { new_focus = child; break; @@ -522,7 +522,7 @@ gtk_radio_button_focus (GtkWidget *widget, { GtkWidget *child = tmp_list->data; - if (GTK_WIDGET_REALIZED (child) && GTK_WIDGET_IS_SENSITIVE (child)) + if (GTK_WIDGET_MAPPED (child) && GTK_WIDGET_IS_SENSITIVE (child)) { new_focus = child; break; diff --git a/gtk/gtkrange.c b/gtk/gtkrange.c index ce4e5e4282..17c0d6f43a 100644 --- a/gtk/gtkrange.c +++ b/gtk/gtkrange.c @@ -844,6 +844,52 @@ gtk_range_get_inverted (GtkRange *range) } /** + * gtk_range_set_flippable: + * @range: a #GtkRange + * @flippable: %TRUE to make the range flippable + * + * If a range is flippable, it will switch its direction if it is + * horizontal and its direction is %GTK_TEXT_DIR_RTL. + * + * See gtk_widget_get_direction(). + * + * Since: 2.18 + **/ +void +gtk_range_set_flippable (GtkRange *range, + gboolean flippable) +{ + g_return_if_fail (GTK_IS_RANGE (range)); + + flippable = flippable ? TRUE : FALSE; + + if (flippable != range->flippable) + { + range->flippable = flippable; + + gtk_widget_queue_draw (GTK_WIDGET (range)); + } +} + +/** + * gtk_range_get_flippable: + * @range: a #GtkRange + * + * Gets the value set by gtk_range_set_flippable(). + * + * Return value: %TRUE if the range is flippable + * + * Since: 2.18 + **/ +gboolean +gtk_range_get_flippable (GtkRange *range) +{ + g_return_val_if_fail (GTK_IS_RANGE (range), FALSE); + + return range->flippable; +} + +/** * gtk_range_set_lower_stepper_sensitivity: * @range: a #GtkRange * @sensitivity: the lower stepper's sensitivity policy. diff --git a/gtk/gtkrange.h b/gtk/gtkrange.h index f7f3b96aa7..268a5223e1 100644 --- a/gtk/gtkrange.h +++ b/gtk/gtkrange.h @@ -147,6 +147,10 @@ void gtk_range_set_inverted (GtkRange *range gboolean setting); gboolean gtk_range_get_inverted (GtkRange *range); +void gtk_range_set_flippable (GtkRange *range, + gboolean flippable); +gboolean gtk_range_get_flippable (GtkRange *range); + void gtk_range_set_lower_stepper_sensitivity (GtkRange *range, GtkSensitivityType sensitivity); GtkSensitivityType gtk_range_get_lower_stepper_sensitivity (GtkRange *range); diff --git a/gtk/gtkrc.c b/gtk/gtkrc.c index a2ac58b932..70540a3130 100644 --- a/gtk/gtkrc.c +++ b/gtk/gtkrc.c @@ -659,11 +659,15 @@ gtk_rc_color_hash_changed (GtkSettings *settings, GParamSpec *pspec, GtkRcContext *context) { - if (context->color_hash) - g_hash_table_unref (context->color_hash); - + GHashTable *old_hash; + + old_hash = context->color_hash; + g_object_get (settings, "color-hash", &context->color_hash, NULL); + if (old_hash) + g_hash_table_unref (old_hash); + gtk_rc_reparse_all_for_settings (settings, TRUE); } @@ -3148,8 +3152,10 @@ gtk_rc_parse_style (GtkRcContext *context, break; case GTK_RC_TOKEN_COLOR: if (our_hash == NULL) - gtk_rc_style_prepend_empty_color_hash (rc_style); - our_hash = rc_priv->color_hashes->data; + { + gtk_rc_style_prepend_empty_color_hash (rc_style); + our_hash = rc_priv->color_hashes->data; + } token = gtk_rc_parse_logical_color (scanner, rc_style, our_hash); break; case G_TOKEN_IDENTIFIER: diff --git a/gtk/gtkrc.key.mac b/gtk/gtkrc.key.mac new file mode 100644 index 0000000000..7b6e8d1e4f --- /dev/null +++ b/gtk/gtkrc.key.mac @@ -0,0 +1,240 @@ +binding "gtk-mac-alt-arrows" +{ + bind "<alt>Right" { "move-cursor" (words, 1, 0) } + bind "<alt>KP_Right" { "move-cursor" (words, 1, 0) } + bind "<alt>Left" { "move-cursor" (words, -1, 0) } + bind "<alt>KP_Left" { "move-cursor" (words, -1, 0) } + bind "<shift><alt>Right" { "move-cursor" (words, 1, 1) } + bind "<shift><alt>KP_Right" { "move-cursor" (words, 1, 1) } + bind "<shift><alt>Left" { "move-cursor" (words, -1, 1) } + bind "<shift><alt>KP_Left" { "move-cursor" (words, -1, 1) } +} + +class "GtkTextView" binding "gtk-mac-alt-arrows" +class "GtkLabel" binding "gtk-mac-alt-arrows" +class "GtkEntry" binding "gtk-mac-alt-arrows" + + +binding "gtk-mac-alt-delete" +{ + bind "<alt>Delete" { "delete-from-cursor" (word-ends, 1) } + bind "<alt>KP_Delete" { "delete-from-cursor" (word-ends, 1) } + bind "<alt>BackSpace" { "delete-from-cursor" (word-ends, -1) } +} + +class "GtkTextView" binding "gtk-mac-alt-delete" +class "GtkEntry" binding "gtk-mac-alt-delete" + + +binding "gtk-mac-cmd-c" +{ + bind "<meta>x" { "cut-clipboard" () } + bind "<meta>c" { "copy-clipboard" () } + bind "<meta>v" { "paste-clipboard" () } + unbind "<ctrl>x" + unbind "<ctrl>c" + unbind "<ctrl>v" +} + +class "GtkTextView" binding "gtk-mac-cmd-c" +class "GtkEntry" binding "gtk-mac-cmd-c" + + +binding "gtk-mac-text-view" +{ + bind "<shift><meta>a" { "select-all" (0) } + bind "<meta>a" { "select-all" (1) } + unbind "<shift><ctrl>a" + unbind "<ctrl>a" +} + +class "GtkTextView" binding "gtk-mac-text-view" + + +binding "gtk-mac-label" +{ + bind "<meta>a" { + "move-cursor" (paragraph-ends, -1, 0) + "move-cursor" (paragraph-ends, 1, 1) + } + bind "<shift><meta>a" { "move-cursor" (paragraph-ends, 0, 0) } + bind "<meta>c" { "copy-clipboard" () } + unbind "<ctrl>a" + unbind "<shift><ctrl>a" + unbind "<ctrl>c" +} + +class "GtkLabel" binding "gtk-mac-label" + + +binding "gtk-mac-entry" +{ + bind "<meta>a" { + "move-cursor" (buffer-ends, -1, 0) + "move-cursor" (buffer-ends, 1, 1) + } + bind "<shift><meta>a" { "move-cursor" (visual-positions, 0, 0) } + unbind "<ctrl>a" + unbind "<shift><ctrl>a" +} + +class "GtkEntry" binding "gtk-mac-entry" + + +binding "gtk-mac-file-chooser" +{ + bind "<meta>v" { "location-popup-on-paste" () } + unbind "<ctrl>v" + + bind "<meta><shift>G" { "location-popup" () } + bind "<meta><shift>H" { "home-folder" () } + bind "<meta>Up" { "up-folder" () } +} + +class "GtkFileChooserDefault" binding "gtk-mac-file-chooser" + + +binding "gtk-mac-tree-view" +{ + bind "<meta>a" { "select-all" () } + bind "<shift><meta>a" { "unselect-all" () } + bind "<meta>f" { "start-interactive-search" () } + bind "<meta>F" { "start-interactive-search" () } + unbind "<ctrl>a" + unbind "<shift><ctrl>a" + unbind "<ctrl>f" + unbind "<ctrl>F" +} + +class "GtkTreeView" binding "gtk-mac-tree-view" + + +binding "gtk-mac-icon-view" +{ + bind "<meta>a" { "select-all" () } + bind "<shift><meta>a" { "unselect-all" () } + unbind "<ctrl>a" + unbind "<shift><ctrl>a" +} + +class "GtkIconView" binding "gtk-mac-icon-view" +binding "gtk-mac-alt-arrows" +{ + bind "<alt>Right" { "move-cursor" (words, 1, 0) } + bind "<alt>KP_Right" { "move-cursor" (words, 1, 0) } + bind "<alt>Left" { "move-cursor" (words, -1, 0) } + bind "<alt>KP_Left" { "move-cursor" (words, -1, 0) } + bind "<shift><alt>Right" { "move-cursor" (words, 1, 1) } + bind "<shift><alt>KP_Right" { "move-cursor" (words, 1, 1) } + bind "<shift><alt>Left" { "move-cursor" (words, -1, 1) } + bind "<shift><alt>KP_Left" { "move-cursor" (words, -1, 1) } +} + +class "GtkTextView" binding "gtk-mac-alt-arrows" +class "GtkLabel" binding "gtk-mac-alt-arrows" +class "GtkEntry" binding "gtk-mac-alt-arrows" + + +binding "gtk-mac-alt-delete" +{ + bind "<alt>Delete" { "delete-from-cursor" (word-ends, 1) } + bind "<alt>KP_Delete" { "delete-from-cursor" (word-ends, 1) } + bind "<alt>BackSpace" { "delete-from-cursor" (word-ends, -1) } +} + +class "GtkTextView" binding "gtk-mac-alt-delete" +class "GtkEntry" binding "gtk-mac-alt-delete" + + +binding "gtk-mac-cmd-c" +{ + bind "<meta>x" { "cut-clipboard" () } + bind "<meta>c" { "copy-clipboard" () } + bind "<meta>v" { "paste-clipboard" () } + unbind "<ctrl>x" + unbind "<ctrl>c" + unbind "<ctrl>v" +} + +class "GtkTextView" binding "gtk-mac-cmd-c" +class "GtkEntry" binding "gtk-mac-cmd-c" + + +binding "gtk-mac-text-view" +{ + bind "<shift><meta>a" { "select-all" (0) } + bind "<meta>a" { "select-all" (1) } + unbind "<shift><ctrl>a" + unbind "<ctrl>a" +} + +class "GtkTextView" binding "gtk-mac-text-view" + + +binding "gtk-mac-label" +{ + bind "<meta>a" { + "move-cursor" (paragraph-ends, -1, 0) + "move-cursor" (paragraph-ends, 1, 1) + } + bind "<shift><meta>a" { "move-cursor" (paragraph-ends, 0, 0) } + bind "<meta>c" { "copy-clipboard" () } + unbind "<ctrl>a" + unbind "<shift><ctrl>a" + unbind "<ctrl>c" +} + +class "GtkLabel" binding "gtk-mac-label" + + +binding "gtk-mac-entry" +{ + bind "<meta>a" { + "move-cursor" (buffer-ends, -1, 0) + "move-cursor" (buffer-ends, 1, 1) + } + bind "<shift><meta>a" { "move-cursor" (visual-positions, 0, 0) } + unbind "<ctrl>a" + unbind "<shift><ctrl>a" +} + +class "GtkEntry" binding "gtk-mac-entry" + + +binding "gtk-mac-file-chooser" +{ + bind "<meta>v" { "location-popup-on-paste" () } + unbind "<ctrl>v" + + bind "<meta><shift>G" { "location-popup" () } + bind "<meta><shift>H" { "home-folder" () } + bind "<meta>Up" { "up-folder" () } +} + +class "GtkFileChooserDefault" binding "gtk-mac-file-chooser" + + +binding "gtk-mac-tree-view" +{ + bind "<meta>a" { "select-all" () } + bind "<shift><meta>a" { "unselect-all" () } + bind "<meta>f" { "start-interactive-search" () } + bind "<meta>F" { "start-interactive-search" () } + unbind "<ctrl>a" + unbind "<shift><ctrl>a" + unbind "<ctrl>f" + unbind "<ctrl>F" +} + +class "GtkTreeView" binding "gtk-mac-tree-view" + + +binding "gtk-mac-icon-view" +{ + bind "<meta>a" { "select-all" () } + bind "<shift><meta>a" { "unselect-all" () } + unbind "<ctrl>a" + unbind "<shift><ctrl>a" +} + +class "GtkIconView" binding "gtk-mac-icon-view" diff --git a/gtk/gtkscale.c b/gtk/gtkscale.c index 87aa469da4..2f4cc98fb6 100644 --- a/gtk/gtkscale.c +++ b/gtk/gtkscale.c @@ -1423,7 +1423,7 @@ marks_start_element (GMarkupParseContext *context, ; else if (strcmp (element_name, "mark") == 0) { - gdouble value; + gdouble value = 0; gboolean has_value = FALSE; GtkPositionType position = GTK_POS_BOTTOM; const gchar *msg_context = NULL; @@ -1578,7 +1578,6 @@ gtk_scale_buildable_custom_finished (GtkBuildable *buildable, { GtkScale *scale = GTK_SCALE (buildable); MarksSubparserData *marks_data; - GtkWidget *toplevel; if (strcmp (tagname, "marks") == 0) { diff --git a/gtk/gtksearchenginetracker.c b/gtk/gtksearchenginetracker.c index e3d5a3ae3c..62447f8c29 100644 --- a/gtk/gtksearchenginetracker.c +++ b/gtk/gtksearchenginetracker.c @@ -31,9 +31,15 @@ typedef struct _TrackerClient TrackerClient; +typedef enum +{ + TRACKER_0_6 = 1 << 0, + TRACKER_0_7 = 1 << 1 +} TrackerVersion; + typedef void (*TrackerArrayReply) (char **result, GError *error, gpointer user_data); -static TrackerClient * (*tracker_connect) (gboolean enable_warnings) = NULL; +static TrackerClient * (*tracker_connect) (gboolean enable_warnings, gint timeout) = NULL; static void (*tracker_disconnect) (TrackerClient *client) = NULL; static int (*tracker_get_version) (TrackerClient *client, GError **error) = NULL; static void (*tracker_cancel_last_call) (TrackerClient *client) = NULL; @@ -52,22 +58,24 @@ static struct TrackerDlMapping { const char *fn_name; gpointer *fn_ptr_ref; + TrackerVersion versions; } tracker_dl_mapping[] = { -#define MAP(a) { #a, (gpointer *)&a } - MAP (tracker_connect), - MAP (tracker_disconnect), - MAP (tracker_get_version), - MAP (tracker_cancel_last_call), - MAP (tracker_search_metadata_by_text_async), - MAP (tracker_search_metadata_by_text_and_location_async), +#define MAP(a,v) { #a, (gpointer *)&a, v } + MAP (tracker_connect, TRACKER_0_6 | TRACKER_0_7), + MAP (tracker_disconnect, TRACKER_0_6 | TRACKER_0_7), + MAP (tracker_get_version, TRACKER_0_6), + MAP (tracker_cancel_last_call, TRACKER_0_6 | TRACKER_0_7), + MAP (tracker_search_metadata_by_text_async, TRACKER_0_6 | TRACKER_0_7), + MAP (tracker_search_metadata_by_text_and_location_async, TRACKER_0_6 | TRACKER_0_7), #undef MAP }; -static void +static TrackerVersion open_libtracker (void) { static gboolean done = FALSE; + static TrackerVersion version = 0; if (!done) { @@ -78,16 +86,29 @@ open_libtracker (void) done = TRUE; flags = G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL; - tracker = g_module_open ("libtrackerclient.so.0", flags); + tracker = g_module_open ("libtracker-client-0.7.so.0", flags); + version = TRACKER_0_7; + + if (!tracker) + { + tracker = g_module_open ("libtrackerclient.so.0", flags); + version = TRACKER_0_6; + } if (!tracker) - tracker = g_module_open ("libtracker.so.0", flags); + { + tracker = g_module_open ("libtracker.so.0", flags); + version = TRACKER_0_6; + } if (!tracker) - return; + return 0; for (i = 0; i < G_N_ELEMENTS (tracker_dl_mapping); i++) { + if ((tracker_dl_mapping[i].versions & version) == 0) + continue; + if (!g_module_symbol (tracker, tracker_dl_mapping[i].fn_name, tracker_dl_mapping[i].fn_ptr_ref)) { @@ -98,10 +119,12 @@ open_libtracker (void) for (i = 0; i < G_N_ELEMENTS (tracker_dl_mapping); i++) tracker_dl_mapping[i].fn_ptr_ref = NULL; - return; + return 0; } } } + + return version; } struct _GtkSearchEngineTrackerPrivate @@ -109,6 +132,7 @@ struct _GtkSearchEngineTrackerPrivate GtkQuery *query; TrackerClient *client; gboolean query_pending; + TrackerVersion version; }; G_DEFINE_TYPE (GtkSearchEngineTracker, _gtk_search_engine_tracker, GTK_TYPE_SEARCH_ENGINE); @@ -161,7 +185,11 @@ search_callback (gchar **results, { gchar *uri; - uri = g_filename_to_uri (*results_p, NULL, NULL); + if (tracker->priv->version == TRACKER_0_6) + uri = g_filename_to_uri (*results_p, NULL, NULL); + else + uri = *results_p; + if (uri) hit_uris = g_list_prepend (hit_uris, uri); } @@ -170,7 +198,8 @@ search_callback (gchar **results, _gtk_search_engine_finished (GTK_SEARCH_ENGINE (tracker)); g_strfreev (results); - g_list_foreach (hit_uris, (GFunc)g_free, NULL); + if (tracker->priv->version == TRACKER_0_6) + g_list_foreach (hit_uris, (GFunc)g_free, NULL); g_list_free (hit_uris); } @@ -195,9 +224,14 @@ gtk_search_engine_tracker_start (GtkSearchEngine *engine) location = NULL; if (location_uri) { - location = g_filename_from_uri (location_uri, NULL, NULL); - g_free (location_uri); - } + if (tracker->priv->version == TRACKER_0_6) + { + location = g_filename_from_uri (location_uri, NULL, NULL); + g_free (location_uri); + } + else + location = location_uri; + } if (location) { @@ -287,34 +321,39 @@ _gtk_search_engine_tracker_new (void) { GtkSearchEngineTracker *engine; TrackerClient *tracker_client; + TrackerVersion version; GError *err = NULL; - open_libtracker (); + version = open_libtracker (); if (!tracker_connect) return NULL; - tracker_client = tracker_connect (FALSE); + tracker_client = tracker_connect (FALSE, -1); if (!tracker_client) return NULL; - if (!tracker_get_version) - return NULL; + if (version == TRACKER_0_6) + { + if (!tracker_get_version) + return NULL; - tracker_get_version (tracker_client, &err); + tracker_get_version (tracker_client, &err); - if (err != NULL) - { - g_error_free (err); - tracker_disconnect (tracker_client); - return NULL; + if (err != NULL) + { + g_error_free (err); + tracker_disconnect (tracker_client); + return NULL; + } } engine = g_object_new (GTK_TYPE_SEARCH_ENGINE_TRACKER, NULL); engine->priv->client = tracker_client; engine->priv->query_pending = FALSE; + engine->priv->version = version; return GTK_SEARCH_ENGINE (engine); } diff --git a/gtk/gtkselection.c b/gtk/gtkselection.c index 7195b86a51..f6187b21c8 100644 --- a/gtk/gtkselection.c +++ b/gtk/gtkselection.c @@ -3067,7 +3067,7 @@ gtk_selection_default_handler (GtkWidget *widget, { gtk_selection_data_set (data, gdk_atom_intern_static_string ("NULL"), - 32, "", 0); + 32, NULL, 0); } else { diff --git a/gtk/gtksettings.c b/gtk/gtksettings.c index 2e507e39f3..96ee7f28aa 100644 --- a/gtk/gtksettings.c +++ b/gtk/gtksettings.c @@ -35,6 +35,12 @@ #include <pango/pangofc-fontmap.h> #endif +#ifdef GDK_WINDOWING_QUARTZ +#define DEFAULT_KEY_THEME "Mac" +#else +#define DEFAULT_KEY_THEME NULL +#endif + #define DEFAULT_TIMEOUT_INITIAL 200 #define DEFAULT_TIMEOUT_REPEAT 20 #define DEFAULT_TIMEOUT_EXPAND 500 @@ -312,7 +318,7 @@ gtk_settings_class_init (GtkSettingsClass *class) g_param_spec_string ("gtk-key-theme-name", P_("Key Theme Name"), P_("Name of key theme RC file to load"), - NULL, + DEFAULT_KEY_THEME, GTK_PARAM_READWRITE), NULL); g_assert (result == PROP_KEY_THEME_NAME); @@ -743,7 +749,7 @@ gtk_settings_class_init (GtkSettingsClass *class) * * A comma-separated list of print backends to use in the print * dialog. Available print backends depend on the GTK+ installation, - * and may include "pdf", "cups" or "lpr". + * and may include "file", "cups", "lpr" or "papi". * * Since: 2.10 */ @@ -2227,8 +2233,8 @@ settings_update_color_scheme (GtkSettings *settings) } static gboolean -add_color_to_hash (gchar *name, - GdkColor *color, +add_color_to_hash (gchar *name, + GdkColor *color, GHashTable *target) { GdkColor *old; @@ -2237,7 +2243,7 @@ add_color_to_hash (gchar *name, if (!old || !gdk_color_equal (old, color)) { g_hash_table_insert (target, g_strdup (name), gdk_color_copy (color)); - + return TRUE; } @@ -2245,7 +2251,7 @@ add_color_to_hash (gchar *name, } static gboolean -add_colors_to_hash_from_string (GHashTable *hash, +add_colors_to_hash_from_string (GHashTable *hash, const gchar *colors) { gchar *s, *p, *name; @@ -2293,24 +2299,27 @@ add_colors_to_hash_from_string (GHashTable *hash, static gboolean update_color_hash (ColorSchemeData *data, - const gchar *str, + const gchar *str, GtkSettingsSource source) { gboolean changed = FALSE; gint i; GHashTable *old_hash; + GHashTableIter iter; + gchar *name; + GdkColor *color; - if ((str == NULL || *str == '\0') && + if ((str == NULL || *str == '\0') && (data->lastentry[source] == NULL || data->lastentry[source][0] == '\0')) return FALSE; if (str && data->lastentry[source] && strcmp (str, data->lastentry[source]) == 0) return FALSE; - /* For the RC_FILE source we merge the values rather than over-writing + /* For the RC_FILE source we merge the values rather than over-writing * them, since multiple rc files might define independent sets of colors */ - if ((source != GTK_SETTINGS_SOURCE_RC_FILE) && + if ((source != GTK_SETTINGS_SOURCE_RC_FILE) && data->tables[source] && g_hash_table_size (data->tables[source]) > 0) { g_hash_table_unref (data->tables[source]); @@ -2319,22 +2328,36 @@ update_color_hash (ColorSchemeData *data, } if (data->tables[source] == NULL) - data->tables[source] = g_hash_table_new_full (g_str_hash, g_str_equal, + data->tables[source] = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) gdk_color_free); g_free (data->lastentry[source]); data->lastentry[source] = g_strdup (str); - + changed |= add_colors_to_hash_from_string (data->tables[source], str); if (!changed) return FALSE; - + /* Rebuild the merged hash table. */ - old_hash = data->color_hash; - data->color_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, - (GDestroyNotify) gdk_color_free); + if (data->color_hash) + { + old_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, + (GDestroyNotify) gdk_color_free); + + g_hash_table_iter_init (&iter, data->color_hash); + while (g_hash_table_iter_next (&iter, &name, &color)) + { + g_hash_table_insert (old_hash, name, color); + g_hash_table_iter_steal (&iter); + } + } + else + { + old_hash = NULL; + } + for (i = 0; i <= GTK_SETTINGS_SOURCE_APPLICATION; i++) { if (data->tables[i]) @@ -2361,13 +2384,13 @@ update_color_hash (ColorSchemeData *data, { changed = TRUE; break; - } + } } } g_hash_table_unref (old_hash); } - else + else changed = TRUE; return changed; diff --git a/gtk/gtksizegroup.c b/gtk/gtksizegroup.c index 5926b52557..6cd02c0e2e 100644 --- a/gtk/gtksizegroup.c +++ b/gtk/gtksizegroup.c @@ -223,6 +223,10 @@ queue_resize_on_widget (GtkWidget *widget, if (widget == parent) real_queue_resize (parent); } + else if (tmp_list->data == widget) + { + g_warning ("A container and its child are part of this SizeGroup"); + } else queue_resize_on_widget (tmp_list->data, FALSE); @@ -249,6 +253,10 @@ queue_resize_on_widget (GtkWidget *widget, if (widget == parent) real_queue_resize (parent); } + else if (tmp_list->data == widget) + { + g_warning ("A container and its child are part of this SizeGroup"); + } else queue_resize_on_widget (tmp_list->data, FALSE); diff --git a/gtk/gtksocket.c b/gtk/gtksocket.c index 8929648795..f059f8d6de 100644 --- a/gtk/gtksocket.c +++ b/gtk/gtksocket.c @@ -372,7 +372,6 @@ _gtk_socket_end_embedding (GtkSocket *socket) { GtkSocketPrivate *private = _gtk_socket_get_private (socket); GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (socket)); - gint i; if (GTK_IS_WINDOW (toplevel)) _gtk_socket_windowing_end_embedding_toplevel (socket); @@ -383,12 +382,7 @@ _gtk_socket_end_embedding (GtkSocket *socket) socket->current_height = 0; private->resize_count = 0; - /* Remove from end to avoid indexes shifting. This is evil */ - for (i = socket->accel_group->n_accels - 1; i >= 0; i--) - { - GtkAccelGroupEntry *accel_entry = &socket->accel_group->priv_accels[i]; - gtk_accel_group_disconnect (socket->accel_group, accel_entry->closure); - } + gtk_accel_group_disconnect (socket->accel_group, NULL); } static void @@ -594,22 +588,9 @@ _gtk_socket_remove_grabbed_key (GtkSocket *socket, guint keyval, GdkModifierType modifiers) { - gint i; - - for (i = 0; i < socket->accel_group->n_accels; i++) - { - GtkAccelGroupEntry *accel_entry = &socket->accel_group->priv_accels[i]; - if (accel_entry->key.accel_key == keyval && - accel_entry->key.accel_mods == modifiers) - { - gtk_accel_group_disconnect (socket->accel_group, - accel_entry->closure); - return; - } - } - - g_warning ("GtkSocket: request to remove non-present grabbed key %u,%#x\n", - keyval, modifiers); + if (!gtk_accel_group_disconnect_key (socket->accel_group, keyval, modifiers)) + g_warning ("GtkSocket: request to remove non-present grabbed key %u,%#x\n", + keyval, modifiers); } static void diff --git a/gtk/gtkspinner.c b/gtk/gtkspinner.c new file mode 100644 index 0000000000..036d0b1812 --- /dev/null +++ b/gtk/gtkspinner.c @@ -0,0 +1,627 @@ +/* GTK - The GIMP Toolkit + * + * Copyright (C) 2007 John Stowers, Neil Jagdish Patel. + * Copyright (C) 2009 Bastien Nocera, David Zeuthen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Code adapted from egg-spinner + * by Christian Hergert <christian.hergert@gmail.com> + */ + +/* + * Modified by the GTK+ Team and others 2007. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "config.h" + +#include "gtkintl.h" +#include "gtkaccessible.h" +#include "gtkimage.h" +#include "gtkspinner.h" +#include "gtkstyle.h" +#include "gtkalias.h" + + +/** + * SECTION:gtkspinner + * @Short_description: Show a spinner animation + * @Title: GtkSpinner + * @See_also: #GtkCellRendererSpinner, #GtkProgressBar + * + * A GtkSpinner widget displays an icon-size spinning animation. + * It is often used as an alternative to a #GtkProgressBar for + * displaying indefinite activity, instead of actual progress. + * + * To start the animation, use gtk_spinner_start(), to stop it + * use gtk_spinner_stop(). + */ + + +#define GTK_SPINNER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_SPINNER, GtkSpinnerPrivate)) + +G_DEFINE_TYPE (GtkSpinner, gtk_spinner, GTK_TYPE_DRAWING_AREA); + +enum { + PROP_0, + PROP_ACTIVE +}; + +struct _GtkSpinnerPrivate +{ + guint current; + guint num_steps; + guint cycle_duration; + gboolean active; + guint timeout; +}; + +static void gtk_spinner_class_init (GtkSpinnerClass *klass); +static void gtk_spinner_init (GtkSpinner *spinner); +static void gtk_spinner_dispose (GObject *gobject); +static void gtk_spinner_realize (GtkWidget *widget); +static void gtk_spinner_unrealize (GtkWidget *widget); +static gboolean gtk_spinner_expose (GtkWidget *widget, + GdkEventExpose *event); +static void gtk_spinner_screen_changed (GtkWidget *widget, + GdkScreen *old_screen); +static void gtk_spinner_style_set (GtkWidget *widget, + GtkStyle *prev_style); +static void gtk_spinner_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void gtk_spinner_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_spinner_set_active (GtkSpinner *spinner, + gboolean active); +static AtkObject *gtk_spinner_get_accessible (GtkWidget *widget); +static GType gtk_spinner_accessible_get_type (void); + +static void +gtk_spinner_class_init (GtkSpinnerClass *klass) +{ + GObjectClass *gobject_class; + GtkWidgetClass *widget_class; + + gobject_class = G_OBJECT_CLASS(klass); + g_type_class_add_private (gobject_class, sizeof (GtkSpinnerPrivate)); + gobject_class->dispose = gtk_spinner_dispose; + gobject_class->get_property = gtk_spinner_get_property; + gobject_class->set_property = gtk_spinner_set_property; + + widget_class = GTK_WIDGET_CLASS(klass); + widget_class->expose_event = gtk_spinner_expose; + widget_class->realize = gtk_spinner_realize; + widget_class->unrealize = gtk_spinner_unrealize; + widget_class->screen_changed = gtk_spinner_screen_changed; + widget_class->style_set = gtk_spinner_style_set; + widget_class->get_accessible = gtk_spinner_get_accessible; + + /* GtkSpinner:active: + * + * Whether the spinner is active + * + * Since 2.20 + */ + g_object_class_install_property (gobject_class, + PROP_ACTIVE, + g_param_spec_boolean ("active", + P_("Active"), + P_("Whether the spinner is active"), + FALSE, + G_PARAM_READWRITE)); + /** + * GtkSpinner:num-steps: + * + * The number of steps for the spinner to complete a full loop. + * The animation will complete a full cycle in one second by default + * (see the #GtkSpinner:cycle-duration style property). + * + * Since: 2.20 + */ + gtk_widget_class_install_style_property (widget_class, + g_param_spec_uint ("num-steps", + P_("Number of steps"), + P_("The number of steps for the spinner to complete a full loop. The animation will complete a full cycle in one second by default (see #GtkSpinner:cycle-duration)."), + 1, + G_MAXUINT, + 12, + G_PARAM_READABLE)); + + /** + * GtkSpinner:cycle-duration: + * + * The duration in milliseconds for the spinner to complete a full cycle. + * + * Since: 2.20 + */ + gtk_widget_class_install_style_property (widget_class, + g_param_spec_uint ("cycle-duration", + P_("Animation duration"), + P_("The length of time in milliseconds for the spinner to complete a full loop"), + 500, + G_MAXUINT, + 1000, + G_PARAM_READABLE)); +} + +static void +gtk_spinner_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + GtkSpinnerPrivate *priv; + + priv = GTK_SPINNER (object)->priv; + + switch (param_id) + { + case PROP_ACTIVE: + g_value_set_boolean (value, priv->active); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + } +} + +static void +gtk_spinner_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (param_id) + { + case PROP_ACTIVE: + gtk_spinner_set_active (GTK_SPINNER (object), g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + } +} + +static void +gtk_spinner_init (GtkSpinner *spinner) +{ + GtkSpinnerPrivate *priv; + + priv = GTK_SPINNER_GET_PRIVATE (spinner); + priv->current = 0; + priv->timeout = 0; + + spinner->priv = priv; + + GTK_WIDGET_SET_FLAGS (GTK_WIDGET (spinner), GTK_NO_WINDOW); +} + +static gboolean +gtk_spinner_expose (GtkWidget *widget, GdkEventExpose *event) +{ + GtkStateType state_type; + GtkSpinnerPrivate *priv; + int width, height; + + priv = GTK_SPINNER (widget)->priv; + + width = widget->allocation.width; + height = widget->allocation.height; + + if ((width < 12) || (height <12)) + gtk_widget_set_size_request (widget, 12, 12); + + state_type = GTK_STATE_NORMAL; + if (!GTK_WIDGET_IS_SENSITIVE (widget)) + state_type = GTK_STATE_INSENSITIVE; + + gtk_paint_spinner (widget->style, + widget->window, + state_type, + priv->current, + event->area.x, event->area.y, + event->area.width, event->area.height); + + return FALSE; +} + +static gboolean +gtk_spinner_timeout (gpointer data) +{ + GtkSpinnerPrivate *priv; + + priv = GTK_SPINNER (data)->priv; + + if (priv->current + 1 >= priv->num_steps) + priv->current = 0; + else + priv->current++; + + gtk_widget_queue_draw (GTK_WIDGET (data)); + + return TRUE; +} + +static void +gtk_spinner_add_timeout (GtkSpinner *spinner) +{ + GtkSpinnerPrivate *priv; + + priv = spinner->priv; + + priv->timeout = gdk_threads_add_timeout ((guint) priv->cycle_duration / priv->num_steps, gtk_spinner_timeout, spinner); +} + +static void +gtk_spinner_remove_timeout (GtkSpinner *spinner) +{ + GtkSpinnerPrivate *priv; + + priv = spinner->priv; + + g_source_remove (priv->timeout); + priv->timeout = 0; +} + +static void +gtk_spinner_realize (GtkWidget *widget) +{ + GtkSpinnerPrivate *priv; + + priv = GTK_SPINNER (widget)->priv; + + GTK_WIDGET_CLASS (gtk_spinner_parent_class)->realize (widget); + + if (priv->active) + gtk_spinner_add_timeout (GTK_SPINNER (widget)); +} + +static void +gtk_spinner_unrealize (GtkWidget *widget) +{ + GtkSpinnerPrivate *priv; + + priv = GTK_SPINNER (widget)->priv; + + if (priv->timeout != 0) + { + gtk_spinner_remove_timeout (GTK_SPINNER (widget)); + } + + GTK_WIDGET_CLASS (gtk_spinner_parent_class)->unrealize (widget); +} + +static void +gtk_spinner_screen_changed (GtkWidget* widget, GdkScreen* old_screen) +{ + GtkSpinner *spinner; + GdkScreen* new_screen; + GdkColormap* colormap; + + spinner = GTK_SPINNER (widget); + + new_screen = gtk_widget_get_screen (widget); + colormap = gdk_screen_get_rgba_colormap (new_screen); + + if (!colormap) + { + colormap = gdk_screen_get_rgb_colormap (new_screen); + } + + gtk_widget_set_colormap (widget, colormap); +} + +static void +gtk_spinner_style_set (GtkWidget *widget, + GtkStyle *prev_style) +{ + GtkSpinnerPrivate *priv; + + priv = GTK_SPINNER (widget)->priv; + + gtk_widget_style_get (GTK_WIDGET (widget), + "num-steps", &(priv->num_steps), + "cycle-duration", &(priv->cycle_duration), + NULL); + + if (priv->current > priv->num_steps) + priv->current = 0; +} + +static void +gtk_spinner_dispose (GObject *gobject) +{ + GtkSpinnerPrivate *priv; + + priv = GTK_SPINNER (gobject)->priv; + + if (priv->timeout != 0) + { + gtk_spinner_remove_timeout (GTK_SPINNER (gobject)); + } + + G_OBJECT_CLASS (gtk_spinner_parent_class)->dispose (gobject); +} + +static void +gtk_spinner_set_active (GtkSpinner *spinner, gboolean active) +{ + GtkSpinnerPrivate *priv; + + active = active != FALSE; + + priv = GTK_SPINNER (spinner)->priv; + + if (priv->active != active) + { + priv->active = active; + g_object_notify (G_OBJECT (spinner), "active"); + + if (active && GTK_WIDGET_REALIZED (GTK_WIDGET (spinner)) && priv->timeout == 0) + { + gtk_spinner_add_timeout (spinner); + } + else if (!active && priv->timeout != 0) + { + gtk_spinner_remove_timeout (spinner); + } + } +} + +static GType +gtk_spinner_accessible_factory_get_accessible_type (void) +{ + return gtk_spinner_accessible_get_type (); +} + +static AtkObject * +gtk_spinner_accessible_new (GObject *obj) +{ + AtkObject *accessible; + + g_return_val_if_fail (GTK_IS_WIDGET (obj), NULL); + + accessible = g_object_new (gtk_spinner_accessible_get_type (), NULL); + atk_object_initialize (accessible, obj); + + return accessible; +} + +static AtkObject* +gtk_spinner_accessible_factory_create_accessible (GObject *obj) +{ + return gtk_spinner_accessible_new (obj); +} + +static void +gtk_spinner_accessible_factory_class_init (AtkObjectFactoryClass *klass) +{ + klass->create_accessible = gtk_spinner_accessible_factory_create_accessible; + klass->get_accessible_type = gtk_spinner_accessible_factory_get_accessible_type; +} + +static GType +gtk_spinner_accessible_factory_get_type (void) +{ + static GType type = 0; + + if (!type) + { + const GTypeInfo tinfo = + { + sizeof (AtkObjectFactoryClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gtk_spinner_accessible_factory_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (AtkObjectFactory), + 0, /* n_preallocs */ + NULL, NULL + }; + + type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY, + I_("GtkSpinnerAccessibleFactory"), + &tinfo, 0); + } + return type; +} + +static AtkObjectClass *a11y_parent_class = NULL; + +static void +gtk_spinner_accessible_initialize (AtkObject *accessible, + gpointer widget) +{ + atk_object_set_name (accessible, _("Spinner")); + atk_object_set_description (accessible, _("Provides visual status")); + + a11y_parent_class->initialize (accessible, widget); +} + +static void +gtk_spinner_accessible_class_init (AtkObjectClass *klass) +{ + a11y_parent_class = g_type_class_peek_parent (klass); + + klass->initialize = gtk_spinner_accessible_initialize; +} + +static void +gtk_spinner_accessible_image_get_size (AtkImage *image, + gint *width, + gint *height) +{ + GtkWidget *widget; + + widget = GTK_ACCESSIBLE (image)->widget; + if (!widget) + { + *width = *height = 0; + } + else + { + *width = widget->allocation.width; + *height = widget->allocation.height; + } +} + +static void +gtk_spinner_accessible_image_interface_init (AtkImageIface *iface) +{ + iface->get_image_size = gtk_spinner_accessible_image_get_size; +} + +static GType +gtk_spinner_accessible_get_type (void) +{ + static GType type = 0; + + /* Action interface + Name etc. ... */ + if (G_UNLIKELY (type == 0)) + { + const GInterfaceInfo atk_image_info = { + (GInterfaceInitFunc) gtk_spinner_accessible_image_interface_init, + (GInterfaceFinalizeFunc) NULL, + NULL + }; + GType type; + GType parent_atk_type; + GTypeInfo tinfo = { 0 }; + GTypeQuery query; + AtkObjectFactory *factory; + + if ((type = g_type_from_name ("GtkSpinnerAccessible"))) + return type; + + factory = atk_registry_get_factory (atk_get_default_registry (), + GTK_TYPE_IMAGE); + if (!factory) + return G_TYPE_INVALID; + + parent_atk_type = atk_object_factory_get_accessible_type (factory); + if (!parent_atk_type) + return G_TYPE_INVALID; + + /* + * Figure out the size of the class and instance + * we are deriving from + */ + g_type_query (parent_atk_type, &query); + + tinfo.class_init = (GClassInitFunc) gtk_spinner_accessible_class_init; + tinfo.class_size = query.class_size; + tinfo.instance_size = query.instance_size; + + /* Register the type */ + type = g_type_register_static (parent_atk_type, + "GtkSpinnerAccessible", + &tinfo, 0); + + g_type_add_interface_static (type, ATK_TYPE_IMAGE, + &atk_image_info); + } + + return type; +} + +static AtkObject * +gtk_spinner_get_accessible (GtkWidget *widget) +{ + static gboolean first_time = TRUE; + + if (first_time) + { + AtkObjectFactory *factory; + AtkRegistry *registry; + GType derived_type; + GType derived_atk_type; + + /* + * Figure out whether accessibility is enabled by looking at the + * type of the accessible object which would be created for + * the parent type of GtkSpinner. + */ + derived_type = g_type_parent (GTK_TYPE_SPINNER); + + registry = atk_get_default_registry (); + factory = atk_registry_get_factory (registry, + derived_type); + derived_atk_type = atk_object_factory_get_accessible_type (factory); + if (g_type_is_a (derived_atk_type, GTK_TYPE_ACCESSIBLE)) + atk_registry_set_factory_type (registry, + GTK_TYPE_SPINNER, + gtk_spinner_accessible_factory_get_type ()); + first_time = FALSE; + } + return GTK_WIDGET_CLASS (gtk_spinner_parent_class)->get_accessible (widget); +} + +/** + * gtk_spinner_new: + * + * Returns a new spinner widget. Not yet started. + * + * Return value: a new #GtkSpinner + * + * Since: 2.20 + */ +GtkWidget * +gtk_spinner_new (void) +{ + return g_object_new (GTK_TYPE_SPINNER, NULL); +} + +/** + * gtk_spinner_start: + * @spinner: a #GtkSpinner + * + * Starts the animation of the spinner. + * + * Since: 2.20 + */ +void +gtk_spinner_start (GtkSpinner *spinner) +{ + g_return_if_fail (GTK_IS_SPINNER (spinner)); + + gtk_spinner_set_active (spinner, TRUE); +} + +/** + * gtk_spinner_stop: + * @spinner: a #GtkSpinner + * + * Stops the animation of the spinner. + * + * Since: 2.20 + */ +void +gtk_spinner_stop (GtkSpinner *spinner) +{ + g_return_if_fail (GTK_IS_SPINNER (spinner)); + + gtk_spinner_set_active (spinner, FALSE); +} + +#define __GTK_SPINNER_C__ +#include "gtkaliasdef.c" diff --git a/gtk/gtkspinner.h b/gtk/gtkspinner.h new file mode 100644 index 0000000000..6f9aa72809 --- /dev/null +++ b/gtk/gtkspinner.h @@ -0,0 +1,65 @@ +/* GTK - The GIMP Toolkit + * + * Copyright (C) 2007 John Stowers, Neil Jagdish Patel. + * Copyright (C) 2009 Bastien Nocera, David Zeuthen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Code adapted from egg-spinner + * by Christian Hergert <christian.hergert@gmail.com> + */ + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only <gtk/gtk.h> can be included directly." +#endif + +#ifndef __GTK_SPINNER_H__ +#define __GTK_SPINNER_H__ + +#include <gtk/gtkdrawingarea.h> + +G_BEGIN_DECLS + +#define GTK_TYPE_SPINNER (gtk_spinner_get_type ()) +#define GTK_SPINNER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SPINNER, GtkSpinner)) +#define GTK_SPINNER_CLASS(obj) (G_TYPE_CHECK_CLASS_CAST ((obj), GTK_SPINNER, GtkSpinnerClass)) +#define GTK_IS_SPINNER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SPINNER)) +#define GTK_IS_SPINNER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE ((obj), GTK_TYPE_SPINNER)) +#define GTK_SPINNER_GET_CLASS (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SPINNER, GtkSpinnerClass)) + +typedef struct _GtkSpinner GtkSpinner; +typedef struct _GtkSpinnerClass GtkSpinnerClass; +typedef struct _GtkSpinnerPrivate GtkSpinnerPrivate; + +struct _GtkSpinner +{ + GtkDrawingArea parent; + GtkSpinnerPrivate *priv; +}; + +struct _GtkSpinnerClass +{ + GtkDrawingAreaClass parent_class; +}; + +GType gtk_spinner_get_type (void) G_GNUC_CONST; +GtkWidget *gtk_spinner_new (void); +void gtk_spinner_start (GtkSpinner *spinner); +void gtk_spinner_stop (GtkSpinner *spinner); + +G_END_DECLS + +#endif /* __GTK_SPINNER_H__ */ diff --git a/gtk/gtkstatusbar.c b/gtk/gtkstatusbar.c index 2bddfa88b6..0a494c912a 100644 --- a/gtk/gtkstatusbar.c +++ b/gtk/gtkstatusbar.c @@ -198,6 +198,8 @@ gtk_statusbar_init (GtkStatusbar *statusbar) box = GTK_BOX (statusbar); + gtk_widget_set_redraw_on_allocate (GTK_WIDGET (box), TRUE); + box->spacing = 2; box->homogeneous = FALSE; @@ -616,10 +618,10 @@ gtk_statusbar_create_window (GtkStatusbar *statusbar) GdkWindowAttr attributes; gint attributes_mask; GdkRectangle rect; - + g_return_if_fail (GTK_WIDGET_REALIZED (statusbar)); g_return_if_fail (statusbar->has_resize_grip); - + widget = GTK_WIDGET (statusbar); get_grip_rect (statusbar, &rect); @@ -640,6 +642,8 @@ gtk_statusbar_create_window (GtkStatusbar *statusbar) gdk_window_set_user_data (statusbar->grip_window, widget); + gdk_window_raise (statusbar->grip_window); + set_grip_cursor (statusbar); } @@ -858,10 +862,10 @@ gtk_statusbar_size_allocate (GtkWidget *widget, if (statusbar->has_resize_grip) { - get_grip_rect (statusbar, &rect); - + get_grip_rect (statusbar, &rect); + extra_children = has_extra_children (statusbar); - + /* If there are extra children, we don't want them to occupy * the space where we draw the resize grip, so we temporarily * shrink the allocation. @@ -882,14 +886,6 @@ gtk_statusbar_size_allocate (GtkWidget *widget, if (statusbar->has_resize_grip) { - if (statusbar->grip_window) - { - gdk_window_raise (statusbar->grip_window); - gdk_window_move_resize (statusbar->grip_window, - rect.x, rect.y, - rect.width, rect.height); - } - if (extra_children) { allocation->width += rect.width; @@ -913,12 +909,23 @@ gtk_statusbar_size_allocate (GtkWidget *widget, /* shrink the label to make room for the grip */ *allocation = child->allocation; allocation->width = MAX (1, allocation->width - rect.width); - if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) allocation->x += child->allocation.width - allocation->width; gtk_widget_size_allocate (child, allocation); } } + + if (statusbar->grip_window) + { + get_grip_rect (statusbar, &rect); + + gdk_window_raise (statusbar->grip_window); + gdk_window_move_resize (statusbar->grip_window, + rect.x, rect.y, + rect.width, rect.height); + } + } } diff --git a/gtk/gtkstatusicon.c b/gtk/gtkstatusicon.c index 0d6a262d8d..f400bff73c 100644 --- a/gtk/gtkstatusicon.c +++ b/gtk/gtkstatusicon.c @@ -370,6 +370,10 @@ gtk_status_icon_class_init (GtkStatusIconClass *class) * the default handler for the #GtkStatusIcon::query-tooltip signal * will take care of displaying the tooltip. * + * Note that some platforms have limitations on the length of tooltips + * that they allow on status icons, e.g. Windows only shows the first + * 64 characters. + * * Since: 2.16 */ g_object_class_install_property (gobject_class, @@ -698,7 +702,7 @@ wndproc (HWND hwnd, if (!Shell_NotifyIconW (NIM_ADD, &priv->nid)) { - g_warning ("%s:%d:Shell_NotifyIcon(NIM_ADD) failed", __FILE__, __LINE__-2); + g_warning (G_STRLOC ": Shell_NotifyIcon(NIM_ADD) failed"); priv->nid.hWnd = NULL; continue; } @@ -898,7 +902,7 @@ gtk_status_icon_init (GtkStatusIcon *status_icon) if (!Shell_NotifyIconW (NIM_ADD, &priv->nid)) { - g_warning ("%s:%d:Shell_NotifyIcon(NIM_ADD) failed", __FILE__, __LINE__-2); + g_warning (G_STRLOC ": Shell_NotifyIcon(NIM_ADD) failed"); priv->nid.hWnd = NULL; } @@ -1348,7 +1352,7 @@ find_icon_size (GtkWidget *widget, dist = G_MAXINT; size = GTK_ICON_SIZE_MENU; - for (s = GTK_ICON_SIZE_MENU; s < GTK_ICON_SIZE_DIALOG; s++) + for (s = GTK_ICON_SIZE_MENU; s <= GTK_ICON_SIZE_DIALOG; s++) { if (gtk_icon_size_lookup_for_settings (settings, s, &w, &h) && w <= pixel_size && h <= pixel_size) @@ -1387,7 +1391,7 @@ gtk_status_icon_update_image (GtkStatusIcon *status_icon) priv->nid.uFlags |= NIF_ICON; if (priv->nid.hWnd != NULL && priv->visible) if (!Shell_NotifyIconW (NIM_MODIFY, &priv->nid)) - g_warning ("%s:%d:Shell_NotifyIcon(NIM_MODIFY) failed", __FILE__, __LINE__-1); + g_warning (G_STRLOC ": Shell_NotifyIcon(NIM_MODIFY) failed"); if (prev_hicon) DestroyIcon (prev_hicon); #endif @@ -1436,7 +1440,7 @@ gtk_status_icon_update_image (GtkStatusIcon *status_icon) priv->nid.uFlags |= NIF_ICON; if (priv->nid.hWnd != NULL && priv->visible) if (!Shell_NotifyIconW (NIM_MODIFY, &priv->nid)) - g_warning ("%s:%d:Shell_NotifyIcon(NIM_MODIFY) failed", __FILE__, __LINE__-1); + g_warning (G_STRLOC ": Shell_NotifyIcon(NIM_MODIFY) failed"); if (prev_hicon) DestroyIcon (prev_hicon); #endif @@ -1457,7 +1461,7 @@ gtk_status_icon_update_image (GtkStatusIcon *status_icon) priv->nid.uFlags &= ~NIF_ICON; if (priv->nid.hWnd != NULL && priv->visible) if (!Shell_NotifyIconW (NIM_MODIFY, &priv->nid)) - g_warning ("%s:%d:Shell_NotifyIcon(NIM_MODIFY) failed", __FILE__, __LINE__-1); + g_warning (G_STRLOC ": Shell_NotifyIcon(NIM_MODIFY) failed"); #endif #ifdef GDK_WINDOWING_QUARTZ [priv->status_item setImage:NULL]; @@ -1487,7 +1491,7 @@ gtk_status_icon_update_image (GtkStatusIcon *status_icon) priv->nid.uFlags |= NIF_ICON; if (priv->nid.hWnd != NULL && priv->visible) if (!Shell_NotifyIconW (NIM_MODIFY, &priv->nid)) - g_warning ("%s:%d:Shell_NotifyIcon(NIM_MODIFY) failed", __FILE__, __LINE__-1); + g_warning (G_STRLOC ": Shell_NotifyIcon(NIM_MODIFY) failed"); if (prev_hicon) DestroyIcon (prev_hicon); g_object_unref (pixbuf); @@ -1531,7 +1535,7 @@ gtk_status_icon_update_image (GtkStatusIcon *status_icon) priv->nid.uFlags |= NIF_ICON; if (priv->nid.hWnd != NULL && priv->visible) if (!Shell_NotifyIconW (NIM_MODIFY, &priv->nid)) - g_warning ("%s:%d:Shell_NotifyIcon(NIM_MODIFY) failed", __FILE__, __LINE__-1); + g_warning (G_STRLOC ": Shell_NotifyIcon(NIM_MODIFY) failed"); if (prev_hicon) DestroyIcon (prev_hicon); g_object_unref (pixbuf); @@ -1578,7 +1582,7 @@ gtk_status_icon_update_image (GtkStatusIcon *status_icon) priv->nid.uFlags |= NIF_ICON; if (priv->nid.hWnd != NULL && priv->visible) if (!Shell_NotifyIconW (NIM_MODIFY, &priv->nid)) - g_warning ("%s:%d:Shell_NotifyIcon(NIM_MODIFY) failed", __FILE__, __LINE__-1); + g_warning (G_STRLOC ": Shell_NotifyIcon(NIM_MODIFY) failed"); if (prev_hicon) DestroyIcon (prev_hicon); g_object_unref (pixbuf); @@ -1611,7 +1615,7 @@ gtk_status_icon_update_image (GtkStatusIcon *status_icon) priv->nid.uFlags &= ~NIF_ICON; if (priv->nid.hWnd != NULL && priv->visible) if (!Shell_NotifyIconW (NIM_MODIFY, &priv->nid)) - g_warning ("%s:%d:Shell_NotifyIcon(NIM_MODIFY) failed", __FILE__, __LINE__-1); + g_warning (G_STRLOC ": Shell_NotifyIcon(NIM_MODIFY) failed"); #endif #ifdef GDK_WINDOWING_QUARTZ { @@ -2733,7 +2737,7 @@ gtk_status_icon_set_tooltip_text (GtkStatusIcon *status_icon, } if (priv->nid.hWnd != NULL && priv->visible) if (!Shell_NotifyIconW (NIM_MODIFY, &priv->nid)) - g_warning ("%s:%d:Shell_NotifyIconW(NIM_MODIFY) failed", __FILE__, __LINE__-1); + g_warning (G_STRLOC ": Shell_NotifyIconW(NIM_MODIFY) failed"); g_free (priv->tooltip_text); priv->tooltip_text = g_strdup (text); diff --git a/gtk/gtkstock.c b/gtk/gtkstock.c index 153ef0877c..f896dec8f5 100644 --- a/gtk/gtkstock.c +++ b/gtk/gtkstock.c @@ -27,6 +27,7 @@ #include "config.h" #include <string.h> +#include "gtkprivate.h" #include "gtkstock.h" #include "gtkiconfactory.h" #include "gtkintl.h" diff --git a/gtk/gtkstyle.c b/gtk/gtkstyle.c index 769fe79143..01f8ff02a6 100644 --- a/gtk/gtkstyle.c +++ b/gtk/gtkstyle.c @@ -313,6 +313,14 @@ static void gtk_default_draw_resize_grip (GtkStyle *style, gint y, gint width, gint height); +static void gtk_default_draw_spinner (GtkStyle *style, + GdkWindow *window, + GtkStateType state_type, + guint step, + gint x, + gint y, + gint width, + gint height); static void rgb_to_hls (gdouble *r, gdouble *g, @@ -511,6 +519,7 @@ gtk_style_class_init (GtkStyleClass *klass) klass->draw_expander = gtk_default_draw_expander; klass->draw_layout = gtk_default_draw_layout; klass->draw_resize_grip = gtk_default_draw_resize_grip; + klass->draw_spinner = gtk_default_draw_spinner; g_type_class_add_private (object_class, sizeof (GtkStylePrivate)); @@ -1764,8 +1773,9 @@ gtk_style_get_style_property (GtkStyle *style, GtkRcPropertyParser parser; const GValue *peek_value; - klass = g_type_class_peek (widget_type); + klass = g_type_class_ref (widget_type); pspec = gtk_widget_class_find_style_property (klass, property_name); + g_type_class_unref (klass); if (!pspec) { @@ -5598,6 +5608,80 @@ gtk_default_draw_resize_grip (GtkStyle *style, } } +static void +gtk_default_draw_spinner (GtkStyle *style, + GdkWindow *window, + GtkStateType state_type, + guint step, + gint x, + gint y, + gint width, + gint height) +{ + GdkColor *color; + cairo_t *cr; + guint num_steps; + gdouble dx, dy; + gdouble radius; + gdouble half; + gint i; + guint real_step; + + gtk_style_get (style, GTK_TYPE_SPINNER, + "num-steps", &num_steps, + NULL); + real_step = step % num_steps; + + /* get cairo context */ + cr = gdk_cairo_create (window); + + /* set a clip region for the expose event */ + cairo_rectangle (cr, x, y, width, height); + cairo_clip (cr); + + cairo_translate (cr, x, y); + + /* draw clip region */ + cairo_set_operator (cr, CAIRO_OPERATOR_OVER); + + color = &style->fg[state_type]; + dx = width / 2; + dy = height / 2; + radius = MIN (width / 2, height / 2); + half = num_steps / 2; + + for (i = 0; i < num_steps; i++) + { + gint inset = 0.7 * radius; + + /* transparency is a function of time and intial value */ + gdouble t = (gdouble) ((i + num_steps - real_step) + % num_steps) / num_steps; + + cairo_save (cr); + + cairo_set_source_rgba (cr, + color->red / 65535., + color->green / 65535., + color->blue / 65535., + t); + + cairo_set_line_width (cr, 2.0); + cairo_move_to (cr, + dx + (radius - inset) * cos (i * G_PI / half), + dy + (radius - inset) * sin (i * G_PI / half)); + cairo_line_to (cr, + dx + radius * cos (i * G_PI / half), + dy + radius * sin (i * G_PI / half)); + cairo_stroke (cr); + + cairo_restore (cr); + } + + /* free memory */ + cairo_destroy (cr); +} + void _gtk_style_shade (const GdkColor *a, GdkColor *b, @@ -6632,6 +6716,38 @@ gtk_paint_resize_grip (GtkStyle *style, } /** + * gtk_paint_spinner: + * @style: a #GtkStyle + * @window: a #GdkWindow + * @state_type: a state + * @widget: the widget + * @step: the nth step, a value between 0 and GtkSpinner::num-steps + * @x: the x origin of the rectangle in which to draw the resize grip + * @y: the y origin of the rectangle in which to draw the resize grip + * @width: the width of the rectangle in which to draw the resize grip + * @height: the height of the rectangle in which to draw the resize grip + * + * Draws a spinner on @window using the given parameters. + */ +void +gtk_paint_spinner (GtkStyle *style, + GdkWindow *window, + GtkStateType state_type, + guint step, + gint x, + gint y, + gint width, + gint height) +{ + g_return_if_fail (GTK_IS_STYLE (style)); + g_return_if_fail (GTK_STYLE_GET_CLASS (style)->draw_spinner != NULL); + g_return_if_fail (style->depth == gdk_drawable_get_depth (window)); + + GTK_STYLE_GET_CLASS (style)->draw_spinner (style, window, state_type, + step, x, y, width, height); +} + +/** * gtk_border_new: * * Allocates a new #GtkBorder structure and initializes its elements to zero. diff --git a/gtk/gtkstyle.h b/gtk/gtkstyle.h index a6af1779c1..3e1401bcf4 100644 --- a/gtk/gtkstyle.h +++ b/gtk/gtkstyle.h @@ -403,6 +403,14 @@ struct _GtkStyleClass gint y, gint width, gint height); + void (*draw_spinner) (GtkStyle *style, + GdkWindow *window, + GtkStateType state_type, + guint step, + gint x, + gint y, + gint width, + gint height); /* Padding for future expansion */ void (*_gtk_reserved1) (void); @@ -416,7 +424,6 @@ struct _GtkStyleClass void (*_gtk_reserved9) (void); void (*_gtk_reserved10) (void); void (*_gtk_reserved11) (void); - void (*_gtk_reserved12) (void); }; struct _GtkBorder @@ -856,7 +863,14 @@ void gtk_paint_resize_grip (GtkStyle *style, gint y, gint width, gint height); - +void gtk_paint_spinner (GtkStyle *style, + GdkWindow *window, + GtkStateType state_type, + guint step, + gint x, + gint y, + gint width, + gint height); GType gtk_border_get_type (void) G_GNUC_CONST; GtkBorder *gtk_border_new (void) G_GNUC_MALLOC; diff --git a/gtk/gtktestutils.c b/gtk/gtktestutils.c index 383af9a7a1..64bf351976 100644 --- a/gtk/gtktestutils.c +++ b/gtk/gtktestutils.c @@ -21,6 +21,8 @@ /* need to get the prototypes of all get_type functions */ #define GTK_ENABLE_BROKEN #undef GTK_DISABLE_DEPRECATED +/* Need to get GDK_WINDOW_OBJECT */ +#undef GDK_DISABLE_DEPRECATED #include "config.h" diff --git a/gtk/gtktextbuffer.c b/gtk/gtktextbuffer.c index ceaa11344a..f82b6d875b 100644 --- a/gtk/gtktextbuffer.c +++ b/gtk/gtktextbuffer.c @@ -3807,11 +3807,7 @@ gtk_text_buffer_delete_selection (GtkTextBuffer *buffer, else { if (interactive) - { - gtk_text_buffer_begin_user_action (buffer); - gtk_text_buffer_delete_interactive (buffer, &start, &end, default_editable); - gtk_text_buffer_end_user_action (buffer); - } + gtk_text_buffer_delete_interactive (buffer, &start, &end, default_editable); else gtk_text_buffer_delete (buffer, &start, &end); @@ -3887,13 +3883,14 @@ gtk_text_buffer_backspace (GtkTextBuffer *buffer, if (gtk_text_buffer_delete_interactive (buffer, &start, &end, default_editable)) { - if (backspace_deletes_character) + /* special case \r\n, since we never want to reinsert \r */ + if (backspace_deletes_character && strcmp ("\r\n", cluster_text)) { gchar *normalized_text = g_utf8_normalize (cluster_text, strlen (cluster_text), G_NORMALIZE_NFD); glong len = g_utf8_strlen (normalized_text, -1); - + if (len > 1) gtk_text_buffer_insert_interactive (buffer, &start, diff --git a/gtk/gtktextlayout.c b/gtk/gtktextlayout.c index 319b8ce645..8c5d419b63 100644 --- a/gtk/gtktextlayout.c +++ b/gtk/gtktextlayout.c @@ -1812,56 +1812,59 @@ allocate_child_widgets (GtkTextLayout *text_layout, { PangoLayout *layout = display->layout; PangoLayoutIter *run_iter; - + run_iter = pango_layout_get_iter (layout); - do { PangoLayoutRun *run = pango_layout_iter_get_run_readonly (run_iter); - + if (run && is_shape (run)) { gint byte_index; GtkTextIter text_iter; - GtkTextChildAnchor *anchor = 0; - GList *widgets = 0; - - /* The pango iterator iterates in visual order. + GtkTextChildAnchor *anchor = NULL; + GList *widgets = NULL; + GList *l; + + /* The pango iterator iterates in visual order. * We use the byte index to find the child widget. */ - byte_index = pango_layout_iter_get_index (run_iter); line_display_index_to_iter (text_layout, display, &text_iter, byte_index, 0); anchor = gtk_text_iter_get_child_anchor (&text_iter); - widgets = gtk_text_child_anchor_get_widgets (anchor); - - if (widgets) + if (anchor) + widgets = gtk_text_child_anchor_get_widgets (anchor); + + for (l = widgets; l; l = l->next) { PangoRectangle extents; - GtkWidget *child = widgets->data; + GtkWidget *child = l->data; - /* We emit "allocate_child" with the x,y of - * the widget with respect to the top of the line - * and the left side of the buffer - */ - - pango_layout_iter_get_run_extents (run_iter, - NULL, - &extents); - - g_signal_emit (text_layout, - signals[ALLOCATE_CHILD], - 0, - child, - PANGO_PIXELS (extents.x) + display->x_offset, - PANGO_PIXELS (extents.y) + display->top_margin); - - g_list_free (widgets); + if (_gtk_anchored_child_get_layout (child) == text_layout) + { + + /* We emit "allocate_child" with the x,y of + * the widget with respect to the top of the line + * and the left side of the buffer + */ + pango_layout_iter_get_run_extents (run_iter, + NULL, + &extents); + + g_signal_emit (text_layout, + signals[ALLOCATE_CHILD], + 0, + child, + PANGO_PIXELS (extents.x) + display->x_offset, + PANGO_PIXELS (extents.y) + display->top_margin); + } } + + g_list_free (widgets); } } while (pango_layout_iter_next_run (run_iter)); - + pango_layout_iter_free (run_iter); } diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c index bf2d4b12b6..0c41e24c21 100644 --- a/gtk/gtktextview.c +++ b/gtk/gtktextview.c @@ -349,6 +349,7 @@ static GtkAdjustment* get_vadjustment (GtkTextView *text_view); static void gtk_text_view_do_popup (GtkTextView *text_view, GdkEventButton *event); +static void cancel_pending_scroll (GtkTextView *text_view); static void gtk_text_view_queue_scroll (GtkTextView *text_view, GtkTextMark *mark, gdouble within_margin, @@ -1412,16 +1413,21 @@ gtk_text_view_set_buffer (GtkTextView *text_view, g_signal_handlers_disconnect_by_func (text_view->buffer, gtk_text_view_paste_done_handler, text_view); - g_object_unref (text_view->buffer); - text_view->dnd_mark = NULL; - text_view->first_para_mark = NULL; if (GTK_WIDGET_REALIZED (text_view)) { GtkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (text_view), GDK_SELECTION_PRIMARY); gtk_text_buffer_remove_selection_clipboard (text_view->buffer, clipboard); - } + } + + if (text_view->layout) + gtk_text_layout_set_buffer (text_view->layout, NULL); + + g_object_unref (text_view->buffer); + text_view->dnd_mark = NULL; + text_view->first_para_mark = NULL; + cancel_pending_scroll (text_view); } text_view->buffer = buffer; @@ -2154,6 +2160,11 @@ gtk_text_view_scroll_to_mark (GtkTextView *text_view, g_return_if_fail (xalign >= 0.0 && xalign <= 1.0); g_return_if_fail (yalign >= 0.0 && yalign <= 1.0); + /* We need to verify that the buffer contains the mark, otherwise this + * can lead to data structure corruption later on. + */ + g_return_if_fail (get_buffer (text_view) == gtk_text_mark_get_buffer (mark)); + gtk_text_view_queue_scroll (text_view, mark, within_margin, use_align, @@ -2183,6 +2194,11 @@ gtk_text_view_scroll_mark_onscreen (GtkTextView *text_view, g_return_if_fail (GTK_IS_TEXT_VIEW (text_view)); g_return_if_fail (GTK_IS_TEXT_MARK (mark)); + /* We need to verify that the buffer contains the mark, otherwise this + * can lead to data structure corruption later on. + */ + g_return_if_fail (get_buffer (text_view) == gtk_text_mark_get_buffer (mark)); + gtk_text_view_scroll_to_mark (text_view, mark, 0.0, FALSE, 0.0, 0.0); } @@ -4314,7 +4330,8 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event) if (gtk_text_buffer_get_selection_bounds (get_buffer (text_view), &start, &end) && - gtk_text_iter_in_range (&iter, &start, &end)) + gtk_text_iter_in_range (&iter, &start, &end) && + !(event->state & GDK_SHIFT_MASK)) { text_view->drag_start_x = event->x; text_view->drag_start_y = event->y; @@ -7195,6 +7212,11 @@ gtk_text_view_value_changed (GtkAdjustment *adj, */ gtk_text_view_update_layout_width (text_view); + /* We also update the IM spot location here, since the IM context + * might do something that leads to validation. + */ + gtk_text_view_update_im_spot_location (text_view); + /* note that validation of onscreen could invoke this function * recursively, by scrolling to maintain first_para, or in response * to updating the layout width, however there is no problem with @@ -7229,6 +7251,9 @@ gtk_text_view_value_changed (GtkAdjustment *adj, text_view->first_validate_idle = 0; } + /* Finally we update the IM cursor location again, to ensure any + * changes made by the validation are pushed through. + */ gtk_text_view_update_im_spot_location (text_view); DV(g_print(">End scroll offset changed handler ("G_STRLOC")\n")); diff --git a/gtk/gtktoolbutton.c b/gtk/gtktoolbutton.c index c9e63f6a68..6eae655931 100644 --- a/gtk/gtktoolbutton.c +++ b/gtk/gtktoolbutton.c @@ -963,6 +963,8 @@ gtk_tool_button_set_label (GtkToolButton *button, const gchar *label) { gchar *old_label; + gchar *elided_label; + AtkObject *accessible; g_return_if_fail (GTK_IS_TOOL_BUTTON (button)); @@ -971,6 +973,14 @@ gtk_tool_button_set_label (GtkToolButton *button, button->priv->label_text = g_strdup (label); button->priv->contents_invalid = TRUE; + if (label) + { + elided_label = _gtk_toolbar_elide_underscores (label); + accessible = gtk_widget_get_accessible (GTK_WIDGET (button->priv->button)); + atk_object_set_name (accessible, elided_label); + g_free (elided_label); + } + g_free (old_label); g_object_notify (G_OBJECT (button), "label"); diff --git a/gtk/gtktoolitem.c b/gtk/gtktoolitem.c index 411cd1ca3a..8f7406a9a9 100644 --- a/gtk/gtktoolitem.c +++ b/gtk/gtktoolitem.c @@ -39,6 +39,22 @@ /** * SECTION:gtktoolitem * @short_description: The base class of widgets that can be added to GtkToolShell + * @see_also: <variablelist> + * <varlistentry> + * <term>#GtkToolbar</term> + * <listitem><para>The toolbar widget</para></listitem> + * </varlistentry> + * <varlistentry> + * <term>#GtkToolButton</term> + * <listitem><para>A subclass of #GtkToolItem that displays buttons on + * the toolbar</para></listitem> + * </varlistentry> + * <varlistentry> + * <term>#GtkSeparatorToolItem</term> + * <listitem><para>A subclass of #GtkToolItem that separates groups of + * items on a toolbar</para></listitem> + * </varlistentry> + * </variablelist> * * #GtkToolItem<!-- -->s are widgets that can appear on a toolbar. To * create a toolbar item that contain something else than a button, use diff --git a/gtk/gtktoolshell.c b/gtk/gtktoolshell.c index f7be68a523..ec1c10b5ca 100644 --- a/gtk/gtktoolshell.c +++ b/gtk/gtktoolshell.c @@ -29,6 +29,7 @@ /** * SECTION:gtktoolshell * @short_description: Interface for containers containing GtkToolItem widgets. + * @Title: GtkToolShell * * The #GtkToolShell interface allows container widgets to provide additional * information when embedding #GtkToolItem widgets. diff --git a/gtk/gtktooltip.c b/gtk/gtktooltip.c index 77e47772cd..1b628776f0 100644 --- a/gtk/gtktooltip.c +++ b/gtk/gtktooltip.c @@ -84,7 +84,7 @@ struct _GtkTooltipClass static void gtk_tooltip_class_init (GtkTooltipClass *klass); static void gtk_tooltip_init (GtkTooltip *tooltip); -static void gtk_tooltip_finalize (GObject *object); +static void gtk_tooltip_dispose (GObject *object); static void gtk_tooltip_window_style_set (GtkTooltip *tooltip); static gboolean gtk_tooltip_paint_window (GtkTooltip *tooltip); @@ -106,7 +106,7 @@ gtk_tooltip_class_init (GtkTooltipClass *klass) object_class = G_OBJECT_CLASS (klass); - object_class->finalize = gtk_tooltip_finalize; + object_class->dispose = gtk_tooltip_dispose; } static void @@ -166,7 +166,7 @@ gtk_tooltip_init (GtkTooltip *tooltip) } static void -gtk_tooltip_finalize (GObject *object) +gtk_tooltip_dispose (GObject *object) { GtkTooltip *tooltip = GTK_TOOLTIP (object); @@ -194,9 +194,10 @@ gtk_tooltip_finalize (GObject *object) gtk_tooltip_display_closed, tooltip); gtk_widget_destroy (tooltip->window); + tooltip->window = NULL; } - G_OBJECT_CLASS (gtk_tooltip_parent_class)->finalize (object); + G_OBJECT_CLASS (gtk_tooltip_parent_class)->dispose (object); } /* public API */ @@ -317,9 +318,9 @@ gtk_tooltip_set_icon_from_stock (GtkTooltip *tooltip, * Since: 2.14 */ void -gtk_tooltip_set_icon_from_icon_name(GtkTooltip *tooltip, - const gchar *icon_name, - GtkIconSize size) +gtk_tooltip_set_icon_from_icon_name (GtkTooltip *tooltip, + const gchar *icon_name, + GtkIconSize size) { g_return_if_fail (GTK_IS_TOOLTIP (tooltip)); @@ -332,6 +333,32 @@ gtk_tooltip_set_icon_from_icon_name(GtkTooltip *tooltip, } /** + * gtk_tooltip_set_from_gicon: + * @tooltip: a #GtkTooltip + * @gicon: a #GIcon representing the icon, or %NULL + * @size: a stock icon size + * Sets the icon of the tooltip (which is in front of the text) to be + * the icon indicated by @gicon with the size indicated + * by @size. If @icon_name is %NULL, the image will be hidden. + * + * Since: 2.20 + */ +void +gtk_tooltip_set_icon_from_gicon (GtkTooltip *tooltip, + GIcon *gicon, + GtkIconSize size) +{ + g_return_if_fail (GTK_IS_TOOLTIP (tooltip)); + + gtk_image_set_from_gicon (GTK_IMAGE (tooltip->image), gicon, size); + + if (gicon) + gtk_widget_show (tooltip->image); + else + gtk_widget_hide (tooltip->image); +} + +/** * gtk_tooltip_set_custom: * @tooltip: a #GtkTooltip * @custom_widget: a #GtkWidget, or %NULL to unset the old custom widget. @@ -474,6 +501,8 @@ gtk_tooltip_window_style_set (GtkTooltip *tooltip) tooltip->window->style->ythickness, tooltip->window->style->xthickness, tooltip->window->style->xthickness); + gtk_box_set_spacing (GTK_BOX (tooltip->box), + tooltip->window->style->xthickness); gtk_widget_queue_draw (tooltip->window); } @@ -767,6 +796,9 @@ static void gtk_tooltip_set_last_window (GtkTooltip *tooltip, GdkWindow *window) { + if (tooltip->last_window == window) + return; + if (tooltip->last_window) g_object_remove_weak_pointer (G_OBJECT (tooltip->last_window), (gpointer *) &tooltip->last_window); @@ -945,8 +977,6 @@ gtk_tooltip_show_tooltip (GdkDisplay *display) g_object_get (tooltip_widget, "has-tooltip", &has_tooltip, NULL); - g_assert (tooltip != NULL); - return_value = gtk_tooltip_run_requery (&tooltip_widget, tooltip, &x, &y); if (!return_value) return; @@ -1048,11 +1078,17 @@ tooltip_popup_timeout (gpointer data) GtkTooltip *tooltip; display = GDK_DISPLAY_OBJECT (data); + tooltip = g_object_get_data (G_OBJECT (display), + "gdk-display-current-tooltip"); + + /* This usually does not happen. However, it does occur in language + * bindings were reference counting of objects behaves differently. + */ + if (!tooltip) + return FALSE; gtk_tooltip_show_tooltip (display); - tooltip = g_object_get_data (G_OBJECT (display), - "gdk-display-current-tooltip"); tooltip->timeout_id = 0; return FALSE; @@ -1068,7 +1104,7 @@ gtk_tooltip_start_delay (GdkDisplay *display) tooltip = g_object_get_data (G_OBJECT (display), "gdk-display-current-tooltip"); - if (tooltip && GTK_TOOLTIP_VISIBLE (tooltip)) + if (!tooltip || GTK_TOOLTIP_VISIBLE (tooltip)) return; if (tooltip->timeout_id) diff --git a/gtk/gtktooltip.h b/gtk/gtktooltip.h index 8039da6fbc..5271376798 100644 --- a/gtk/gtktooltip.h +++ b/gtk/gtktooltip.h @@ -48,6 +48,9 @@ void gtk_tooltip_set_icon_from_stock (GtkTooltip *tooltip, void gtk_tooltip_set_icon_from_icon_name (GtkTooltip *tooltip, const gchar *icon_name, GtkIconSize size); +void gtk_tooltip_set_icon_from_gicon (GtkTooltip *tooltip, + GIcon *gicon, + GtkIconSize size); void gtk_tooltip_set_custom (GtkTooltip *tooltip, GtkWidget *custom_widget); diff --git a/gtk/gtktreemodelfilter.c b/gtk/gtktreemodelfilter.c index cc5b86ebba..074a7a4fa1 100644 --- a/gtk/gtktreemodelfilter.c +++ b/gtk/gtktreemodelfilter.c @@ -73,7 +73,7 @@ struct _FilterLevel gint ref_count; gint visible_nodes; - FilterElt *parent_elt; + gint parent_elt_index; FilterLevel *parent_level; }; @@ -129,6 +129,9 @@ enum #define FILTER_ELT(filter_elt) ((FilterElt *)filter_elt) #define FILTER_LEVEL(filter_level) ((FilterLevel *)filter_level) +#define FILTER_LEVEL_PARENT_ELT(level) (&g_array_index (FILTER_LEVEL ((level))->parent_level->array, FilterElt, FILTER_LEVEL ((level))->parent_elt_index)) +#define FILTER_LEVEL_ELT_INDEX(level, elt) (FILTER_ELT ((elt)) - FILTER_ELT (FILTER_LEVEL ((level))->array->data)) + /* general code (object/interface init, properties, etc) */ static void gtk_tree_model_filter_tree_model_init (GtkTreeModelIface *iface); static void gtk_tree_model_filter_drag_source_init (GtkTreeDragSourceIface *iface); @@ -214,7 +217,7 @@ static gboolean gtk_tree_model_filter_drag_data_delete (GtkTr /* private functions */ static void gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter, FilterLevel *parent_level, - FilterElt *parent_elt, + gint parent_elt_index, gboolean emit_inserted); static void gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter, @@ -257,6 +260,8 @@ static GtkTreePath *gtk_real_tree_model_filter_convert_child_path_to_path (GtkTr static FilterElt *gtk_tree_model_filter_get_nth (GtkTreeModelFilter *filter, FilterLevel *level, int n); +static gboolean gtk_tree_model_filter_elt_is_visible_in_target (FilterLevel *level, + FilterElt *elt); static FilterElt *gtk_tree_model_filter_get_nth_visible (GtkTreeModelFilter *filter, FilterLevel *level, int n); @@ -436,12 +441,13 @@ gtk_tree_model_filter_get_property (GObject *object, static void gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter, FilterLevel *parent_level, - FilterElt *parent_elt, + gint parent_elt_index, gboolean emit_inserted) { GtkTreeIter iter; GtkTreeIter first_node; GtkTreeIter root; + FilterElt *parent_elt = NULL; FilterLevel *new_level; gint length = 0; gint i; @@ -474,6 +480,8 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter, GtkTreeIter parent_iter; GtkTreeIter child_parent_iter; + parent_elt = &g_array_index (parent_level->array, FilterElt, parent_elt_index); + parent_iter.stamp = filter->priv->stamp; parent_iter.user_data = parent_level; parent_iter.user_data2 = parent_elt; @@ -499,10 +507,10 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter, length); new_level->ref_count = 0; new_level->visible_nodes = 0; - new_level->parent_elt = parent_elt; + new_level->parent_elt_index = parent_elt_index; new_level->parent_level = parent_level; - if (parent_elt) + if (parent_elt_index >= 0) parent_elt->children = new_level; else filter->priv->root = new_level; @@ -510,9 +518,9 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter, /* increase the count of zero ref_counts */ while (parent_level) { - parent_elt->zero_ref_count++; + g_array_index (parent_level->array, FilterElt, parent_elt_index).zero_ref_count++; - parent_elt = parent_level->parent_elt; + parent_elt_index = parent_level->parent_elt_index; parent_level = parent_level->parent_level; } if (new_level != filter->priv->root) @@ -526,6 +534,7 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter, { if (gtk_tree_model_filter_visible (filter, &iter)) { + GtkTreeIter f_iter; FilterElt filter_elt; filter_elt.offset = i; @@ -540,26 +549,29 @@ gtk_tree_model_filter_build_level (GtkTreeModelFilter *filter, g_array_append_val (new_level->array, filter_elt); new_level->visible_nodes++; - if (new_level->parent_level || filter->priv->virtual_root) - { - GtkTreeIter f_iter; - - f_iter.stamp = filter->priv->stamp; - f_iter.user_data = new_level; - f_iter.user_data2 = &(g_array_index (new_level->array, FilterElt, new_level->array->len - 1)); - - gtk_tree_model_filter_ref_node (GTK_TREE_MODEL (filter), &f_iter); + f_iter.stamp = filter->priv->stamp; + f_iter.user_data = new_level; + f_iter.user_data2 = &(g_array_index (new_level->array, FilterElt, new_level->array->len - 1)); - if (emit_inserted) - { - GtkTreePath *f_path; + if (new_level->parent_level || filter->priv->virtual_root) + gtk_tree_model_filter_ref_node (GTK_TREE_MODEL (filter), &f_iter); - f_path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), - &f_iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), - f_path, &f_iter); - gtk_tree_path_free (f_path); - } + if (emit_inserted) + { + GtkTreePath *f_path; + GtkTreeIter children; + + f_path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), + &f_iter); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), + f_path, &f_iter); + gtk_tree_path_free (f_path); + + if (gtk_tree_model_iter_children (filter->priv->child_model, + &children, &iter)) + gtk_tree_model_filter_update_children (filter, + new_level, + FILTER_ELT (f_iter.user_data2)); } } i++; @@ -626,13 +638,13 @@ gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter, if (filter_level->ref_count == 0) { FilterLevel *parent_level = filter_level->parent_level; - FilterElt *parent_elt = filter_level->parent_elt; + gint parent_elt_index = filter_level->parent_elt_index; while (parent_level) { - parent_elt->zero_ref_count--; + g_array_index (parent_level->array, FilterElt, parent_elt_index).zero_ref_count--; - parent_elt = parent_level->parent_elt; + parent_elt_index = parent_level->parent_elt_index; parent_level = parent_level->parent_level; } @@ -640,8 +652,8 @@ gtk_tree_model_filter_free_level (GtkTreeModelFilter *filter, filter->priv->zero_ref_count--; } - if (filter_level->parent_elt) - filter_level->parent_elt->children = NULL; + if (filter_level->parent_elt_index >= 0) + FILTER_LEVEL_PARENT_ELT (filter_level)->children = NULL; else filter->priv->root = NULL; @@ -672,7 +684,10 @@ gtk_tree_model_filter_elt_get_path (FilterLevel *level, { gtk_tree_path_prepend_index (path, walker2->offset); - walker2 = walker->parent_elt; + if (!walker->parent_level) + break; + + walker2 = FILTER_LEVEL_PARENT_ELT (walker); walker = walker->parent_level; } @@ -804,6 +819,32 @@ gtk_tree_model_filter_get_nth (GtkTreeModelFilter *filter, return &g_array_index (level->array, FilterElt, n); } +static gboolean +gtk_tree_model_filter_elt_is_visible_in_target (FilterLevel *level, + FilterElt *elt) +{ + gint elt_index; + + if (!elt->visible) + return FALSE; + + if (level->parent_elt_index == -1) + return TRUE; + + do + { + elt_index = level->parent_elt_index; + level = level->parent_level; + + if (elt_index >= 0 + && !g_array_index (level->array, FilterElt, elt_index).visible) + return FALSE; + } + while (level); + + return TRUE; +} + static FilterElt * gtk_tree_model_filter_get_nth_visible (GtkTreeModelFilter *filter, FilterLevel *level, @@ -848,11 +889,11 @@ gtk_tree_model_filter_fetch_child (GtkTreeModelFilter *filter, FilterElt elt; /* check if child exists and is visible */ - if (level->parent_elt) + if (level->parent_elt_index >= 0) { c_parent_path = gtk_tree_model_filter_elt_get_path (level->parent_level, - level->parent_elt, + FILTER_LEVEL_PARENT_ELT (level), filter->priv->virtual_root); if (!c_parent_path) return NULL; @@ -931,7 +972,7 @@ gtk_tree_model_filter_fetch_child (GtkTreeModelFilter *filter, { FilterElt *e = &(g_array_index (level->array, FilterElt, i)); if (e->children) - e->children->parent_elt = e; + e->children->parent_elt_index = i; } c_iter.stamp = filter->priv->stamp; @@ -950,14 +991,18 @@ gtk_tree_model_filter_remove_node (GtkTreeModelFilter *filter, { FilterElt *elt, *parent; FilterLevel *level, *parent_level; - gint i, length; + gint i, length, parent_elt_index; gboolean emit_child_toggled = FALSE; level = FILTER_LEVEL (iter->user_data); elt = FILTER_ELT (iter->user_data2); - parent = level->parent_elt; + parent_elt_index = level->parent_elt_index; + if (parent_elt_index >= 0) + parent = FILTER_LEVEL_PARENT_ELT (level); + else + parent = NULL; parent_level = level->parent_level; length = level->array->len; @@ -972,7 +1017,10 @@ gtk_tree_model_filter_remove_node (GtkTreeModelFilter *filter, * if level != root level and visible nodes == 0, emit row-has-child-toggled. */ - if (level != filter->priv->root && level->visible_nodes == 0) + if (level != filter->priv->root + && level->visible_nodes == 0 + && parent + && parent->visible) emit_child_toggled = TRUE; if (length > 1) @@ -980,9 +1028,13 @@ gtk_tree_model_filter_remove_node (GtkTreeModelFilter *filter, GtkTreePath *path; FilterElt *tmp; - /* we emit row-deleted, and remove the node from the cache. + /* We emit row-deleted, and remove the node from the cache. + * If it has any children, these will be removed here as well. */ + if (elt->children) + gtk_tree_model_filter_free_level (filter, elt->children); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), iter); elt->visible = FALSE; gtk_tree_model_filter_increment_stamp (filter); @@ -1015,7 +1067,7 @@ gtk_tree_model_filter_remove_node (GtkTreeModelFilter *filter, */ elt = &g_array_index (level->array, FilterElt, i); if (elt->children) - elt->children->parent_elt = elt; + elt->children->parent_elt_index = i; } } } @@ -1024,10 +1076,16 @@ gtk_tree_model_filter_remove_node (GtkTreeModelFilter *filter, { GtkTreePath *path; - /* we emit row-deleted, but keep the node in the cache and - * referenced. + /* We emit row-deleted, but keep the node in the cache and + * referenced. Its children will be removed. */ + if (elt->children) + { + gtk_tree_model_filter_free_level (filter, elt->children); + elt->children = NULL; + } + path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), iter); elt->visible = FALSE; gtk_tree_model_filter_increment_stamp (filter); @@ -1038,7 +1096,7 @@ gtk_tree_model_filter_remove_node (GtkTreeModelFilter *filter, { GtkTreePath *path; - /* blow level away */ + /* Blow level away, including any child levels */ path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), iter); elt->visible = FALSE; @@ -1170,6 +1228,7 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model, gboolean requested_state; gboolean current_state; gboolean free_c_path = FALSE; + gboolean signals_emitted = FALSE; g_return_if_fail (c_path != NULL || c_iter != NULL); @@ -1230,14 +1289,18 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model, */ gtk_tree_path_free (path); path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter); - gtk_tree_model_row_changed (GTK_TREE_MODEL (filter), path, &iter); level = FILTER_LEVEL (iter.user_data); elt = FILTER_ELT (iter.user_data2); - /* and update the children */ - if (gtk_tree_model_iter_children (c_model, &children, &real_c_iter)) - gtk_tree_model_filter_update_children (filter, level, elt); + if (gtk_tree_model_filter_elt_is_visible_in_target (level, elt)) + { + gtk_tree_model_row_changed (GTK_TREE_MODEL (filter), path, &iter); + + /* and update the children */ + if (gtk_tree_model_iter_children (c_model, &children, &real_c_iter)) + gtk_tree_model_filter_update_children (filter, level, elt); + } goto done; } @@ -1250,27 +1313,29 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model, /* make sure the new item has been pulled in */ if (!filter->priv->root) { - gint i; FilterLevel *root; - gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); + gtk_tree_model_filter_build_level (filter, NULL, -1, TRUE); - root = FILTER_LEVEL (filter->priv->root); + /* We will only proceed below if the item is found. If the item + * is found, we can be sure row-inserted has just been emitted + * for it. + */ + signals_emitted = TRUE; - if (root) - { - for (i = 0; i < root->array->len; i++) - g_array_index (root->array, FilterElt, i).visible = FALSE; - root->visible_nodes = 0; - } + root = FILTER_LEVEL (filter->priv->root); } gtk_tree_model_filter_increment_stamp (filter); + /* We need to allow to build new levels, because we are then pulling + * in a child in an invisible level. We only want to find path if it + * is in a visible level (and thus has a parent that is visible). + */ if (!path) path = gtk_real_tree_model_filter_convert_child_path_to_path (filter, c_path, - TRUE, + FALSE, TRUE); if (!path) @@ -1289,15 +1354,32 @@ gtk_tree_model_filter_row_changed (GtkTreeModel *c_model, level->visible_nodes++; } - if ((level->parent_elt && level->parent_elt->visible) || !level->parent_elt) + if (gtk_tree_model_filter_elt_is_visible_in_target (level, elt)) { /* visibility changed -- reget path */ gtk_tree_path_free (path); path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter); - gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), path, &iter); + if (!signals_emitted) + gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), path, &iter); + + if (level->parent_level && level->visible_nodes == 1) + { + /* We know that this is the first visible node in this level, so + * we need to emit row-has-child-toggled on the parent. This + * does not apply to the root level. + */ + + gtk_tree_path_up (path); + gtk_tree_model_get_iter (GTK_TREE_MODEL (filter), &iter, path); + + gtk_tree_model_row_has_child_toggled (GTK_TREE_MODEL (filter), + path, + &iter); + } - if (gtk_tree_model_iter_children (c_model, &children, c_iter)) + if (!signals_emitted + && gtk_tree_model_iter_children (c_model, &children, c_iter)) gtk_tree_model_filter_update_children (filter, level, elt); } @@ -1379,7 +1461,7 @@ gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model, goto done; /* build level will pull in the new child */ - gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); + gtk_tree_model_filter_build_level (filter, NULL, -1, FALSE); if (filter->priv->root && FILTER_LEVEL (filter->priv->root)->visible_nodes) @@ -1506,7 +1588,7 @@ gtk_tree_model_filter_row_inserted (GtkTreeModel *c_model, { FilterElt *e = &g_array_index (level->array, FilterElt, i); if (e->children) - e->children->parent_elt = e; + e->children->parent_elt_index = i; } /* don't emit the signal if we aren't visible */ @@ -1555,6 +1637,7 @@ gtk_tree_model_filter_row_has_child_toggled (GtkTreeModel *c_model, GtkTreeIter iter; FilterLevel *level; FilterElt *elt; + gboolean requested_state; g_return_if_fail (c_path != NULL && c_iter != NULL); @@ -1564,12 +1647,13 @@ gtk_tree_model_filter_row_has_child_toggled (GtkTreeModel *c_model, if (filter->priv->virtual_root && !filter->priv->root && !gtk_tree_path_compare (c_path, filter->priv->virtual_root)) { - gtk_tree_model_filter_build_level (filter, NULL, NULL, TRUE); + gtk_tree_model_filter_build_level (filter, NULL, -1, TRUE); return; } - if (!gtk_tree_model_filter_visible (filter, c_iter)) - return; + /* For all other levels, there is a chance that the visibility state + * of the parent has changed now. + */ path = gtk_real_tree_model_filter_convert_child_path_to_path (filter, c_path, @@ -1580,18 +1664,60 @@ gtk_tree_model_filter_row_has_child_toggled (GtkTreeModel *c_model, gtk_tree_model_filter_get_iter_full (GTK_TREE_MODEL (data), &iter, path); - gtk_tree_path_free (path); - level = FILTER_LEVEL (iter.user_data); elt = FILTER_ELT (iter.user_data2); - g_assert (elt->visible); + gtk_tree_path_free (path); + + requested_state = gtk_tree_model_filter_visible (filter, c_iter); + + if (!elt->visible && !requested_state) + { + /* The parent node currently is not visible and will not become + * visible, so we will not pass on the row-has-child-toggled event. + */ + return; + } + else if (elt->visible && !requested_state) + { + /* The node is no longer visible, so it has to be removed. + * _remove_node() takes care of emitting row-has-child-toggled + * when required. + */ + level->visible_nodes--; + + gtk_tree_model_filter_remove_node (filter, &iter); + + return; + } + else if (!elt->visible && requested_state) + { + elt->visible = TRUE; + level->visible_nodes++; + + /* Only insert if the parent is visible in the target */ + if (gtk_tree_model_filter_elt_is_visible_in_target (level, elt)) + { + path = gtk_tree_model_get_path (GTK_TREE_MODEL (filter), &iter); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (filter), path, &iter); + gtk_tree_path_free (path); + + /* We do not update children now, because that will happen + * below. + */ + } + } + /* For the remaining possibility, elt->visible && requested_state + * no action is required. + */ /* If this node is referenced and has children, build the level so we * can monitor it for changes. */ if (elt->ref_count > 1 && gtk_tree_model_iter_has_child (c_model, c_iter)) - gtk_tree_model_filter_build_level (filter, level, elt, TRUE); + gtk_tree_model_filter_build_level (filter, level, + FILTER_LEVEL_ELT_INDEX (level, elt), + TRUE); /* get a path taking only visible nodes into account */ path = gtk_tree_model_get_path (GTK_TREE_MODEL (data), &iter); @@ -1607,11 +1733,12 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model, GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER (data); GtkTreePath *path; GtkTreeIter iter; - FilterElt *elt, *parent = NULL; + FilterElt *elt; FilterLevel *level, *parent_level = NULL; gboolean emit_child_toggled = FALSE; gint offset; gint i; + gint parent_elt_index = -1; g_return_if_fail (c_path != NULL); @@ -1749,7 +1876,7 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model, if (elt->offset > offset) elt->offset--; if (elt->children) - elt->children->parent_elt = elt; + elt->children->parent_elt_index = i; } return; @@ -1774,7 +1901,7 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model, { emit_child_toggled = TRUE; parent_level = level->parent_level; - parent = level->parent_elt; + parent_elt_index = level->parent_elt_index; } /* emit row_deleted */ @@ -1819,7 +1946,7 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model, if (elt->offset > offset) elt->offset--; if (elt->children) - elt->children->parent_elt = elt; + elt->children->parent_elt_index = i; } } @@ -1830,7 +1957,7 @@ gtk_tree_model_filter_row_deleted (GtkTreeModel *c_model, iter.stamp = filter->priv->stamp; iter.user_data = parent_level; - iter.user_data2 = parent; + iter.user_data2 = &g_array_index (parent_level->array, FilterElt, parent_elt_index); /* We set in_row_deleted to TRUE to avoid a level build triggered * by row-has-child-toggled (parent model could call iter_has_child @@ -2013,7 +2140,7 @@ gtk_tree_model_filter_rows_reordered (GtkTreeModel *c_model, { FilterElt *e = &g_array_index (level->array, FilterElt, i); if (e->children) - e->children->parent_elt = e; + e->children->parent_elt_index = i; } /* emit rows_reordered */ @@ -2114,7 +2241,7 @@ gtk_tree_model_filter_get_iter_full (GtkTreeModel *model, indices = gtk_tree_path_get_indices (path); if (filter->priv->root == NULL) - gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); + gtk_tree_model_filter_build_level (filter, NULL, -1, FALSE); level = FILTER_LEVEL (filter->priv->root); depth = gtk_tree_path_get_depth (path); @@ -2134,7 +2261,9 @@ gtk_tree_model_filter_get_iter_full (GtkTreeModel *model, elt = gtk_tree_model_filter_get_nth (filter, level, indices[i]); if (!elt->children) - gtk_tree_model_filter_build_level (filter, level, elt, FALSE); + gtk_tree_model_filter_build_level (filter, level, + FILTER_LEVEL_ELT_INDEX (level, elt), + FALSE); level = elt->children; } @@ -2169,7 +2298,7 @@ gtk_tree_model_filter_get_iter (GtkTreeModel *model, indices = gtk_tree_path_get_indices (path); if (filter->priv->root == NULL) - gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); + gtk_tree_model_filter_build_level (filter, NULL, -1, FALSE); level = FILTER_LEVEL (filter->priv->root); depth = gtk_tree_path_get_depth (path); @@ -2189,7 +2318,9 @@ gtk_tree_model_filter_get_iter (GtkTreeModel *model, elt = gtk_tree_model_filter_get_nth_visible (filter, level, indices[i]); if (!elt->children) - gtk_tree_model_filter_build_level (filter, level, elt, FALSE); + gtk_tree_model_filter_build_level (filter, level, + FILTER_LEVEL_ELT_INDEX (level, elt), + FALSE); level = elt->children; } @@ -2216,6 +2347,7 @@ gtk_tree_model_filter_get_path (GtkTreeModel *model, GtkTreePath *retval; FilterLevel *level; FilterElt *elt; + gint elt_index; g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), NULL); g_return_val_if_fail (GTK_TREE_MODEL_FILTER (model)->priv->child_model != NULL, NULL); @@ -2223,6 +2355,7 @@ gtk_tree_model_filter_get_path (GtkTreeModel *model, level = iter->user_data; elt = iter->user_data2; + elt_index = FILTER_LEVEL_ELT_INDEX (level, elt); if (!elt->visible) return NULL; @@ -2233,7 +2366,7 @@ gtk_tree_model_filter_get_path (GtkTreeModel *model, { int i = 0, index = 0; - while (&g_array_index (level->array, FilterElt, i) != elt) + while (i < elt_index) { if (g_array_index (level->array, FilterElt, i).visible) index++; @@ -2243,7 +2376,7 @@ gtk_tree_model_filter_get_path (GtkTreeModel *model, } gtk_tree_path_prepend_index (retval, index); - elt = level->parent_elt; + elt_index = level->parent_elt_index; level = level->parent_level; } @@ -2336,7 +2469,7 @@ gtk_tree_model_filter_iter_children (GtkTreeModel *model, int i = 0; if (!filter->priv->root) - gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); + gtk_tree_model_filter_build_level (filter, NULL, -1, FALSE); if (!filter->priv->root) return FALSE; @@ -2366,20 +2499,24 @@ gtk_tree_model_filter_iter_children (GtkTreeModel *model, else { int i = 0; + FilterElt *elt; + + elt = FILTER_ELT (parent->user_data2); - if (FILTER_ELT (parent->user_data2)->children == NULL) + if (elt->children == NULL) gtk_tree_model_filter_build_level (filter, FILTER_LEVEL (parent->user_data), - FILTER_ELT (parent->user_data2), + FILTER_LEVEL_ELT_INDEX (parent->user_data, elt), FALSE); - if (FILTER_ELT (parent->user_data2)->children == NULL) + + if (elt->children == NULL) return FALSE; - if (FILTER_ELT (parent->user_data2)->children->visible_nodes <= 0) + if (elt->children->visible_nodes <= 0) return FALSE; iter->stamp = filter->priv->stamp; - iter->user_data = FILTER_ELT (parent->user_data2)->children; + iter->user_data = elt->children; level = FILTER_LEVEL (iter->user_data); @@ -2429,7 +2566,8 @@ gtk_tree_model_filter_iter_has_child (GtkTreeModel *model, if (!elt->children && gtk_tree_model_iter_has_child (filter->priv->child_model, &child_iter)) gtk_tree_model_filter_build_level (filter, FILTER_LEVEL (iter->user_data), - elt, FALSE); + FILTER_LEVEL_ELT_INDEX (iter->user_data, elt), + FALSE); if (elt->children && elt->children->visible_nodes > 0) return TRUE; @@ -2453,7 +2591,7 @@ gtk_tree_model_filter_iter_n_children (GtkTreeModel *model, if (!iter) { if (!filter->priv->root) - gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); + gtk_tree_model_filter_build_level (filter, NULL, -1, FALSE); if (filter->priv->root) return FILTER_LEVEL (filter->priv->root)->visible_nodes; @@ -2472,7 +2610,8 @@ gtk_tree_model_filter_iter_n_children (GtkTreeModel *model, gtk_tree_model_iter_has_child (filter->priv->child_model, &child_iter)) gtk_tree_model_filter_build_level (filter, FILTER_LEVEL (iter->user_data), - elt, FALSE); + FILTER_LEVEL_ELT_INDEX (iter->user_data, elt), + FALSE); if (elt->children) return elt->children->visible_nodes; @@ -2538,7 +2677,7 @@ gtk_tree_model_filter_iter_parent (GtkTreeModel *model, { iter->stamp = GTK_TREE_MODEL_FILTER (model)->priv->stamp; iter->user_data = level->parent_level; - iter->user_data2 = level->parent_elt; + iter->user_data2 = FILTER_LEVEL_PARENT_ELT (level); return TRUE; } @@ -2571,14 +2710,14 @@ gtk_tree_model_filter_ref_node (GtkTreeModel *model, if (level->ref_count == 1) { FilterLevel *parent_level = level->parent_level; - FilterElt *parent_elt = level->parent_elt; + gint parent_elt_index = level->parent_elt_index; /* we were at zero -- time to decrease the zero_ref_count val */ while (parent_level) { - parent_elt->zero_ref_count--; + g_array_index (parent_level->array, FilterElt, parent_elt_index).zero_ref_count--; - parent_elt = parent_level->parent_elt; + parent_elt_index = parent_level->parent_elt_index; parent_level = parent_level->parent_level; } @@ -2624,14 +2763,14 @@ gtk_tree_model_filter_real_unref_node (GtkTreeModel *model, if (level->ref_count == 0) { FilterLevel *parent_level = level->parent_level; - FilterElt *parent_elt = level->parent_elt; + gint parent_elt_index = level->parent_elt_index; /* we are at zero -- time to increase the zero_ref_count val */ while (parent_level) { - parent_elt->zero_ref_count++; + g_array_index (parent_level->array, FilterElt, parent_elt_index).zero_ref_count++; - parent_elt = parent_level->parent_elt; + parent_elt_index = parent_level->parent_elt_index; parent_level = parent_level->parent_level; } @@ -3037,6 +3176,7 @@ gtk_tree_model_filter_convert_child_iter_to_iter (GtkTreeModelFilter *filter, g_return_val_if_fail (filter->priv->child_model != NULL, FALSE); g_return_val_if_fail (filter_iter != NULL, FALSE); g_return_val_if_fail (child_iter != NULL, FALSE); + g_return_val_if_fail (filter_iter != child_iter, FALSE); filter_iter->stamp = 0; @@ -3076,6 +3216,7 @@ gtk_tree_model_filter_convert_iter_to_child_iter (GtkTreeModelFilter *filter, g_return_if_fail (child_iter != NULL); g_return_if_fail (filter_iter != NULL); g_return_if_fail (filter_iter->stamp == filter->priv->stamp); + g_return_if_fail (filter_iter != child_iter); if (GTK_TREE_MODEL_FILTER_CACHE_CHILD_ITERS (filter)) { @@ -3124,7 +3265,7 @@ gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filte child_indices = gtk_tree_path_get_indices (real_path); if (filter->priv->root == NULL && build_levels) - gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); + gtk_tree_model_filter_build_level (filter, NULL, -1, FALSE); level = FILTER_LEVEL (filter->priv->root); for (i = 0; i < gtk_tree_path_get_depth (real_path); i++) @@ -3144,7 +3285,9 @@ gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filte { gtk_tree_path_append_index (retval, j); if (!tmp->children && build_levels) - gtk_tree_model_filter_build_level (filter, level, tmp, FALSE); + gtk_tree_model_filter_build_level (filter, level, + FILTER_LEVEL_ELT_INDEX (level, tmp), + FALSE); level = tmp->children; found_child = TRUE; } @@ -3166,7 +3309,9 @@ gtk_real_tree_model_filter_convert_child_path_to_path (GtkTreeModelFilter *filte gtk_tree_path_append_index (retval, j); if (!tmp->children && build_levels) - gtk_tree_model_filter_build_level (filter, level, tmp, FALSE); + gtk_tree_model_filter_build_level (filter, level, + FILTER_LEVEL_ELT_INDEX (level, tmp), + FALSE); level = tmp->children; found_child = TRUE; } @@ -3258,7 +3403,7 @@ gtk_tree_model_filter_convert_path_to_child_path (GtkTreeModelFilter *filter, retval = gtk_tree_path_new (); filter_indices = gtk_tree_path_get_indices (filter_path); if (!filter->priv->root) - gtk_tree_model_filter_build_level (filter, NULL, NULL, FALSE); + gtk_tree_model_filter_build_level (filter, NULL, -1, FALSE); level = FILTER_LEVEL (filter->priv->root); for (i = 0; i < gtk_tree_path_get_depth (filter_path); i++) @@ -3275,7 +3420,9 @@ gtk_tree_model_filter_convert_path_to_child_path (GtkTreeModelFilter *filter, filter_indices[i]); if (elt->children == NULL) - gtk_tree_model_filter_build_level (filter, level, elt, FALSE); + gtk_tree_model_filter_build_level (filter, level, + FILTER_LEVEL_ELT_INDEX (level, elt), + FALSE); if (!level || level->visible_nodes <= filter_indices[i]) { diff --git a/gtk/gtktreemodelsort.c b/gtk/gtktreemodelsort.c index 1dbdda41f8..f07795cc23 100644 --- a/gtk/gtktreemodelsort.c +++ b/gtk/gtktreemodelsort.c @@ -67,7 +67,7 @@ struct _SortLevel { GArray *array; gint ref_count; - SortElt *parent_elt; + gint parent_elt_index; SortLevel *parent_level; }; @@ -101,6 +101,10 @@ enum { #define SORT_ELT(sort_elt) ((SortElt *)sort_elt) #define SORT_LEVEL(sort_level) ((SortLevel *)sort_level) +#define SORT_LEVEL_PARENT_ELT(level) (&g_array_index (SORT_LEVEL ((level))->parent_level->array, SortElt, SORT_LEVEL ((level))->parent_elt_index)) +#define SORT_LEVEL_ELT_INDEX(level, elt) (SORT_ELT ((elt)) - SORT_ELT (SORT_LEVEL ((level))->array->data)) + + #define GET_CHILD_ITER(tree_model_sort,ch_iter,so_iter) gtk_tree_model_sort_convert_iter_to_child_iter((GtkTreeModelSort*)(tree_model_sort), (ch_iter), (so_iter)); #define NO_SORT_FUNC ((GtkTreeIterCompareFunc) 0x1) @@ -211,7 +215,7 @@ static gboolean gtk_tree_model_sort_has_default_sort_func (GtkTreeSortable /* Private functions (sort funcs, level handling and other utils) */ static void gtk_tree_model_sort_build_level (GtkTreeModelSort *tree_model_sort, SortLevel *parent_level, - SortElt *parent_elt); + gint parent_elt_index); static void gtk_tree_model_sort_free_level (GtkTreeModelSort *tree_model_sort, SortLevel *sort_level); static void gtk_tree_model_sort_increment_stamp (GtkTreeModelSort *tree_model_sort); @@ -499,7 +503,7 @@ gtk_tree_model_sort_row_changed (GtkTreeModel *s_model, for (i = 0; i < level->array->len; i++) if (g_array_index (level->array, SortElt, i).children) - g_array_index (level->array, SortElt, i).children->parent_elt = &g_array_index (level->array, SortElt, i); + g_array_index (level->array, SortElt, i).children->parent_elt_index = i; gtk_tree_path_up (path); gtk_tree_path_append_index (path, index); @@ -539,11 +543,11 @@ gtk_tree_model_sort_row_changed (GtkTreeModel *s_model, /* else? shouldn't really happen */ } - if (level->parent_elt) + if (level->parent_elt_index >= 0) { iter.stamp = tree_model_sort->stamp; iter.user_data = level->parent_level; - iter.user_data2 = level->parent_elt; + iter.user_data2 = SORT_LEVEL_PARENT_ELT (level); tmppath = gtk_tree_model_get_path (GTK_TREE_MODEL (tree_model_sort), &iter); @@ -609,7 +613,7 @@ gtk_tree_model_sort_row_inserted (GtkTreeModel *s_model, if (!tree_model_sort->root) { - gtk_tree_model_sort_build_level (tree_model_sort, NULL, NULL); + gtk_tree_model_sort_build_level (tree_model_sort, NULL, -1); /* the build level already put the inserted iter in the level, so no need to handle this signal anymore */ @@ -785,7 +789,7 @@ gtk_tree_model_sort_row_deleted (GtkTreeModel *s_model, if (elt->offset > offset) elt->offset--; if (elt->children) - elt->children->parent_elt = elt; + elt->children->parent_elt_index = i; } gtk_tree_path_free (path); @@ -934,7 +938,7 @@ gtk_tree_model_sort_get_iter (GtkTreeModel *tree_model, indices = gtk_tree_path_get_indices (path); if (tree_model_sort->root == NULL) - gtk_tree_model_sort_build_level (tree_model_sort, NULL, NULL); + gtk_tree_model_sort_build_level (tree_model_sort, NULL, -1); level = SORT_LEVEL (tree_model_sort->root); depth = gtk_tree_path_get_depth (path); @@ -948,7 +952,7 @@ gtk_tree_model_sort_get_iter (GtkTreeModel *tree_model, return FALSE; if (g_array_index (level->array, SortElt, indices[i]).children == NULL) - gtk_tree_model_sort_build_level (tree_model_sort, level, &g_array_index (level->array, SortElt, indices[i])); + gtk_tree_model_sort_build_level (tree_model_sort, level, indices[i]); level = g_array_index (level->array, SortElt, indices[i]).children; } @@ -972,19 +976,21 @@ gtk_tree_model_sort_get_path (GtkTreeModel *tree_model, GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; GtkTreePath *retval; SortLevel *level; - SortElt *elt; + gint elt_index; g_return_val_if_fail (tree_model_sort->child_model != NULL, NULL); g_return_val_if_fail (tree_model_sort->stamp == iter->stamp, NULL); retval = gtk_tree_path_new (); - level = iter->user_data; - elt = iter->user_data2; - while (level != NULL) + + level = SORT_LEVEL (iter->user_data); + elt_index = SORT_LEVEL_ELT_INDEX (level, iter->user_data2); + + while (level) { - gtk_tree_path_prepend_index (retval, elt - (SortElt *)level->array->data); + gtk_tree_path_prepend_index (retval, elt_index); - elt = level->parent_elt; + elt_index = level->parent_elt_index; level = level->parent_level; } @@ -1048,7 +1054,7 @@ gtk_tree_model_sort_iter_children (GtkTreeModel *tree_model, if (parent == NULL) { if (tree_model_sort->root == NULL) - gtk_tree_model_sort_build_level (tree_model_sort, NULL, NULL); + gtk_tree_model_sort_build_level (tree_model_sort, NULL, -1); if (tree_model_sort->root == NULL) return FALSE; @@ -1059,15 +1065,21 @@ gtk_tree_model_sort_iter_children (GtkTreeModel *tree_model, } else { - if (((SortElt *)parent->user_data2)->children == NULL) - gtk_tree_model_sort_build_level (tree_model_sort, - (SortLevel *)parent->user_data, - (SortElt *)parent->user_data2); - if (((SortElt *)parent->user_data2)->children == NULL) + SortElt *elt; + + level = SORT_LEVEL (parent->user_data); + elt = SORT_ELT (parent->user_data2); + + if (elt->children == NULL) + gtk_tree_model_sort_build_level (tree_model_sort, level, + SORT_LEVEL_ELT_INDEX (level, elt)); + + if (elt->children == NULL) return FALSE; + iter->stamp = tree_model_sort->stamp; - iter->user_data = ((SortElt *)parent->user_data2)->children; - iter->user_data2 = ((SortLevel *)iter->user_data)->array->data; + iter->user_data = elt->children; + iter->user_data2 = elt->children->array->data; } return TRUE; @@ -1160,7 +1172,7 @@ gtk_tree_model_sort_iter_parent (GtkTreeModel *tree_model, { iter->stamp = tree_model_sort->stamp; iter->user_data = level->parent_level; - iter->user_data2 = level->parent_elt; + iter->user_data2 = SORT_LEVEL_PARENT_ELT (level); return TRUE; } @@ -1173,9 +1185,9 @@ gtk_tree_model_sort_ref_node (GtkTreeModel *tree_model, { GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; GtkTreeIter child_iter; - GtkTreeIter tmp_iter; - SortLevel *level; + SortLevel *level, *parent_level; SortElt *elt; + gint parent_elt_index; g_return_if_fail (tree_model_sort->child_model != NULL); g_return_if_fail (VALID_ITER (iter, tree_model_sort)); @@ -1193,28 +1205,34 @@ gtk_tree_model_sort_ref_node (GtkTreeModel *tree_model, level->ref_count++; /* Increase the reference count of all parent elements */ - tmp_iter.stamp = tree_model_sort->stamp; - tmp_iter.user_data = level->parent_level; - tmp_iter.user_data2 = level->parent_elt;; + parent_level = level->parent_level; + parent_elt_index = level->parent_elt_index; - while (tmp_iter.user_data2) + while (parent_level) { + GtkTreeIter tmp_iter; + + tmp_iter.stamp = tree_model_sort->stamp; + tmp_iter.user_data = parent_level; + tmp_iter.user_data2 = &g_array_index (parent_level->array, SortElt, parent_elt_index); + gtk_tree_model_sort_ref_node (tree_model, &tmp_iter); - tmp_iter.user_data2 = SORT_LEVEL (tmp_iter.user_data)->parent_elt; - tmp_iter.user_data = SORT_LEVEL (tmp_iter.user_data)->parent_level; + parent_elt_index = parent_level->parent_elt_index; + parent_level = parent_level->parent_level; } if (level->ref_count == 1) { SortLevel *parent_level = level->parent_level; - SortElt *parent_elt = level->parent_elt; + gint parent_elt_index = level->parent_elt_index; + /* We were at zero -- time to decrement the zero_ref_count val */ while (parent_level) - { - parent_elt->zero_ref_count--; + { + g_array_index (parent_level->array, SortElt, parent_elt_index).zero_ref_count--; - parent_elt = parent_level->parent_elt; + parent_elt_index = parent_level->parent_elt_index; parent_level = parent_level->parent_level; } @@ -1229,9 +1247,9 @@ gtk_tree_model_sort_real_unref_node (GtkTreeModel *tree_model, gboolean propagate_unref) { GtkTreeModelSort *tree_model_sort = (GtkTreeModelSort *) tree_model; - GtkTreeIter tmp_iter; - SortLevel *level; + SortLevel *level, *parent_level; SortElt *elt; + gint parent_elt_index; g_return_if_fail (tree_model_sort->child_model != NULL); g_return_if_fail (VALID_ITER (iter, tree_model_sort)); @@ -1253,29 +1271,34 @@ gtk_tree_model_sort_real_unref_node (GtkTreeModel *tree_model, level->ref_count--; /* Decrease the reference count of all parent elements */ - tmp_iter.stamp = tree_model_sort->stamp; - tmp_iter.user_data = level->parent_level; - tmp_iter.user_data2 = level->parent_elt;; + parent_level = level->parent_level; + parent_elt_index = level->parent_elt_index; - while (tmp_iter.user_data2) + while (parent_level) { + GtkTreeIter tmp_iter; + + tmp_iter.stamp = tree_model_sort->stamp; + tmp_iter.user_data = parent_level; + tmp_iter.user_data2 = &g_array_index (parent_level->array, SortElt, parent_elt_index); + gtk_tree_model_sort_real_unref_node (tree_model, &tmp_iter, FALSE); - tmp_iter.user_data2 = SORT_LEVEL (tmp_iter.user_data)->parent_elt; - tmp_iter.user_data = SORT_LEVEL (tmp_iter.user_data)->parent_level; + parent_elt_index = parent_level->parent_elt_index; + parent_level = parent_level->parent_level; } if (level->ref_count == 0) { SortLevel *parent_level = level->parent_level; - SortElt *parent_elt = level->parent_elt; + gint parent_elt_index = level->parent_elt_index; /* We are at zero -- time to increment the zero_ref_count val */ while (parent_level) { - parent_elt->zero_ref_count++; + g_array_index (parent_level->array, SortElt, parent_elt_index).zero_ref_count++; - parent_elt = parent_level->parent_elt; + parent_elt_index = parent_level->parent_elt_index; parent_level = parent_level->parent_level; } @@ -1558,10 +1581,10 @@ gtk_tree_model_sort_sort_level (GtkTreeModelSort *tree_model_sort, /* Set up data */ data.tree_model_sort = tree_model_sort; - if (level->parent_elt) + if (level->parent_elt_index >= 0) { data.parent_path = gtk_tree_model_sort_elt_get_path (level->parent_level, - level->parent_elt); + SORT_LEVEL_PARENT_ELT (level)); gtk_tree_path_append_index (data.parent_path, 0); } else @@ -1627,9 +1650,8 @@ gtk_tree_model_sort_sort_level (GtkTreeModelSort *tree_model_sort, new_order[i] = g_array_index (sort_array, SortTuple, i).offset; g_array_append_val (new_array, *elt); - elt = &g_array_index (new_array, SortElt, i); if (elt->children) - elt->children->parent_elt = elt; + elt->children->parent_elt_index = i; } g_array_free (level->array, TRUE); @@ -1639,11 +1661,11 @@ gtk_tree_model_sort_sort_level (GtkTreeModelSort *tree_model_sort, if (emit_reordered) { gtk_tree_model_sort_increment_stamp (tree_model_sort); - if (level->parent_elt) + if (level->parent_elt_index >= 0) { iter.stamp = tree_model_sort->stamp; iter.user_data = level->parent_level; - iter.user_data2 = level->parent_elt; + iter.user_data2 = SORT_LEVEL_PARENT_ELT (level); path = gtk_tree_model_get_path (GTK_TREE_MODEL (tree_model_sort), &iter); @@ -1846,7 +1868,7 @@ gtk_tree_model_sort_insert_value (GtkTreeModelSort *tree_model_sort, tmp_elt = SORT_ELT (level->array->data); for (i = 0; i < level->array->len; i++, tmp_elt++) if (tmp_elt->children) - tmp_elt->children->parent_elt = tmp_elt; + tmp_elt->children->parent_elt_index = i; return TRUE; } @@ -1869,7 +1891,10 @@ gtk_tree_model_sort_elt_get_path (SortLevel *level, { gtk_tree_path_prepend_index (path, walker2->offset); - walker2 = walker->parent_elt; + if (!walker->parent_level) + break; + + walker2 = SORT_LEVEL_PARENT_ELT (walker); walker = walker->parent_level; } @@ -1992,7 +2017,7 @@ gtk_real_tree_model_sort_convert_child_path_to_path (GtkTreeModelSort *tree_mode child_indices = gtk_tree_path_get_indices (child_path); if (tree_model_sort->root == NULL && build_levels) - gtk_tree_model_sort_build_level (tree_model_sort, NULL, NULL); + gtk_tree_model_sort_build_level (tree_model_sort, NULL, -1); level = SORT_LEVEL (tree_model_sort->root); for (i = 0; i < gtk_tree_path_get_depth (child_path); i++) @@ -2018,7 +2043,7 @@ gtk_real_tree_model_sort_convert_child_path_to_path (GtkTreeModelSort *tree_mode gtk_tree_path_append_index (retval, j); if (g_array_index (level->array, SortElt, j).children == NULL && build_levels) { - gtk_tree_model_sort_build_level (tree_model_sort, level, &g_array_index (level->array, SortElt, j)); + gtk_tree_model_sort_build_level (tree_model_sort, level, j); } level = g_array_index (level->array, SortElt, j).children; found_child = TRUE; @@ -2084,6 +2109,7 @@ gtk_tree_model_sort_convert_child_iter_to_iter (GtkTreeModelSort *tree_model_sor g_return_val_if_fail (tree_model_sort->child_model != NULL, FALSE); g_return_val_if_fail (sort_iter != NULL, FALSE); g_return_val_if_fail (child_iter != NULL, FALSE); + g_return_val_if_fail (sort_iter != child_iter, FALSE); sort_iter->stamp = 0; @@ -2135,7 +2161,7 @@ gtk_tree_model_sort_convert_path_to_child_path (GtkTreeModelSort *tree_model_sor retval = gtk_tree_path_new (); sorted_indices = gtk_tree_path_get_indices (sorted_path); if (tree_model_sort->root == NULL) - gtk_tree_model_sort_build_level (tree_model_sort, NULL, NULL); + gtk_tree_model_sort_build_level (tree_model_sort, NULL, -1); level = SORT_LEVEL (tree_model_sort->root); for (i = 0; i < gtk_tree_path_get_depth (sorted_path); i++) @@ -2150,7 +2176,7 @@ gtk_tree_model_sort_convert_path_to_child_path (GtkTreeModelSort *tree_model_sor } if (g_array_index (level->array, SortElt, count).children == NULL) - gtk_tree_model_sort_build_level (tree_model_sort, level, &g_array_index (level->array, SortElt, count)); + gtk_tree_model_sort_build_level (tree_model_sort, level, count); if (level == NULL) { @@ -2182,6 +2208,7 @@ gtk_tree_model_sort_convert_iter_to_child_iter (GtkTreeModelSort *tree_model_sor g_return_if_fail (tree_model_sort->child_model != NULL); g_return_if_fail (child_iter != NULL); g_return_if_fail (VALID_ITER (sorted_iter, tree_model_sort)); + g_return_if_fail (sorted_iter != child_iter); if (GTK_TREE_MODEL_SORT_CACHE_CHILD_ITERS (tree_model_sort)) { @@ -2201,9 +2228,10 @@ gtk_tree_model_sort_convert_iter_to_child_iter (GtkTreeModelSort *tree_model_sor static void gtk_tree_model_sort_build_level (GtkTreeModelSort *tree_model_sort, SortLevel *parent_level, - SortElt *parent_elt) + gint parent_elt_index) { GtkTreeIter iter; + SortElt *parent_elt = NULL; SortLevel *new_level; gint length = 0; gint i; @@ -2221,6 +2249,8 @@ gtk_tree_model_sort_build_level (GtkTreeModelSort *tree_model_sort, GtkTreeIter parent_iter; GtkTreeIter child_parent_iter; + parent_elt = &g_array_index (parent_level->array, SortElt, parent_elt_index); + parent_iter.stamp = tree_model_sort->stamp; parent_iter.user_data = parent_level; parent_iter.user_data2 = parent_elt; @@ -2246,10 +2276,10 @@ gtk_tree_model_sort_build_level (GtkTreeModelSort *tree_model_sort, new_level = g_new (SortLevel, 1); new_level->array = g_array_sized_new (FALSE, FALSE, sizeof (SortElt), length); new_level->ref_count = 0; - new_level->parent_elt = parent_elt; new_level->parent_level = parent_level; + new_level->parent_elt_index = parent_elt_index; - if (parent_elt) + if (parent_elt_index >= 0) parent_elt->children = new_level; else tree_model_sort->root = new_level; @@ -2257,11 +2287,12 @@ gtk_tree_model_sort_build_level (GtkTreeModelSort *tree_model_sort, /* increase the count of zero ref_counts.*/ while (parent_level) { - parent_elt->zero_ref_count++; + g_array_index (parent_level->array, SortElt, parent_elt_index).zero_ref_count++; - parent_elt = parent_level->parent_elt; + parent_elt_index = parent_level->parent_elt_index; parent_level = parent_level->parent_level; } + if (new_level != tree_model_sort->root) tree_model_sort->zero_ref_count++; @@ -2332,13 +2363,13 @@ gtk_tree_model_sort_free_level (GtkTreeModelSort *tree_model_sort, if (sort_level->ref_count == 0) { SortLevel *parent_level = sort_level->parent_level; - SortElt *parent_elt = sort_level->parent_elt; + gint parent_elt_index = sort_level->parent_elt_index; while (parent_level) - { - parent_elt->zero_ref_count--; + { + g_array_index (parent_level->array, SortElt, parent_elt_index).zero_ref_count--; - parent_elt = parent_level->parent_elt; + parent_elt_index = parent_level->parent_elt_index; parent_level = parent_level->parent_level; } @@ -2346,8 +2377,8 @@ gtk_tree_model_sort_free_level (GtkTreeModelSort *tree_model_sort, tree_model_sort->zero_ref_count--; } - if (sort_level->parent_elt) - sort_level->parent_elt->children = NULL; + if (sort_level->parent_elt_index >= 0) + SORT_LEVEL_PARENT_ELT (sort_level)->children = NULL; else tree_model_sort->root = NULL; diff --git a/gtk/gtktreeprivate.h b/gtk/gtktreeprivate.h index 44259abe35..8b672f26cc 100644 --- a/gtk/gtktreeprivate.h +++ b/gtk/gtktreeprivate.h @@ -285,14 +285,13 @@ struct _GtkTreeViewPrivate { \ g_log (G_LOG_DOMAIN, \ G_LOG_LEVEL_CRITICAL, \ - "file %s: line %d (%s): assertion `%s' failed.\n" \ + "%s (%s): assertion `%s' failed.\n" \ "There is a disparity between the internal view of the GtkTreeView,\n" \ "and the GtkTreeModel. This generally means that the model has changed\n"\ "without letting the view know. Any display from now on is likely to\n" \ "be incorrect.\n", \ - __FILE__, \ - __LINE__, \ - __PRETTY_FUNCTION__, \ + G_STRLOC, \ + G_STRFUNC, \ #expr); \ return ret; \ }; }G_STMT_END @@ -302,14 +301,13 @@ struct _GtkTreeViewPrivate { \ g_log (G_LOG_DOMAIN, \ G_LOG_LEVEL_CRITICAL, \ - "file %s: line %d (%s): assertion `%s' failed.\n" \ + "%s (%s): assertion `%s' failed.\n" \ "There is a disparity between the internal view of the GtkTreeView,\n" \ "and the GtkTreeModel. This generally means that the model has changed\n"\ "without letting the view know. Any display from now on is likely to\n" \ "be incorrect.\n", \ - __FILE__, \ - __LINE__, \ - __PRETTY_FUNCTION__, \ + G_STRLOC, \ + G_STRFUNC, \ #expr); \ return; \ }; }G_STMT_END diff --git a/gtk/gtktreestore.c b/gtk/gtktreestore.c index 1fb178d375..30ff14f810 100644 --- a/gtk/gtktreestore.c +++ b/gtk/gtktreestore.c @@ -358,30 +358,20 @@ static void gtk_tree_store_set_n_columns (GtkTreeStore *tree_store, gint n_columns) { - GType *new_columns; + int i; if (tree_store->n_columns == n_columns) return; - new_columns = g_new0 (GType, n_columns); - if (tree_store->column_headers) - { - /* copy the old header orders over */ - if (n_columns >= tree_store->n_columns) - memcpy (new_columns, tree_store->column_headers, tree_store->n_columns * sizeof (gchar *)); - else - memcpy (new_columns, tree_store->column_headers, n_columns * sizeof (GType)); - - g_free (tree_store->column_headers); - } + tree_store->column_headers = g_renew (GType, tree_store->column_headers, n_columns); + for (i = tree_store->n_columns; i < n_columns; i++) + tree_store->column_headers[i] = G_TYPE_INVALID; + tree_store->n_columns = n_columns; if (tree_store->sort_list) _gtk_tree_data_list_header_free (tree_store->sort_list); tree_store->sort_list = _gtk_tree_data_list_header_new (n_columns, tree_store->column_headers); - - tree_store->column_headers = new_columns; - tree_store->n_columns = n_columns; } /** @@ -620,7 +610,10 @@ gtk_tree_store_iter_next (GtkTreeModel *tree_model, return TRUE; } else - return FALSE; + { + iter->stamp = 0; + return FALSE; + } } static gboolean @@ -646,7 +639,10 @@ gtk_tree_store_iter_children (GtkTreeModel *tree_model, return TRUE; } else - return FALSE; + { + iter->stamp = 0; + return FALSE; + } } static gboolean @@ -708,7 +704,10 @@ gtk_tree_store_iter_nth_child (GtkTreeModel *tree_model, return TRUE; } else - return FALSE; + { + iter->stamp = 0; + return FALSE; + } } static gboolean @@ -733,7 +732,10 @@ gtk_tree_store_iter_parent (GtkTreeModel *tree_model, return TRUE; } else - return FALSE; + { + iter->stamp = 0; + return FALSE; + } } diff --git a/gtk/gtktreeview.c b/gtk/gtktreeview.c index b4d4708761..3aad46cd44 100644 --- a/gtk/gtktreeview.c +++ b/gtk/gtktreeview.c @@ -48,7 +48,7 @@ #define GTK_TREE_VIEW_PRIORITY_VALIDATE (GDK_PRIORITY_REDRAW + 5) #define GTK_TREE_VIEW_PRIORITY_SCROLL_SYNC (GTK_TREE_VIEW_PRIORITY_VALIDATE + 2) -#define GTK_TREE_VIEW_NUM_ROWS_PER_IDLE 500 +#define GTK_TREE_VIEW_TIME_MS_PER_IDLE 30 #define SCROLL_EDGE_SIZE 15 #define EXPANDER_EXTRA_PADDING 4 #define GTK_TREE_VIEW_SEARCH_DIALOG_TIMEOUT 5000 @@ -345,15 +345,6 @@ static void gtk_tree_view_build_tree (GtkTreeView GtkTreeIter *iter, gint depth, gboolean recurse); -static gboolean gtk_tree_view_discover_dirty_iter (GtkTreeView *tree_view, - GtkTreeIter *iter, - gint depth, - gint *height, - GtkRBNode *node); -static void gtk_tree_view_discover_dirty (GtkTreeView *tree_view, - GtkRBTree *tree, - GtkTreeIter *iter, - gint depth); static void gtk_tree_view_clamp_node_visible (GtkTreeView *tree_view, GtkRBTree *tree, GtkRBNode *node); @@ -2043,9 +2034,8 @@ gtk_tree_view_size_request (GtkWidget *widget, GtkTreeView *tree_view = GTK_TREE_VIEW (widget); GList *tmp_list; - /* we validate GTK_TREE_VIEW_NUM_ROWS_PER_IDLE rows initially just to make - * sure we have some size. In practice, with a lot of static lists, this - * should get a good width. + /* we validate some rows initially just to make sure we have some size. + * In practice, with a lot of static lists, this should get a good width. */ do_validate_rows (tree_view, FALSE); gtk_tree_view_size_request_columns (tree_view); @@ -4582,78 +4572,6 @@ gtk_tree_view_bin_expose (GtkWidget *widget, background_area.y + max_height); } - if (gtk_tree_view_is_expander_column (tree_view, column) && - tree_view->priv->tree_lines_enabled) - { - gint x = background_area.x; - gint mult = rtl ? -1 : 1; - gint y0 = background_area.y; - gint y1 = background_area.y + background_area.height/2; - gint y2 = background_area.y + background_area.height; - - if (rtl) - x += background_area.width - 1; - - if ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT - && depth > 1) - { - gdk_draw_line (event->window, - tree_view->priv->tree_line_gc, - x + tree_view->priv->expander_size * (depth - 1.5) * mult, - y1, - x + tree_view->priv->expander_size * (depth - 1.1) * mult, - y1); - } - else if (depth > 1) - { - gdk_draw_line (event->window, - tree_view->priv->tree_line_gc, - x + tree_view->priv->expander_size * (depth - 1.5) * mult, - y1, - x + tree_view->priv->expander_size * (depth - 0.5) * mult, - y1); - } - - if (depth > 1) - { - gint i; - GtkRBNode *tmp_node; - GtkRBTree *tmp_tree; - - if (!_gtk_rbtree_next (tree, node)) - gdk_draw_line (event->window, - tree_view->priv->tree_line_gc, - x + tree_view->priv->expander_size * (depth - 1.5) * mult, - y0, - x + tree_view->priv->expander_size * (depth - 1.5) * mult, - y1); - else - gdk_draw_line (event->window, - tree_view->priv->tree_line_gc, - x + tree_view->priv->expander_size * (depth - 1.5) * mult, - y0, - x + tree_view->priv->expander_size * (depth - 1.5) * mult, - y2); - - tmp_node = tree->parent_node; - tmp_tree = tree->parent_tree; - - for (i = depth - 2; i > 0; i--) - { - if (_gtk_rbtree_next (tmp_tree, tmp_node)) - gdk_draw_line (event->window, - tree_view->priv->tree_line_gc, - x + tree_view->priv->expander_size * (i - 0.5) * mult, - y0, - x + tree_view->priv->expander_size * (i - 0.5) * mult, - y2); - - tmp_node = tmp_tree->parent_node; - tmp_tree = tmp_tree->parent_tree; - } - } - } - if (gtk_tree_view_is_expander_column (tree_view, column)) { if (!rtl) @@ -4727,6 +4645,79 @@ gtk_tree_view_bin_expose (GtkWidget *widget, &event->area, flags); } + + if (gtk_tree_view_is_expander_column (tree_view, column) && + tree_view->priv->tree_lines_enabled) + { + gint x = background_area.x; + gint mult = rtl ? -1 : 1; + gint y0 = background_area.y; + gint y1 = background_area.y + background_area.height/2; + gint y2 = background_area.y + background_area.height; + + if (rtl) + x += background_area.width - 1; + + if ((node->flags & GTK_RBNODE_IS_PARENT) == GTK_RBNODE_IS_PARENT + && depth > 1) + { + gdk_draw_line (event->window, + tree_view->priv->tree_line_gc, + x + tree_view->priv->expander_size * (depth - 1.5) * mult, + y1, + x + tree_view->priv->expander_size * (depth - 1.1) * mult, + y1); + } + else if (depth > 1) + { + gdk_draw_line (event->window, + tree_view->priv->tree_line_gc, + x + tree_view->priv->expander_size * (depth - 1.5) * mult, + y1, + x + tree_view->priv->expander_size * (depth - 0.5) * mult, + y1); + } + + if (depth > 1) + { + gint i; + GtkRBNode *tmp_node; + GtkRBTree *tmp_tree; + + if (!_gtk_rbtree_next (tree, node)) + gdk_draw_line (event->window, + tree_view->priv->tree_line_gc, + x + tree_view->priv->expander_size * (depth - 1.5) * mult, + y0, + x + tree_view->priv->expander_size * (depth - 1.5) * mult, + y1); + else + gdk_draw_line (event->window, + tree_view->priv->tree_line_gc, + x + tree_view->priv->expander_size * (depth - 1.5) * mult, + y0, + x + tree_view->priv->expander_size * (depth - 1.5) * mult, + y2); + + tmp_node = tree->parent_node; + tmp_tree = tree->parent_tree; + + for (i = depth - 2; i > 0; i--) + { + if (_gtk_rbtree_next (tmp_tree, tmp_node)) + gdk_draw_line (event->window, + tree_view->priv->tree_line_gc, + x + tree_view->priv->expander_size * (i - 0.5) * mult, + y0, + x + tree_view->priv->expander_size * (i - 0.5) * mult, + y2); + + tmp_node = tmp_tree->parent_node; + tmp_tree = tmp_tree->parent_tree; + } + } + } + if (node == cursor && has_special_cell && ((column == tree_view->priv->focus_column && GTK_TREE_VIEW_FLAG_SET (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS) && @@ -4740,6 +4731,7 @@ gtk_tree_view_bin_expose (GtkWidget *widget, &event->area, flags); } + cell_offset += column->width; } @@ -5483,6 +5475,12 @@ gtk_tree_view_enter_notify (GtkWidget *widget, if (tree_view->priv->tree == NULL) return FALSE; + if (event->mode == GDK_CROSSING_GRAB || + event->mode == GDK_CROSSING_GTK_GRAB || + event->mode == GDK_CROSSING_GTK_UNGRAB || + event->mode == GDK_CROSSING_STATE_CHANGED) + return TRUE; + /* find the node internally */ new_y = TREE_WINDOW_Y_TO_RBTREE_Y(tree_view, event->y); if (new_y < 0) @@ -5992,16 +5990,24 @@ validate_visible_area (GtkTreeView *tree_view) while (area_above > 0) { _gtk_rbtree_prev_full (tree, node, &tree, &node); - if (! gtk_tree_path_prev (above_path) && node != NULL) - { - gtk_tree_path_free (above_path); - above_path = _gtk_tree_view_find_path (tree_view, tree, node); - } - gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path); + + /* Always find the new path in the tree. We cannot just assume + * a gtk_tree_path_prev() is enough here, as there might be children + * in between this node and the previous sibling node. If this + * appears to be a performance hotspot in profiles, we can look into + * intrigate logic for keeping path, node and iter in sync like + * we do for forward walks. (Which will be hard because of the lacking + * iter_prev). + */ if (node == NULL) break; + gtk_tree_path_free (above_path); + above_path = _gtk_tree_view_find_path (tree_view, tree, node); + + gtk_tree_model_get_iter (tree_view->priv->model, &iter, above_path); + if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) || GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID)) { @@ -6118,6 +6124,7 @@ do_validate_rows (GtkTreeView *tree_view, gboolean queue_resize) gint retval = TRUE; GtkTreePath *path = NULL; GtkTreeIter iter; + GTimer *timer; gint i = 0; gint prev_height = -1; @@ -6136,6 +6143,9 @@ do_validate_rows (GtkTreeView *tree_view, gboolean queue_resize) return FALSE; } + timer = g_timer_new (); + g_timer_start (timer); + do { if (! GTK_RBNODE_FLAG_SET (tree_view->priv->tree->root, GTK_RBNODE_DESCENDANTS_INVALID)) @@ -6213,7 +6223,7 @@ do_validate_rows (GtkTreeView *tree_view, gboolean queue_resize) i++; } - while (i < GTK_TREE_VIEW_NUM_ROWS_PER_IDLE); + while (g_timer_elapsed (timer, NULL) < GTK_TREE_VIEW_TIME_MS_PER_IDLE / 1000.); if (!tree_view->priv->fixed_height_check) { @@ -6238,10 +6248,11 @@ do_validate_rows (GtkTreeView *tree_view, gboolean queue_resize) gtk_adjustment_changed (tree_view->priv->vadjustment); if (queue_resize) - gtk_widget_queue_resize_no_redraw (GTK_WIDGET (tree_view)); + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); } if (path) gtk_tree_path_free (path); + g_timer_destroy (timer); return retval; } @@ -6421,6 +6432,10 @@ gtk_tree_view_top_row_to_dy (GtkTreeView *tree_view) GtkRBNode *node; int new_dy; + /* Avoid recursive calls */ + if (tree_view->priv->in_top_row_to_dy) + return; + if (tree_view->priv->top_row) path = gtk_tree_row_reference_get_path (tree_view->priv->top_row); else @@ -8757,133 +8772,6 @@ gtk_tree_view_build_tree (GtkTreeView *tree_view, gtk_tree_path_free (path); } -/* If height is non-NULL, then we set it to be the new height. if it's all - * dirty, then height is -1. We know we'll remeasure dirty rows, anyways. - */ -static gboolean -gtk_tree_view_discover_dirty_iter (GtkTreeView *tree_view, - GtkTreeIter *iter, - gint depth, - gint *height, - GtkRBNode *node) -{ - GtkTreeViewColumn *column; - GList *list; - gboolean retval = FALSE; - gint tmpheight; - gint horizontal_separator; - - gtk_widget_style_get (GTK_WIDGET (tree_view), - "horizontal-separator", &horizontal_separator, - NULL); - - if (height) - *height = -1; - - for (list = tree_view->priv->columns; list; list = list->next) - { - gint width; - column = list->data; - if (column->dirty == TRUE) - continue; - if (height == NULL && column->column_type == GTK_TREE_VIEW_COLUMN_FIXED) - continue; - if (!column->visible) - continue; - - gtk_tree_view_column_cell_set_cell_data (column, tree_view->priv->model, iter, - GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_PARENT), - node->children?TRUE:FALSE); - - if (height) - { - gtk_tree_view_column_cell_get_size (column, - NULL, NULL, NULL, - &width, &tmpheight); - *height = MAX (*height, tmpheight); - } - else - { - gtk_tree_view_column_cell_get_size (column, - NULL, NULL, NULL, - &width, NULL); - } - - if (gtk_tree_view_is_expander_column (tree_view, column)) - { - int tmp = 0; - - tmp = horizontal_separator + width + (depth - 1) * tree_view->priv->level_indentation; - if (TREE_VIEW_DRAW_EXPANDERS (tree_view)) - tmp += depth * tree_view->priv->expander_size; - - if (tmp > column->requested_width) - { - _gtk_tree_view_column_cell_set_dirty (column, TRUE); - retval = TRUE; - } - } - else - { - if (horizontal_separator + width > column->requested_width) - { - _gtk_tree_view_column_cell_set_dirty (column, TRUE); - retval = TRUE; - } - } - } - - return retval; -} - -static void -gtk_tree_view_discover_dirty (GtkTreeView *tree_view, - GtkRBTree *tree, - GtkTreeIter *iter, - gint depth) -{ - GtkRBNode *temp = tree->root; - GtkTreeViewColumn *column; - GList *list; - GtkTreeIter child; - gboolean is_all_dirty; - - TREE_VIEW_INTERNAL_ASSERT_VOID (tree != NULL); - - while (temp->left != tree->nil) - temp = temp->left; - - do - { - TREE_VIEW_INTERNAL_ASSERT_VOID (temp != NULL); - is_all_dirty = TRUE; - for (list = tree_view->priv->columns; list; list = list->next) - { - column = list->data; - if (column->dirty == FALSE) - { - is_all_dirty = FALSE; - break; - } - } - - if (is_all_dirty) - return; - - gtk_tree_view_discover_dirty_iter (tree_view, - iter, - depth, - NULL, - temp); - if (gtk_tree_model_iter_children (tree_view->priv->model, &child, iter) && - temp->children != NULL) - gtk_tree_view_discover_dirty (tree_view, temp->children, &child, depth + 1); - temp = _gtk_rbtree_next (tree, temp); - } - while (gtk_tree_model_iter_next (tree_view->priv->model, iter)); -} - - /* Make sure the node is visible vertically */ static void gtk_tree_view_clamp_node_visible (GtkTreeView *tree_view, @@ -9863,6 +9751,8 @@ gtk_tree_view_move_cursor_page_up_down (GtkTreeView *tree_view, if (!gtk_tree_path_compare (old_cursor_path, cursor_path)) gtk_widget_error_bell (GTK_WIDGET (tree_view)); + gtk_widget_grab_focus (GTK_WIDGET (tree_view)); + cleanup: gtk_tree_path_free (old_cursor_path); gtk_tree_path_free (cursor_path); @@ -9957,6 +9847,7 @@ gtk_tree_view_move_cursor_left_right (GtkTreeView *tree_view, cursor_node, NULL); g_signal_emit (tree_view, tree_view_signals[CURSOR_CHANGED], 0); + gtk_widget_grab_focus (GTK_WIDGET (tree_view)); } else { @@ -10022,6 +9913,7 @@ gtk_tree_view_move_cursor_start_end (GtkTreeView *tree_view, if (gtk_tree_path_compare (old_path, path)) { gtk_tree_view_real_set_cursor (tree_view, path, TRUE, TRUE); + gtk_widget_grab_focus (GTK_WIDGET (tree_view)); } else { @@ -10643,6 +10535,9 @@ gtk_tree_view_adjustment_changed (GtkAdjustment *adjustment, if (!tree_view->priv->in_top_row_to_dy) gtk_tree_view_dy_to_top_row (tree_view); } + + gdk_window_process_updates (tree_view->priv->header_window, TRUE); + gdk_window_process_updates (tree_view->priv->bin_window, TRUE); } } @@ -12552,14 +12447,24 @@ gtk_tree_view_real_set_cursor (GtkTreeView *tree_view, gtk_tree_row_reference_free (tree_view->priv->cursor); tree_view->priv->cursor = NULL; - /* One cannot set the cursor on a separator. */ - if (!row_is_separator (tree_view, NULL, path)) + /* One cannot set the cursor on a separator. Also, if + * _gtk_tree_view_find_node returns TRUE, it ran out of tree + * before finding the tree and node belonging to path. The + * path maps to a non-existing path and we will silently bail out. + * We unset tree and node to avoid further processing. + */ + if (!row_is_separator (tree_view, NULL, path) + && _gtk_tree_view_find_node (tree_view, path, &tree, &node) == FALSE) { tree_view->priv->cursor = - gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), - tree_view->priv->model, - path); - _gtk_tree_view_find_node (tree_view, path, &tree, &node); + gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view), + tree_view->priv->model, + path); + } + else + { + tree = NULL; + node = NULL; } if (tree != NULL) @@ -12649,6 +12554,9 @@ gtk_tree_view_get_cursor (GtkTreeView *tree_view, * This function is often followed by @gtk_widget_grab_focus (@tree_view) * in order to give keyboard focus to the widget. Please note that editing * can only happen when the widget is realized. + * + * If @path is invalid for @model, the current cursor (if any) will be unset + * and the function will return without failing. **/ void gtk_tree_view_set_cursor (GtkTreeView *tree_view, @@ -12680,6 +12588,9 @@ gtk_tree_view_set_cursor (GtkTreeView *tree_view, * widget. Please note that editing can only happen when the widget is * realized. * + * If @path is invalid for @model, the current cursor (if any) will be unset + * and the function will return without failing. + * * Since: 2.2 **/ void @@ -12690,9 +12601,12 @@ gtk_tree_view_set_cursor_on_cell (GtkTreeView *tree_view, gboolean start_editing) { g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); - g_return_if_fail (tree_view->priv->tree != NULL); g_return_if_fail (path != NULL); g_return_if_fail (focus_column == NULL || GTK_IS_TREE_VIEW_COLUMN (focus_column)); + + if (!tree_view->priv->model) + return; + if (focus_cell) { g_return_if_fail (focus_column); @@ -12765,7 +12679,8 @@ gtk_tree_view_get_bin_window (GtkTreeView *tree_view) * with the column at that point. @cell_x and @cell_y return the coordinates * relative to the cell background (i.e. the @background_area passed to * gtk_cell_renderer_render()). This function is only meaningful if - * @tree_view is realized. + * @tree_view is realized. Therefore this function will always return %FALSE + * if @tree_view is not realized or does not have a model. * * For converting widget coordinates (eg. the ones you get from * GtkWidget::query-tooltip), please see @@ -12787,13 +12702,15 @@ gtk_tree_view_get_path_at_pos (GtkTreeView *tree_view, gint y_offset; g_return_val_if_fail (tree_view != NULL, FALSE); - g_return_val_if_fail (tree_view->priv->bin_window != NULL, FALSE); if (path) *path = NULL; if (column) *column = NULL; + if (tree_view->priv->bin_window == NULL) + return FALSE; + if (tree_view->priv->tree == NULL) return FALSE; @@ -13616,9 +13533,12 @@ gtk_tree_view_get_drag_dest_row (GtkTreeView *tree_view, * @pos: Return location for the drop position, or %NULL * * Determines the destination row for a given position. @drag_x and - * @drag_y are expected to be in widget coordinates. + * @drag_y are expected to be in widget coordinates. This function is only + * meaningful if @tree_view is realized. Therefore this function will always + * return %FALSE if @tree_view is not realized or does not have a model. * - * Return value: whether there is a row at the given position. + * Return value: whether there is a row at the given position, %TRUE if this + * is indeed the case. **/ gboolean gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view, @@ -13642,12 +13562,13 @@ gtk_tree_view_get_dest_row_at_pos (GtkTreeView *tree_view, g_return_val_if_fail (tree_view != NULL, FALSE); g_return_val_if_fail (drag_x >= 0, FALSE); g_return_val_if_fail (drag_y >= 0, FALSE); - g_return_val_if_fail (tree_view->priv->bin_window != NULL, FALSE); - if (path) *path = NULL; + if (tree_view->priv->bin_window == NULL) + return FALSE; + if (tree_view->priv->tree == NULL) return FALSE; @@ -14405,7 +14326,7 @@ gtk_tree_view_search_key_press_event (GtkWidget *widget, retval = TRUE; } - if (((event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) + if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) && (event->keyval == GDK_g || event->keyval == GDK_G)) { if (!gtk_tree_view_search_move (widget, tree_view, TRUE)) @@ -14423,7 +14344,7 @@ gtk_tree_view_search_key_press_event (GtkWidget *widget, retval = TRUE; } - if (((event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == GDK_CONTROL_MASK) + if (((event->state & (GTK_DEFAULT_ACCEL_MOD_MASK | GDK_SHIFT_MASK)) == GTK_DEFAULT_ACCEL_MOD_MASK) && (event->keyval == GDK_g || event->keyval == GDK_G)) { if (!gtk_tree_view_search_move (widget, tree_view, FALSE)) @@ -14587,8 +14508,8 @@ gtk_tree_view_search_iter (GtkTreeModel *model, { gtk_tree_view_scroll_to_cell (tree_view, path, NULL, TRUE, 0.5, 0.0); - gtk_tree_view_real_set_cursor (tree_view, path, FALSE, TRUE); gtk_tree_selection_select_iter (selection, iter); + gtk_tree_view_real_set_cursor (tree_view, path, FALSE, TRUE); if (path) gtk_tree_path_free (path); @@ -14674,7 +14595,6 @@ gtk_tree_view_search_init (GtkWidget *entry, GtkTreeView *tree_view) { gint ret; - gint len; gint count = 0; const gchar *text; GtkTreeIter iter; @@ -14685,7 +14605,7 @@ gtk_tree_view_search_init (GtkWidget *entry, g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); text = gtk_entry_get_text (GTK_ENTRY (entry)); - len = strlen (text); + model = gtk_tree_view_get_model (tree_view); selection = gtk_tree_view_get_selection (tree_view); @@ -14701,7 +14621,7 @@ gtk_tree_view_search_init (GtkWidget *entry, tree_view); } - if (len < 1) + if (*text == '\0') return; if (!gtk_tree_model_get_iter_first (model, &iter)) @@ -15094,6 +15014,10 @@ gtk_tree_view_set_row_separator_func (GtkTreeView *tree_view, tree_view->priv->row_separator_func = func; tree_view->priv->row_separator_data = data; tree_view->priv->row_separator_destroy = destroy; + + /* Have the tree recalculate heights */ + _gtk_rbtree_mark_invalid (tree_view->priv->tree); + gtk_widget_queue_resize (GTK_WIDGET (tree_view)); } diff --git a/gtk/gtktreeviewcolumn.c b/gtk/gtktreeviewcolumn.c index d3259d0f64..edec5deffe 100644 --- a/gtk/gtktreeviewcolumn.c +++ b/gtk/gtktreeviewcolumn.c @@ -51,7 +51,8 @@ enum PROP_ALIGNMENT, PROP_REORDERABLE, PROP_SORT_INDICATOR, - PROP_SORT_ORDER + PROP_SORT_ORDER, + PROP_SORT_COLUMN_ID }; enum @@ -325,7 +326,24 @@ gtk_tree_view_column_class_init (GtkTreeViewColumnClass *class) GTK_TYPE_SORT_TYPE, GTK_SORT_ASCENDING, GTK_PARAM_READWRITE)); - + + /** + * GtkTreeViewColumn:sort-column-id: + * + * Logical sort column ID this column sorts on when selected for sorting. Setting the sort column ID makes the column header + * clickable. Set to %-1 to make the column unsortable. + * + * Since: 2.18 + **/ + g_object_class_install_property (object_class, + PROP_SORT_COLUMN_ID, + g_param_spec_int ("sort-column-id", + P_("Sort column ID"), + P_("Logical sort column ID this column sorts on when selected for sorting"), + -1, + G_MAXINT, + -1, + GTK_PARAM_READWRITE)); } static void @@ -497,6 +515,11 @@ gtk_tree_view_column_set_property (GObject *object, g_value_get_enum (value)); break; + case PROP_SORT_COLUMN_ID: + gtk_tree_view_column_set_sort_column_id (tree_column, + g_value_get_int (value)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -595,6 +618,11 @@ gtk_tree_view_column_get_property (GObject *object, gtk_tree_view_column_get_sort_order (tree_column)); break; + case PROP_SORT_COLUMN_ID: + g_value_set_int (value, + gtk_tree_view_column_get_sort_column_id (tree_column)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -2381,6 +2409,7 @@ gtk_tree_view_column_set_sort_column_id (GtkTreeViewColumn *tree_column, gtk_tree_view_column_set_sort_order (tree_column, GTK_SORT_ASCENDING); gtk_tree_view_column_set_sort_indicator (tree_column, FALSE); gtk_tree_view_column_set_clickable (tree_column, FALSE); + g_object_notify (G_OBJECT (tree_column), "sort-column-id"); return; } @@ -2393,6 +2422,7 @@ gtk_tree_view_column_set_sort_column_id (GtkTreeViewColumn *tree_column, NULL); gtk_tree_view_column_setup_sort_column_id_callback (tree_column); + g_object_notify (G_OBJECT (tree_column), "sort-column-id"); } /** diff --git a/gtk/gtktypeutils.c b/gtk/gtktypeutils.c index 986f9ff188..6047d6f218 100644 --- a/gtk/gtktypeutils.c +++ b/gtk/gtktypeutils.c @@ -125,6 +125,8 @@ gtk_type_init (GTypeDebugFlags debug_flags) if (!initialized) { + GType unused; + initialized = TRUE; /* initialize GLib type system @@ -133,7 +135,7 @@ gtk_type_init (GTypeDebugFlags debug_flags) /* GTK_TYPE_OBJECT */ - gtk_object_get_type (); + unused = gtk_object_get_type (); } } diff --git a/gtk/gtkviewport.c b/gtk/gtkviewport.c index 3e64564611..5fd9014ab4 100644 --- a/gtk/gtkviewport.c +++ b/gtk/gtkviewport.c @@ -556,6 +556,24 @@ gtk_viewport_get_shadow_type (GtkViewport *viewport) return viewport->shadow_type; } +/** + * gtk_viewport_get_bin_window: + * @viewport: a #GtkViewport + * + * Gets the bin window of the #GtkViewport. + * + * Return value: a #GdkWindow + * + * Since: 2.20 + **/ +GdkWindow* +gtk_viewport_get_bin_window (GtkViewport *viewport) +{ + g_return_val_if_fail (GTK_IS_VIEWPORT (viewport), NULL); + + return viewport->bin_window; +} + static void gtk_viewport_realize (GtkWidget *widget) { diff --git a/gtk/gtkviewport.h b/gtk/gtkviewport.h index a9300da5ba..da6b97c8d5 100644 --- a/gtk/gtkviewport.h +++ b/gtk/gtkviewport.h @@ -83,6 +83,7 @@ void gtk_viewport_set_vadjustment (GtkViewport *viewport, void gtk_viewport_set_shadow_type (GtkViewport *viewport, GtkShadowType type); GtkShadowType gtk_viewport_get_shadow_type (GtkViewport *viewport); +GdkWindow* gtk_viewport_get_bin_window (GtkViewport *viewport); G_END_DECLS diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index e263660eb2..5677fd73f4 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -154,7 +154,8 @@ enum { PROP_HAS_TOOLTIP, PROP_TOOLTIP_MARKUP, PROP_TOOLTIP_TEXT, - PROP_WINDOW + PROP_WINDOW, + PROP_DOUBLE_BUFFERED }; typedef struct _GtkStateData GtkStateData; @@ -692,6 +693,21 @@ gtk_widget_class_init (GtkWidgetClass *klass) GDK_TYPE_WINDOW, GTK_PARAM_READABLE)); + /** + * GtkWidget:double-buffered + * + * Whether or not the widget is double buffered. + * + * Since: 2.18 + */ + g_object_class_install_property (gobject_class, + PROP_DOUBLE_BUFFERED, + g_param_spec_boolean ("double-buffered", + P_("Double Buffered"), + P_("Whether or not the widget is double buffered"), + TRUE, + GTK_PARAM_READWRITE)); + widget_signals[SHOW] = g_signal_new (I_("show"), G_TYPE_FROM_CLASS (gobject_class), @@ -2399,11 +2415,10 @@ gtk_widget_set_property (GObject *object, switch (prop_id) { gboolean tmp; - guint32 saved_flags; gchar *tooltip_markup; const gchar *tooltip_text; GtkWindow *tooltip_window; - + case PROP_NAME: gtk_widget_set_name (widget, g_value_get_string (value)); break; @@ -2417,10 +2432,7 @@ gtk_widget_set_property (GObject *object, gtk_widget_set_usize_internal (widget, -2, g_value_get_int (value)); break; case PROP_VISIBLE: - if (g_value_get_boolean (value)) - gtk_widget_show (widget); - else - gtk_widget_hide (widget); + gtk_widget_set_visible (widget, g_value_get_boolean (value)); break; case PROP_SENSITIVE: gtk_widget_set_sensitive (widget, g_value_get_boolean (value)); @@ -2429,13 +2441,7 @@ gtk_widget_set_property (GObject *object, gtk_widget_set_app_paintable (widget, g_value_get_boolean (value)); break; case PROP_CAN_FOCUS: - saved_flags = GTK_WIDGET_FLAGS (widget); - if (g_value_get_boolean (value)) - GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS); - else - GTK_WIDGET_UNSET_FLAGS (widget, GTK_CAN_FOCUS); - if (saved_flags != GTK_WIDGET_FLAGS (widget)) - gtk_widget_queue_resize (widget); + gtk_widget_set_can_focus (widget, g_value_get_boolean (value)); break; case PROP_HAS_FOCUS: if (g_value_get_boolean (value)) @@ -2446,23 +2452,14 @@ gtk_widget_set_property (GObject *object, gtk_widget_grab_focus (widget); break; case PROP_CAN_DEFAULT: - saved_flags = GTK_WIDGET_FLAGS (widget); - if (g_value_get_boolean (value)) - GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_DEFAULT); - else - GTK_WIDGET_UNSET_FLAGS (widget, GTK_CAN_DEFAULT); - if (saved_flags != GTK_WIDGET_FLAGS (widget)) - gtk_widget_queue_resize (widget); + gtk_widget_set_can_default (widget, g_value_get_boolean (value)); break; case PROP_HAS_DEFAULT: if (g_value_get_boolean (value)) gtk_widget_grab_default (widget); break; case PROP_RECEIVES_DEFAULT: - if (g_value_get_boolean (value)) - GTK_WIDGET_SET_FLAGS (widget, GTK_RECEIVES_DEFAULT); - else - GTK_WIDGET_UNSET_FLAGS (widget, GTK_RECEIVES_DEFAULT); + gtk_widget_set_receives_default (widget, g_value_get_boolean (value)); break; case PROP_STYLE: gtk_widget_set_style (widget, g_value_get_object (value)); @@ -2523,6 +2520,9 @@ gtk_widget_set_property (GObject *object, if (GTK_WIDGET_VISIBLE (widget)) gtk_widget_queue_tooltip_query (widget); break; + case PROP_DOUBLE_BUFFERED: + gtk_widget_set_double_buffered (widget, g_value_get_boolean (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -2630,6 +2630,9 @@ gtk_widget_get_property (GObject *object, case PROP_WINDOW: g_value_set_object (value, gtk_widget_get_window (widget)); break; + case PROP_DOUBLE_BUFFERED: + g_value_set_boolean (value, gtk_widget_get_double_buffered (widget)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -5066,8 +5069,10 @@ _gtk_widget_grab_notify (GtkWidget *widget, * * Causes @widget to have the keyboard focus for the #GtkWindow it's * inside. @widget must be a focusable widget, such as a #GtkEntry; - * something like #GtkFrame won't work. (More precisely, it must have the - * %GTK_CAN_FOCUS flag set.) + * something like #GtkFrame won't work. + * + * More precisely, it must have the %GTK_CAN_FOCUS flag set. Use + * gtk_widget_set_can_focus() to modify that flag. **/ void gtk_widget_grab_focus (GtkWidget *widget) @@ -5259,6 +5264,74 @@ gtk_widget_real_keynav_failed (GtkWidget *widget, } /** + * gtk_widget_set_can_focus: + * @widget: a #GtkWidget + * @can_focus: whether or not @widget can own the input focus. + * + * Specifies whether @widget can own the input focus. See + * gtk_widget_grab_focus() for actually setting the input focus on a + * widget. + * + * Since: 2.18 + **/ +void +gtk_widget_set_can_focus (GtkWidget *widget, + gboolean can_focus) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + if (can_focus != GTK_WIDGET_CAN_FOCUS (widget)) + { + if (can_focus) + GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS); + else + GTK_WIDGET_UNSET_FLAGS (widget, GTK_CAN_FOCUS); + + gtk_widget_queue_resize (widget); + g_object_notify (G_OBJECT (widget), "can-focus"); + } +} + +/** + * gtk_widget_get_can_focus: + * @widget: a #GtkWidget + * + * Determines whether @widget can own the input focus. See + * gtk_widget_set_can_focus(). + * + * Return value: %TRUE if @widget can own the input focus, %FALSE otherwise + * + * Since: 2.18 + **/ +gboolean +gtk_widget_get_can_focus (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + return GTK_WIDGET_CAN_FOCUS (widget); +} + +/** + * gtk_widget_has_focus: + * @widget: a #GtkWidget + * + * Determines if the widget has the global input focus. See + * gtk_widget_is_focus() for the difference between having the global + * input focus, and only having the focus within a toplevel. + * + * Return value: %TRUE if the widget has the global input focus. + * + * Since: 2.18 + **/ +gboolean +gtk_widget_has_focus (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + return GTK_WIDGET_HAS_FOCUS (widget); +} + +/** * gtk_widget_is_focus: * @widget: a #GtkWidget * @@ -5285,13 +5358,81 @@ gtk_widget_is_focus (GtkWidget *widget) } /** + * gtk_widget_set_can_default: + * @widget: a #GtkWidget + * @can_default: whether or not @widget can be a default widget. + * + * Specifies whether @widget can be a default widget. See + * gtk_widget_grab_default() for details about the meaning of + * "default". + * + * Since: 2.18 + **/ +void +gtk_widget_set_can_default (GtkWidget *widget, + gboolean can_default) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + if (can_default != GTK_WIDGET_CAN_DEFAULT (widget)) + { + if (can_default) + GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_DEFAULT); + else + GTK_WIDGET_UNSET_FLAGS (widget, GTK_CAN_DEFAULT); + + gtk_widget_queue_resize (widget); + g_object_notify (G_OBJECT (widget), "can-default"); + } +} + +/** + * gtk_widget_get_can_default: + * @widget: a #GtkWidget + * + * Determines whether @widget can be a default widget. See + * gtk_widget_set_can_default(). + * + * Return value: %TRUE if @widget can be a default widget, %FALSE otherwise + * + * Since: 2.18 + **/ +gboolean +gtk_widget_get_can_default (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + return GTK_WIDGET_CAN_DEFAULT (widget); +} + +/** + * gtk_widget_has_default: + * @widget: a #GtkWidget + * + * Determines whether @widget is the current default widget within its + * toplevel. See gtk_widget_set_can_default(). + * + * Return value: %TRUE if @widget is the current default widget within + * its toplevel, %FALSE otherwise + * + * Since: 2.18 + */ +gboolean +gtk_widget_has_default (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + return GTK_WIDGET_HAS_DEFAULT (widget); +} + +/** * gtk_widget_grab_default: * @widget: a #GtkWidget * * Causes @widget to become the default widget. @widget must have the * %GTK_CAN_DEFAULT flag set; typically you have to set this flag - * yourself by calling <literal>GTK_WIDGET_SET_FLAGS (@widget, - * GTK_CAN_DEFAULT)</literal>. The default widget is activated when + * yourself by calling <literal>gtk_widget_set_can_default (@widget, + * %TRUE)</literal>. The default widget is activated when * the user presses Enter in a window. Default widgets must be * activatable, that is, gtk_widget_activate() should affect them. **/ @@ -5312,6 +5453,81 @@ gtk_widget_grab_default (GtkWidget *widget) } /** + * gtk_widget_set_receives_default: + * @widget: a #GtkWidget + * @receives_default: whether or not @widget can be a default widget. + * + * Specifies whether @widget will be treated as the default widget + * within its toplevel when it has the focus, even if another widget + * is the default. + * + * See gtk_widget_grab_default() for details about the meaning of + * "default". + * + * Since: 2.18 + **/ +void +gtk_widget_set_receives_default (GtkWidget *widget, + gboolean receives_default) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + if (receives_default != gtk_widget_get_receives_default (widget)) + { + if (receives_default) + GTK_WIDGET_SET_FLAGS (widget, GTK_RECEIVES_DEFAULT); + else + GTK_WIDGET_UNSET_FLAGS (widget, GTK_RECEIVES_DEFAULT); + + g_object_notify (G_OBJECT (widget), "receives-default"); + } +} + +/** + * gtk_widget_get_receives_default: + * @widget: a #GtkWidget + * + * Determines whether @widget is alyways treated as default widget + * withing its toplevel when it has the focus, even if another widget + * is the default. + * + * See gtk_widget_set_receives_default(). + * + * Return value: %TRUE if @widget acts as default widget when focussed, + * %FALSE otherwise + * + * Since: 2.18 + **/ +gboolean +gtk_widget_get_receives_default (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + return (GTK_WIDGET_FLAGS (widget) & GTK_RECEIVES_DEFAULT) != 0; +} + +/** + * gtk_widget_has_grab: + * @widget: a #GtkWidget + * + * Determines whether the widget is currently grabbing events, so it + * is the only widget receiving input events (keyboard and mouse). + * + * See also gtk_grab_add(). + * + * Return value: %TRUE if the widget is in the grab_widgets stack + * + * Since: 2.18 + **/ +gboolean +gtk_widget_has_grab (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + return (GTK_WIDGET_FLAGS (widget) & GTK_HAS_GRAB) != 0; +} + +/** * gtk_widget_set_name: * @widget: a #GtkWidget * @name: name for the widget @@ -5401,6 +5617,164 @@ gtk_widget_set_state (GtkWidget *widget, } } +/** + * gtk_widget_get_state: + * @widget: a #GtkWidget + * + * Returns the widget's state. See gtk_widget_set_state(). + * + * Returns: the state of @widget. + * + * Since: 2.18 + */ +GtkStateType +gtk_widget_get_state (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), GTK_STATE_NORMAL); + + return widget->state; +} + +/** + * gtk_widget_set_visible: + * @widget: a #GtkWidget + * @visible: whether the widget should be shown or not + * + * Sets the visibility state of @widget. Note that setting this to + * %TRUE doesn't mean the widget is actually viewable, see + * gtk_widget_get_visible(). + * + * This function simply calls gtk_widget_show() or gtk_widget_hide() + * but is nicer to use when the visibility of the widget depends on + * some condition. + * + * Since: 2.18 + **/ +void +gtk_widget_set_visible (GtkWidget *widget, + gboolean visible) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + if (visible != GTK_WIDGET_VISIBLE (widget)) + { + if (visible) + gtk_widget_show (widget); + else + gtk_widget_hide (widget); + } +} + +/** + * gtk_widget_get_visible: + * @widget: a #GtkWidget + * + * Determines whether the widget is visible. Note that this doesn't + * take into account whether the widget's parent is also visible + * or the widget is obscured in any way. + * + * See gtk_widget_set_visible(). + * + * Return value: %TRUE if the widget is visible + * + * Since: 2.18 + **/ +gboolean +gtk_widget_get_visible (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + return (GTK_WIDGET_FLAGS (widget) & GTK_VISIBLE) != 0; +} + +/** + * gtk_widget_set_has_window: + * @widget: a #GtkWidget + * @has_window: whether or not @widget has a window. + * + * Specifies whether @widget has a #GdkWindow of its own. Note that + * all realized widgets have a non-%NULL "window" pointer + * (gtk_widget_get_window() never returns a %NULL window when a widget + * is realized), but for many of them it's actually the #GdkWindow of + * one of its parent widgets. Widgets that create a %window for + * themselves in GtkWidget::realize() however must announce this by + * calling this function with @has_window = %TRUE. + * + * This function should only be called by widget implementations, + * and they should call it in their init() function. + * + * Since: 2.18 + **/ +void +gtk_widget_set_has_window (GtkWidget *widget, + gboolean has_window) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + + if (has_window) + GTK_WIDGET_UNSET_FLAGS (widget, GTK_NO_WINDOW); + else + GTK_WIDGET_SET_FLAGS (widget, GTK_NO_WINDOW); +} + +/** + * gtk_widget_get_has_window: + * @widget: a #GtkWidget + * + * Determines whether @widget has a #GdkWindow of its own. See + * gtk_widget_set_has_window(). + * + * Return value: %TRUE if @widget has a window, %FALSE otherwise + * + * Since: 2.18 + **/ +gboolean +gtk_widget_get_has_window (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + return !GTK_WIDGET_NO_WINDOW (widget); +} + +/** + * gtk_widget_is_toplevel: + * @widget: a #GtkWidget + * + * Determines whether @widget is a toplevel widget. Currently only + * #GtkWindow and #GtkInvisible are toplevel widgets. Toplevel + * widgets have no parent widget. + * + * Return value: %TRUE if @widget is a toplevel, %FALSE otherwise + * + * Since: 2.18 + **/ +gboolean +gtk_widget_is_toplevel (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + return (GTK_WIDGET_FLAGS (widget) & GTK_TOPLEVEL) != 0; +} + +/** + * gtk_widget_is_drawable: + * @widget: a #GtkWidget + * + * Determines whether @widget can be drawn to. A widget can be drawn + * to if it is mapped and visible. + * + * Return value: %TRUE if @widget is drawable, %FALSE otherwise + * + * Since: 2.18 + **/ +gboolean +gtk_widget_is_drawable (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + return ((GTK_WIDGET_FLAGS (widget) & GTK_VISIBLE) != 0 && + (GTK_WIDGET_FLAGS (widget) & GTK_MAPPED) != 0); +} /** * gtk_widget_set_app_paintable: @@ -5449,6 +5823,27 @@ gtk_widget_set_app_paintable (GtkWidget *widget, } /** + * gtk_widget_get_app_paintable: + * @widget: a #GtkWidget + * + * Determines whether the application intends to draw on the widget in + * an #GtkWidget::expose-event handler. + * + * See gtk_widget_set_app_paintable() + * + * Return value: %TRUE if the widget is app paintable + * + * Since: 2.18 + **/ +gboolean +gtk_widget_get_app_paintable (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + return (GTK_WIDGET_FLAGS (widget) & GTK_APP_PAINTABLE) != 0; +} + +/** * gtk_widget_set_double_buffered: * @widget: a #GtkWidget * @double_buffered: %TRUE to double-buffer a widget @@ -5478,10 +5873,35 @@ gtk_widget_set_double_buffered (GtkWidget *widget, { g_return_if_fail (GTK_IS_WIDGET (widget)); - if (double_buffered) - GTK_WIDGET_SET_FLAGS (widget, GTK_DOUBLE_BUFFERED); - else - GTK_WIDGET_UNSET_FLAGS (widget, GTK_DOUBLE_BUFFERED); + if (double_buffered != GTK_WIDGET_DOUBLE_BUFFERED (widget)) + { + if (double_buffered) + GTK_WIDGET_SET_FLAGS (widget, GTK_DOUBLE_BUFFERED); + else + GTK_WIDGET_UNSET_FLAGS (widget, GTK_DOUBLE_BUFFERED); + + g_object_notify (G_OBJECT (widget), "double-buffered"); + } +} + +/** + * gtk_widget_get_double_buffered: + * @widget: a #GtkWidget + * + * Determines whether the widget is double buffered. + * + * See gtk_widget_set_double_buffered() + * + * Return value: %TRUE if the widget is double buffered + * + * Since: 2.18 + **/ +gboolean +gtk_widget_get_double_buffered (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + return (GTK_WIDGET_FLAGS (widget) & GTK_DOUBLE_BUFFERED) != 0; } /** @@ -5567,6 +5987,47 @@ gtk_widget_set_sensitive (GtkWidget *widget, } /** + * gtk_widget_get_sensitive: + * @widget: a #GtkWidget + * + * Returns the widget's sensitivity (in the sense of returning + * the value that has been set using gtk_widget_set_sensitive()). + * + * The effective sensitivity of a widget is however determined by both its + * own and its parent widget's sensitivity. See gtk_widget_is_sensitive(). + * + * Returns: %TRUE if the widget is sensitive + * + * Since: 2.18 + */ +gboolean +gtk_widget_get_sensitive (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + return GTK_WIDGET_SENSITIVE (widget); +} + +/** + * gtk_widget_is_sensitive: + * @widget: a #GtkWidget + * + * Returns the widget's effective sensitivity, which means + * it is sensitive itself and also its parent widget is sensntive + * + * Returns: %TRUE if the widget is effectively sensitive + * + * Since: 2.18 + */ +gboolean +gtk_widget_is_sensitive (GtkWidget *widget) +{ + g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); + + return GTK_WIDGET_IS_SENSITIVE (widget); +} + +/** * gtk_widget_set_parent: * @widget: a #GtkWidget * @parent: parent container @@ -10142,6 +10603,9 @@ gtk_widget_real_set_has_tooltip (GtkWidget *widget, * the default tooltip window. If @custom_window is %NULL, the default * tooltip window will be used. * + * If the custom window should have the default theming it needs to + * have the name "gtk-tooltip", see gtk_widget_set_name(). + * * Since: 2.12 */ void @@ -10371,6 +10835,76 @@ gtk_widget_get_has_tooltip (GtkWidget *widget) } /** + * gtk_widget_get_allocation: + * @widget: a #GtkWidget + * @allocation: a pointer to a #GtkAllocation to copy to + * + * Retrieves the widget's allocation. + * + * Since: 2.18 + */ +void +gtk_widget_get_allocation (GtkWidget *widget, + GtkAllocation *allocation) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (allocation != NULL); + + *allocation = widget->allocation; +} + +/** + * gtk_widget_set_allocation: + * @widget: a #GtkWidget + * @allocation: a pointer to a #GtkAllocation to copy from + * + * Sets the widget's allocation. This should not be used + * directly, but from within a widget's size_allocate method. + * + * Since: 2.18 + */ +void +gtk_widget_set_allocation (GtkWidget *widget, + const GtkAllocation *allocation) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (allocation != NULL); + + widget->allocation = *allocation; +} + +/** + * gtk_widget_set_window: + * @widget: a #GtkWidget + * @window: a #GdkWindow + * + * Sets a widget's window. This function should only be used in a + * widget's GtkWidget::realize() implementation. The %window passed is + * usually either new window created with gdk_window_new(), or the + * window of its parent widget as returned by + * gtk_widget_get_parent_window(). + * + * Widgets must indicate whether they will create their own #GdkWindow + * by calling gtk_widget_set_has_window(). This is usually done in the + * widget's init() function. + * + * Since: 2.18 + */ +void +gtk_widget_set_window (GtkWidget *widget, + GdkWindow *window) +{ + g_return_if_fail (GTK_IS_WIDGET (widget)); + g_return_if_fail (window == NULL || GDK_IS_WINDOW (window)); + + if (widget->window != window) + { + widget->window = window; + g_object_notify (G_OBJECT (widget), "window"); + } +} + +/** * gtk_widget_get_window: * @widget: a #GtkWidget * diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index 2d89fb0e29..ed286636d5 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -548,33 +548,80 @@ void gtk_widget_child_notify (GtkWidget *widget, const gchar *child_property); void gtk_widget_thaw_child_notify (GtkWidget *widget); +void gtk_widget_set_can_focus (GtkWidget *widget, + gboolean can_focus); +gboolean gtk_widget_get_can_focus (GtkWidget *widget); +gboolean gtk_widget_has_focus (GtkWidget *widget); gboolean gtk_widget_is_focus (GtkWidget *widget); -void gtk_widget_grab_focus (GtkWidget *widget); -void gtk_widget_grab_default (GtkWidget *widget); +void gtk_widget_grab_focus (GtkWidget *widget); + +void gtk_widget_set_can_default (GtkWidget *widget, + gboolean can_default); +gboolean gtk_widget_get_can_default (GtkWidget *widget); +gboolean gtk_widget_has_default (GtkWidget *widget); +void gtk_widget_grab_default (GtkWidget *widget); + +void gtk_widget_set_receives_default (GtkWidget *widget, + gboolean receives_default); +gboolean gtk_widget_get_receives_default (GtkWidget *widget); + +gboolean gtk_widget_has_grab (GtkWidget *widget); void gtk_widget_set_name (GtkWidget *widget, const gchar *name); G_CONST_RETURN gchar* gtk_widget_get_name (GtkWidget *widget); + void gtk_widget_set_state (GtkWidget *widget, GtkStateType state); +GtkStateType gtk_widget_get_state (GtkWidget *widget); + void gtk_widget_set_sensitive (GtkWidget *widget, gboolean sensitive); +gboolean gtk_widget_get_sensitive (GtkWidget *widget); +gboolean gtk_widget_is_sensitive (GtkWidget *widget); + +void gtk_widget_set_visible (GtkWidget *widget, + gboolean visible); +gboolean gtk_widget_get_visible (GtkWidget *widget); + +void gtk_widget_set_has_window (GtkWidget *widget, + gboolean has_window); +gboolean gtk_widget_get_has_window (GtkWidget *widget); + +gboolean gtk_widget_is_toplevel (GtkWidget *widget); +gboolean gtk_widget_is_drawable (GtkWidget *widget); + void gtk_widget_set_app_paintable (GtkWidget *widget, gboolean app_paintable); +gboolean gtk_widget_get_app_paintable (GtkWidget *widget); + void gtk_widget_set_double_buffered (GtkWidget *widget, gboolean double_buffered); +gboolean gtk_widget_get_double_buffered (GtkWidget *widget); + void gtk_widget_set_redraw_on_allocate (GtkWidget *widget, gboolean redraw_on_allocate); + void gtk_widget_set_parent (GtkWidget *widget, GtkWidget *parent); GtkWidget * gtk_widget_get_parent (GtkWidget *widget); + void gtk_widget_set_parent_window (GtkWidget *widget, GdkWindow *parent_window); GdkWindow * gtk_widget_get_parent_window (GtkWidget *widget); + void gtk_widget_set_child_visible (GtkWidget *widget, gboolean is_visible); gboolean gtk_widget_get_child_visible (GtkWidget *widget); -GdkWindow* gtk_widget_get_window (GtkWidget *widget); + +void gtk_widget_set_window (GtkWidget *widget, + GdkWindow *window); +GdkWindow * gtk_widget_get_window (GtkWidget *widget); + +void gtk_widget_get_allocation (GtkWidget *widget, + GtkAllocation *allocation); +void gtk_widget_set_allocation (GtkWidget *widget, + const GtkAllocation *allocation); gboolean gtk_widget_child_focus (GtkWidget *widget, GtkDirectionType direction); @@ -816,8 +863,8 @@ GtkRequisition *gtk_requisition_copy (const GtkRequisition *requisition); void gtk_requisition_free (GtkRequisition *requisition); #if defined (GTK_TRACE_OBJECTS) && defined (__GNUC__) -# define gtk_widget_ref gtk_object_ref -# define gtk_widget_unref gtk_object_unref +# define gtk_widget_ref g_object_ref +# define gtk_widget_unref g_object_unref #endif /* GTK_TRACE_OBJECTS && __GNUC__ */ void _gtk_widget_grab_notify (GtkWidget *widget, diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 1238c2295c..e0e6afe196 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -496,7 +496,7 @@ gtk_window_class_init (GtkWindowClass *klass) P_("Unique identifier for the window to be used when restoring a session"), NULL, GTK_PARAM_READWRITE)); - + /** * GtkWindow:startup-id: * @@ -505,9 +505,9 @@ gtk_window_class_init (GtkWindowClass *klass) * for more details. * * Since: 2.12 - */ + */ g_object_class_install_property (gobject_class, - PROP_ROLE, + PROP_STARTUP_ID, g_param_spec_string ("startup-id", P_("Startup ID"), P_("Unique startup identifier for the window used by startup-notification"), @@ -4400,6 +4400,7 @@ static void gtk_window_finalize (GObject *object) { GtkWindow *window = GTK_WINDOW (object); + GtkWindowPrivate *priv = GTK_WINDOW_GET_PRIVATE (window); GtkMnemonicHash *mnemonic_hash; g_free (window->title); @@ -4427,11 +4428,11 @@ gtk_window_finalize (GObject *object) } if (window->screen) - { - g_signal_handlers_disconnect_by_func (window->screen, - gtk_window_on_composited_changed, window); - } - + g_signal_handlers_disconnect_by_func (window->screen, + gtk_window_on_composited_changed, window); + + g_free (priv->startup_id); + G_OBJECT_CLASS (gtk_window_parent_class)->finalize (object); } @@ -4601,7 +4602,8 @@ gtk_window_map (GtkWidget *widget) /* Make sure we have a "real" id */ if (!startup_id_is_fake (priv->startup_id)) gdk_notify_startup_complete_with_id (priv->startup_id); - + + g_free (priv->startup_id); priv->startup_id = NULL; } else if (!sent_startup_notification) @@ -5646,6 +5648,13 @@ gtk_window_compute_configure_request_size (GtkWindow *window, if (info->resize_height > 0) *height = info->resize_height; } + + /* Don't ever request zero width or height, its not supported by + gdk. The size allocation code will round it to 1 anyway but if + we do it then the value returned from this function will is + not comparable to the size allocation read from the GtkWindow. */ + *width = MAX (*width, 1); + *height = MAX (*height, 1); } static GtkWindowPosition @@ -5744,7 +5753,7 @@ clamp_window_to_rectangle (gint *x, const GdkRectangle *rect) { #ifdef DEBUGGING_OUTPUT - g_print ("%s: %+d%+d %dx%d: %+d%+d: %dx%d", __FUNCTION__, rect->x, rect->y, rect->width, rect->height, *x, *y, w, h); + g_print ("%s: %+d%+d %dx%d: %+d%+d: %dx%d", G_STRFUNC, rect->x, rect->y, rect->width, rect->height, *x, *y, w, h); #endif /* If it is too large, center it. If it fits on the monitor but is @@ -8319,7 +8328,7 @@ _gtk_window_set_is_active (GtkWindow *window, } /** - * _gtk_windwo_set_is_toplevel: + * _gtk_window_set_is_toplevel: * @window: a #GtkWindow * @is_toplevel: %TRUE if the window is still a real toplevel (nominally a * parent of the root window); %FALSE if it is not (for example, for an diff --git a/gtk/makefile.msc.in b/gtk/makefile.msc.in index 0227fa7f0f..bb6ac9999e 100644 --- a/gtk/makefile.msc.in +++ b/gtk/makefile.msc.in @@ -572,10 +572,10 @@ gtk.def: gtk.symbols makefile.msc -DG_GNUC_PRINTF=;G_GNUC_PRINTF gtk.symbols >> gtk.def gtkalias.h: gtk.symbols - cl /EP -DG_OS_WIN32 -DGTK_WINDOWING_WIN32 -DINCLUDE_INTERNAL_SYMBOLS gtk.symbols | $(PERL) makegtkalias.pl > gtkalias.h + $(PERL) makegtkalias.pl < gtk.symbols > gtkalias.h gtkaliasdef.c: gtk.symbols - perl makegtkalias.pl -def < gtk.symbols > gtkaliasdef.c + $(PERL) makegtkalias.pl -def < gtk.symbols > gtkaliasdef.c # generate type identifier header (GTK_TYPE_WIDGET_FLAGS) # use 'echo' to work around 'command line too long' diff --git a/gtk/tests/Makefile.am b/gtk/tests/Makefile.am index 4c8ff1f1a6..c287c258df 100644 --- a/gtk/tests/Makefile.am +++ b/gtk/tests/Makefile.am @@ -34,6 +34,10 @@ TEST_PROGS += treestore treestore_SOURCES = treestore.c treestore_LDADD = $(progs_ldadd) +TEST_PROGS += treeview +treeview_SOURCES = treeview.c +treeview_LDADD = $(progs_ldadd) + TEST_PROGS += treeview-scrolling treeview_scrolling_SOURCES = treeview-scrolling.c treeview_scrolling_LDADD = $(progs_ldadd) @@ -78,4 +82,12 @@ TEST_PROGS += textbuffer textbuffer_SOURCES = textbuffer.c pixbuf-init.c textbuffer_LDADD = $(progs_ldadd) +TEST_PROGS += filtermodel +filtermodel_SOURCES = filtermodel.c +filtermodel_LDADD = $(progs_ldadd) + +TEST_PROGS += expander +expander_SOURCES = expander.c +expander_LDADD = $(progs_ldadd) + -include $(top_srcdir)/git.mk diff --git a/gtk/tests/builder.c b/gtk/tests/builder.c index f5a85ac4fa..2ae604d5e6 100644 --- a/gtk/tests/builder.c +++ b/gtk/tests/builder.c @@ -116,6 +116,13 @@ test_parser (void) GTK_BUILDER_ERROR_INVALID_VALUE)); g_error_free (error); + error = NULL; + gtk_builder_add_from_string (builder, "<interface><object class=\"GtkButton\" id=\"a\"></object><object class=\"GtkButton\" id=\"a\"/></object></interface>", -1, &error); + g_assert (g_error_matches (error, + GTK_BUILDER_ERROR, + GTK_BUILDER_ERROR_DUPLICATE_ID)); + g_error_free (error); + g_object_unref (builder); } @@ -1944,6 +1951,8 @@ test_icon_factory (void) gtk_icon_factory_add_default (GTK_ICON_FACTORY (factory)); image = gtk_image_new_from_stock ("apple-red", GTK_ICON_SIZE_BUTTON); g_assert (image != NULL); + g_object_ref_sink (image); + g_object_unref (image); g_object_unref (builder); @@ -2129,6 +2138,7 @@ test_requires (void) GTK_BUILDER_ERROR_VERSION_MISMATCH)); g_object_unref (builder); g_error_free (error); + g_free (buffer); } static void @@ -2172,7 +2182,7 @@ test_add_objects (void) " </object>" " <object class=\"GtkWindow\" id=\"window2\">" " <child>" - " <object class=\"GtkLabel\" id=\"label1\">" + " <object class=\"GtkLabel\" id=\"label3\">" " <property name=\"label\" translatable=\"no\">second label</property>" " </object>" " </child>" diff --git a/gtk/tests/defaultvalue.c b/gtk/tests/defaultvalue.c index 6a157e42ae..a3534b176d 100644 --- a/gtk/tests/defaultvalue.c +++ b/gtk/tests/defaultvalue.c @@ -173,8 +173,8 @@ test_type (gconstpointer data) /* Default invisible char is determined at runtime */ if (g_type_is_a (type, GTK_TYPE_ENTRY) && - strcmp (pspec->name, "invisible-char") == 0 || - strcmp (pspec->name, "buffer") == 0) + (strcmp (pspec->name, "invisible-char") == 0 || + strcmp (pspec->name, "buffer") == 0)) continue; /* Gets set to the cwd */ @@ -239,7 +239,10 @@ test_type (gconstpointer data) strcmp (pspec->name, "gtk-icon-theme-name") == 0 || strcmp (pspec->name, "gtk-im-module") == 0 || strcmp (pspec->name, "gtk-key-theme-name") == 0 || - strcmp (pspec->name, "gtk-theme-name") == 0)) + strcmp (pspec->name, "gtk-theme-name") == 0 || + strcmp (pspec->name, "gtk-sound-theme-name") == 0 || + strcmp (pspec->name, "gtk-enable-input-feedback-sounds") == 0 || + strcmp (pspec->name, "gtk-enable-event-sounds") == 0)) continue; if (g_type_is_a (type, GTK_TYPE_SPIN_BUTTON) && diff --git a/gtk/tests/expander.c b/gtk/tests/expander.c new file mode 100644 index 0000000000..853b20aada --- /dev/null +++ b/gtk/tests/expander.c @@ -0,0 +1,94 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-2001. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ +#include <gtk/gtk.h> + +static void +test_click_expander (void) +{ + GtkWidget *window = gtk_test_create_simple_window ("Test Window", "Test click on expander"); + GtkWidget *expander = gtk_expander_new ("Test Expander"); + GtkWidget *label = gtk_label_new ("Test Label"); + gboolean expanded; + gboolean simsuccess; + gtk_container_add (GTK_CONTAINER (expander), label); + gtk_container_add (GTK_CONTAINER (GTK_BIN (window)->child), expander); + gtk_widget_show (expander); + gtk_widget_show (label); + gtk_widget_show_now (window); + /* check initial expander state */ + expanded = gtk_expander_get_expanded (GTK_EXPANDER (expander)); + g_assert (!expanded); + /* check expanding */ + simsuccess = gtk_test_widget_click (expander, 1, 0); + g_assert (simsuccess == TRUE); + while (gtk_events_pending ()) /* let expander timeout/idle handlers update */ + gtk_main_iteration (); + expanded = gtk_expander_get_expanded (GTK_EXPANDER (expander)); + g_assert (expanded); + /* check collapsing */ + simsuccess = gtk_test_widget_click (expander, 1, 0); + g_assert (simsuccess == TRUE); + while (gtk_events_pending ()) /* let expander timeout/idle handlers update */ + gtk_main_iteration (); + expanded = gtk_expander_get_expanded (GTK_EXPANDER (expander)); + g_assert (!expanded); +} + +static void +test_click_content_widget (void) +{ + GtkWidget *window = gtk_test_create_simple_window ("Test Window", "Test click on content widget"); + GtkWidget *expander = gtk_expander_new ("Test Expander"); + GtkWidget *entry = gtk_entry_new (); + gboolean expanded; + gboolean simsuccess; + gtk_container_add (GTK_CONTAINER (expander), entry); + gtk_container_add (GTK_CONTAINER (GTK_BIN (window)->child), expander); + gtk_expander_set_expanded (GTK_EXPANDER (expander), TRUE); + gtk_widget_show (expander); + gtk_widget_show (entry); + gtk_widget_show_now (window); + + /* check click on content with expander open */ + expanded = gtk_expander_get_expanded (GTK_EXPANDER (expander)); + g_assert (expanded); + simsuccess = gtk_test_widget_click (entry, 1, 0); + g_assert (simsuccess == TRUE); + while (gtk_events_pending ()) /* let expander timeout/idle handlers update */ + gtk_main_iteration (); + expanded = gtk_expander_get_expanded (GTK_EXPANDER (expander)); + g_assert (expanded); +} + +int +main (int argc, + char *argv[]) +{ + gtk_test_init (&argc, &argv); + g_test_add_func ("/expander/click-expander", test_click_expander); + g_test_add_func ("/expander/click-content-widget", test_click_content_widget); + return g_test_run(); +} diff --git a/gtk/tests/filtermodel.c b/gtk/tests/filtermodel.c new file mode 100644 index 0000000000..22253e0144 --- /dev/null +++ b/gtk/tests/filtermodel.c @@ -0,0 +1,3000 @@ +/* Extensive GtkTreeModelFilter tests. + * Copyright (C) 2009 Kristian Rietveld <kris@gtk.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> + + +/* Left to do: + * - Proper coverage checking to see if the unit tests cover + * all possible cases. + * - Verify if the ref counting is done properly for both the + * normal ref_count and the zero_ref_count. One way to test + * this area is by collapsing/expanding branches on the view + * that is connected to the filter model. + * - Check if the iterator stamp is incremented at the correct times. + */ + + +/* + * Model creation + */ + +#define LEVEL_LENGTH 5 + +static void +create_tree_store_set_values (GtkTreeStore *store, + GtkTreeIter *iter, + gboolean visible) +{ + GtkTreePath *path; + gchar *path_string; + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), iter); + path_string = gtk_tree_path_to_string (path); + + gtk_tree_store_set (store, iter, + 0, path_string, + 1, visible, + -1); + + gtk_tree_path_free (path); + g_free (path_string); +} + +static void +create_tree_store_recurse (int depth, + GtkTreeStore *store, + GtkTreeIter *parent, + gboolean visible) +{ + int i; + + for (i = 0; i < LEVEL_LENGTH; i++) + { + GtkTreeIter iter; + + gtk_tree_store_insert (store, &iter, parent, i); + create_tree_store_set_values (store, &iter, visible); + + if (depth > 0) + create_tree_store_recurse (depth - 1, store, &iter, visible); + } +} + +static GtkTreeStore * +create_tree_store (int depth, + gboolean visible) +{ + GtkTreeStore *store; + + store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN); + + create_tree_store_recurse (depth, store, NULL, visible); + + return store; +} + +/* + * Signal monitor + */ + +typedef enum +{ + ROW_INSERTED, + ROW_DELETED, + ROW_CHANGED, + ROW_HAS_CHILD_TOGGLED, + ROWS_REORDERED, + LAST_SIGNAL +} +SignalName; + +static const char * +signal_name_to_string (SignalName signal) +{ + switch (signal) + { + case ROW_INSERTED: + return "row-inserted"; + + case ROW_DELETED: + return "row-deleted"; + + case ROW_CHANGED: + return "row-changed"; + + case ROW_HAS_CHILD_TOGGLED: + return "row-has-child-toggled"; + + case ROWS_REORDERED: + return "rows-reordered"; + + default: + /* Fall through */ + break; + } + + return "(unknown)"; +} + +typedef struct +{ + SignalName signal; + GtkTreePath *path; +} +Signal; + + +static Signal * +signal_new (SignalName signal, GtkTreePath *path) +{ + Signal *s; + + s = g_new0 (Signal, 1); + s->signal = signal; + s->path = gtk_tree_path_copy (path); + + return s; +} + +static void +signal_free (Signal *s) +{ + if (s->path) + gtk_tree_path_free (s->path); + + g_free (s); +} + + +typedef struct +{ + GQueue *queue; + GtkTreeModel *client; + guint signal_ids[LAST_SIGNAL]; +} +SignalMonitor; + + +static void +signal_monitor_generic_handler (SignalMonitor *m, + SignalName signal, + GtkTreeModel *model, + GtkTreePath *path) +{ + Signal *s; + + if (g_queue_is_empty (m->queue)) + { + g_error ("Signal queue empty\n"); + g_assert_not_reached (); + } + + if (m->client != model) + { + g_error ("Model mismatch; expected %p, got %p\n", + m->client, model); + g_assert_not_reached (); + } + + s = g_queue_peek_tail (m->queue); + +#if 0 + /* For debugging: output signals that are coming in. Leaks memory. */ + g_print ("signal=%d path=%s\n", signal, gtk_tree_path_to_string (path)); +#endif + + if (s->signal != signal + || gtk_tree_path_compare (s->path, path) != 0) + { + gchar *path_str, *s_path_str; + + s_path_str = gtk_tree_path_to_string (s->path); + path_str = gtk_tree_path_to_string (path); + + g_error ("Signals don't match; expected signal %s path %s, got signal %s path %s\n", + signal_name_to_string (s->signal), s_path_str, + signal_name_to_string (signal), path_str); + + g_free (s_path_str); + g_free (path_str); + + g_assert_not_reached (); + } + + s = g_queue_pop_tail (m->queue); + + signal_free (s); +} + +static void +signal_monitor_row_inserted (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + signal_monitor_generic_handler (data, ROW_INSERTED, + model, path); +} + +static void +signal_monitor_row_deleted (GtkTreeModel *model, + GtkTreePath *path, + gpointer data) +{ + signal_monitor_generic_handler (data, ROW_DELETED, + model, path); +} + +static void +signal_monitor_row_changed (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + signal_monitor_generic_handler (data, ROW_CHANGED, + model, path); +} + +static void +signal_monitor_row_has_child_toggled (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + signal_monitor_generic_handler (data, ROW_HAS_CHILD_TOGGLED, + model, path); +} + +static void +signal_monitor_rows_reordered (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gint *new_order, + gpointer data) +{ + signal_monitor_generic_handler (data, ROWS_REORDERED, + model, path); +} + +static SignalMonitor * +signal_monitor_new (GtkTreeModel *client) +{ + SignalMonitor *m; + + m = g_new0 (SignalMonitor, 1); + m->client = g_object_ref (client); + m->queue = g_queue_new (); + + m->signal_ids[ROW_INSERTED] = g_signal_connect (client, + "row-inserted", + G_CALLBACK (signal_monitor_row_inserted), + m); + m->signal_ids[ROW_DELETED] = g_signal_connect (client, + "row-deleted", + G_CALLBACK (signal_monitor_row_deleted), + m); + m->signal_ids[ROW_CHANGED] = g_signal_connect (client, + "row-changed", + G_CALLBACK (signal_monitor_row_changed), + m); + m->signal_ids[ROW_HAS_CHILD_TOGGLED] = g_signal_connect (client, + "row-has-child-toggled", + G_CALLBACK (signal_monitor_row_has_child_toggled), + m); + m->signal_ids[ROWS_REORDERED] = g_signal_connect (client, + "rows-reordered", + G_CALLBACK (signal_monitor_rows_reordered), + m); + + return m; +} + +static void +signal_monitor_free (SignalMonitor *m) +{ + int i; + + for (i = 0; i < LAST_SIGNAL; i++) + g_signal_handler_disconnect (m->client, m->signal_ids[i]); + + g_object_unref (m->client); + + if (m->queue) + g_queue_free (m->queue); + + g_free (m); +} + +static void +signal_monitor_assert_is_empty (SignalMonitor *m) +{ + g_assert (g_queue_is_empty (m->queue)); +} + +static void +signal_monitor_append_signal_path (SignalMonitor *m, + SignalName signal, + GtkTreePath *path) +{ + Signal *s; + + s = signal_new (signal, path); + g_queue_push_head (m->queue, s); +} + +static void +signal_monitor_append_signal (SignalMonitor *m, + SignalName signal, + const gchar *path_string) +{ + Signal *s; + GtkTreePath *path; + + path = gtk_tree_path_new_from_string (path_string); + + s = signal_new (signal, path); + g_queue_push_head (m->queue, s); + + gtk_tree_path_free (path); +} + +/* + * Fixture + */ + +typedef struct +{ + GtkWidget *tree_view; + + GtkTreeStore *store; + GtkTreeModelFilter *filter; + + SignalMonitor *monitor; + + guint block_signals : 1; +} FilterTest; + + +static void +filter_test_store_signal (FilterTest *fixture) +{ + if (fixture->block_signals) + g_signal_stop_emission_by_name (fixture->store, "row-changed"); +} + + +static void +filter_test_setup_generic (FilterTest *fixture, + gconstpointer test_data, + int depth, + gboolean empty, + gboolean unfiltered) +{ + const GtkTreePath *vroot = test_data; + GtkTreeModel *filter; + + fixture->store = create_tree_store (depth, !empty); + + g_signal_connect_swapped (fixture->store, "row-changed", + G_CALLBACK (filter_test_store_signal), fixture); + + /* Please forgive me for casting const away. */ + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture->store), + (GtkTreePath *)vroot); + fixture->filter = GTK_TREE_MODEL_FILTER (filter); + + if (!unfiltered) + gtk_tree_model_filter_set_visible_column (fixture->filter, 1); + + /* We need a tree view that's listening to get ref counting from that + * side. + */ + fixture->tree_view = gtk_tree_view_new_with_model (filter); + + fixture->monitor = signal_monitor_new (filter); +} + +static void +filter_test_setup (FilterTest *fixture, + gconstpointer test_data) +{ + filter_test_setup_generic (fixture, test_data, 3, FALSE, FALSE); +} + +static void +filter_test_setup_empty (FilterTest *fixture, + gconstpointer test_data) +{ + filter_test_setup_generic (fixture, test_data, 3, TRUE, FALSE); +} + +static void +filter_test_setup_unfiltered (FilterTest *fixture, + gconstpointer test_data) +{ + filter_test_setup_generic (fixture, test_data, 3, FALSE, TRUE); +} + +static void +filter_test_setup_empty_unfiltered (FilterTest *fixture, + gconstpointer test_data) +{ + filter_test_setup_generic (fixture, test_data, 3, TRUE, TRUE); +} + +static GtkTreePath * +strip_virtual_root (GtkTreePath *path, + GtkTreePath *root_path) +{ + GtkTreePath *real_path; + + if (root_path) + { + int j; + int depth = gtk_tree_path_get_depth (path); + int root_depth = gtk_tree_path_get_depth (root_path); + + real_path = gtk_tree_path_new (); + + for (j = 0; j < depth - root_depth; j++) + gtk_tree_path_append_index (real_path, + gtk_tree_path_get_indices (path)[root_depth + j]); + } + else + real_path = gtk_tree_path_copy (path); + + return real_path; +} + +static void +filter_test_append_refilter_signals_recurse (FilterTest *fixture, + GtkTreePath *store_path, + GtkTreePath *filter_path, + int depth, + GtkTreePath *root_path) +{ + int i; + int rows_deleted = 0; + GtkTreeIter iter; + + gtk_tree_path_down (store_path); + gtk_tree_path_down (filter_path); + + gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store), + &iter, store_path); + + for (i = 0; i < LEVEL_LENGTH; i++) + { + gboolean visible; + GtkTreePath *real_path; + + gtk_tree_model_get (GTK_TREE_MODEL (fixture->store), &iter, + 1, &visible, + -1); + + if (root_path && + (!gtk_tree_path_is_descendant (store_path, root_path) + || !gtk_tree_path_compare (store_path, root_path))) + { + if (!gtk_tree_path_compare (store_path, root_path)) + { + if (depth > 1 + && gtk_tree_model_iter_has_child (GTK_TREE_MODEL (fixture->store), + &iter)) + { + GtkTreePath *store_copy; + GtkTreePath *filter_copy; + + store_copy = gtk_tree_path_copy (store_path); + filter_copy = gtk_tree_path_copy (filter_path); + filter_test_append_refilter_signals_recurse (fixture, + store_copy, + filter_copy, + depth - 1, + root_path); + gtk_tree_path_free (store_copy); + gtk_tree_path_free (filter_copy); + } + } + + gtk_tree_path_next (store_path); + gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter); + + if (visible) + gtk_tree_path_next (filter_path); + + continue; + } + + real_path = strip_virtual_root (filter_path, root_path); + + if (visible) + { + /* This row will be inserted */ + signal_monitor_append_signal_path (fixture->monitor, ROW_CHANGED, + real_path); + signal_monitor_append_signal_path (fixture->monitor, + ROW_HAS_CHILD_TOGGLED, + real_path); + + if (depth > 1 + && gtk_tree_model_iter_has_child (GTK_TREE_MODEL (fixture->store), + &iter)) + { + GtkTreePath *store_copy; + GtkTreePath *filter_copy; + + store_copy = gtk_tree_path_copy (store_path); + filter_copy = gtk_tree_path_copy (filter_path); + filter_test_append_refilter_signals_recurse (fixture, + store_copy, + filter_copy, + depth - 1, + root_path); + gtk_tree_path_free (store_copy); + gtk_tree_path_free (filter_copy); + } + + gtk_tree_path_next (filter_path); + } + else + { + /* This row will be deleted */ + rows_deleted++; + signal_monitor_append_signal_path (fixture->monitor, ROW_DELETED, + real_path); + } + + gtk_tree_path_free (real_path); + + gtk_tree_path_next (store_path); + gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter); + } + + if (rows_deleted == LEVEL_LENGTH + && gtk_tree_path_get_depth (filter_path) > 1) + { + GtkTreePath *real_path; + + gtk_tree_path_up (store_path); + gtk_tree_path_up (filter_path); + + /* A row-has-child-toggled will be emitted on the parent */ + if (!root_path + || (root_path + && gtk_tree_path_is_descendant (store_path, root_path) + && gtk_tree_path_compare (store_path, root_path))) + { + real_path = strip_virtual_root (filter_path, root_path); + signal_monitor_append_signal_path (fixture->monitor, + ROW_HAS_CHILD_TOGGLED, + real_path); + + gtk_tree_path_free (real_path); + } + } +} + +static void +filter_test_append_refilter_signals (FilterTest *fixture, + int depth) +{ + /* A special function that walks the tree store like the + * model validation functions below. + */ + GtkTreePath *path; + GtkTreePath *filter_path; + + path = gtk_tree_path_new (); + filter_path = gtk_tree_path_new (); + filter_test_append_refilter_signals_recurse (fixture, + path, + filter_path, + depth, + NULL); + gtk_tree_path_free (path); + gtk_tree_path_free (filter_path); +} + +static void +filter_test_append_refilter_signals_with_vroot (FilterTest *fixture, + int depth, + GtkTreePath *root_path) +{ + /* A special function that walks the tree store like the + * model validation functions below. + */ + GtkTreePath *path; + GtkTreePath *filter_path; + + path = gtk_tree_path_new (); + filter_path = gtk_tree_path_new (); + filter_test_append_refilter_signals_recurse (fixture, + path, + filter_path, + depth, + root_path); + gtk_tree_path_free (path); + gtk_tree_path_free (filter_path); +} + +static void +filter_test_enable_filter (FilterTest *fixture) +{ + gtk_tree_model_filter_set_visible_column (fixture->filter, 1); + gtk_tree_model_filter_refilter (fixture->filter); +} + +static void +filter_test_block_signals (FilterTest *fixture) +{ + fixture->block_signals = TRUE; +} + +static void +filter_test_unblock_signals (FilterTest *fixture) +{ + fixture->block_signals = FALSE; +} + +static void +filter_test_teardown (FilterTest *fixture, + gconstpointer test_data) +{ + signal_monitor_free (fixture->monitor); + + g_object_unref (fixture->filter); + g_object_unref (fixture->store); +} + +/* + * Model structure validation + */ + +static void +check_filter_model_recurse (FilterTest *fixture, + GtkTreePath *store_parent_path, + GtkTreePath *filter_parent_path) +{ + int i; + GtkTreeIter store_iter; + GtkTreeIter filter_iter; + gboolean store_has_next, filter_has_next; + + gtk_tree_path_down (store_parent_path); + gtk_tree_path_down (filter_parent_path); + + store_has_next = gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store), + &store_iter, store_parent_path); + filter_has_next = gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->filter), + &filter_iter, filter_parent_path); + + for (i = 0; i < LEVEL_LENGTH; i++) + { + gboolean visible; + + g_return_if_fail (store_has_next == TRUE); + + gtk_tree_model_get (GTK_TREE_MODEL (fixture->store), + &store_iter, + 1, &visible, + -1); + + if (visible) + { + GtkTreePath *tmp; + gchar *filter_str, *store_str; + + g_return_if_fail (filter_has_next == TRUE); + + /* Verify path */ + tmp = gtk_tree_model_get_path (GTK_TREE_MODEL (fixture->filter), + &filter_iter); + g_return_if_fail (gtk_tree_path_compare (tmp, filter_parent_path) == 0); + + /* Verify model content */ + gtk_tree_model_get (GTK_TREE_MODEL (fixture->store), + &store_iter, + 0, &store_str, + -1); + gtk_tree_model_get (GTK_TREE_MODEL (fixture->filter), + &filter_iter, + 0, &filter_str, + -1); + + g_return_if_fail (g_strcmp0 (store_str, filter_str) == 0); + + g_free (store_str); + g_free (filter_str); + + if (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (fixture->filter), + &filter_iter)) + { + g_return_if_fail (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (fixture->store), &store_iter)); + + check_filter_model_recurse (fixture, + gtk_tree_path_copy (store_parent_path), + tmp); + } + + gtk_tree_path_next (filter_parent_path); + filter_has_next = gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->filter), &filter_iter); + } + + gtk_tree_path_next (store_parent_path); + store_has_next = gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &store_iter); + } + + /* Both models should have no more content! */ + g_return_if_fail (store_has_next == FALSE); + g_return_if_fail (filter_has_next == FALSE); + + gtk_tree_path_free (store_parent_path); + gtk_tree_path_free (filter_parent_path); +} + +static void +check_filter_model (FilterTest *fixture) +{ + GtkTreePath *path; + + if (fixture->monitor) + signal_monitor_assert_is_empty (fixture->monitor); + + path = gtk_tree_path_new (); + + check_filter_model_recurse (fixture, path, gtk_tree_path_copy (path)); +} + +static void +check_filter_model_with_root (FilterTest *fixture, + GtkTreePath *path) +{ + if (fixture->monitor) + signal_monitor_assert_is_empty (fixture->monitor); + + check_filter_model_recurse (fixture, + gtk_tree_path_copy (path), + gtk_tree_path_new ()); +} + +/* Helpers */ + +static void +check_level_length (GtkTreeModelFilter *filter, + const gchar *level, + const int length) +{ + if (!level) + { + int l = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (filter), NULL); + g_return_if_fail (l == length); + } + else + { + int l; + gboolean retrieved_iter = FALSE; + GtkTreeIter iter; + + retrieved_iter = gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (filter), + &iter, level); + g_return_if_fail (retrieved_iter); + l = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (filter), &iter); + g_return_if_fail (l == length); + } +} + +static void +set_path_visibility (FilterTest *fixture, + const gchar *path, + gboolean visible) +{ + GtkTreeIter store_iter; + + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture->store), + &store_iter, path); + gtk_tree_store_set (fixture->store, &store_iter, + 1, visible, + -1); +} + +#if 0 +static void +insert_path_with_visibility (FilterTest *fixture, + const gchar *path_string, + gboolean visible) +{ + int position; + GtkTreePath *path; + GtkTreeIter parent, iter; + + path = gtk_tree_path_new_from_string (path_string); + position = gtk_tree_path_get_indices (path)[gtk_tree_path_get_depth (path)]; + gtk_tree_path_up (path); + + if (gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store), &parent, path)) + { + gtk_tree_store_insert (fixture->store, &iter, &parent, position); + create_tree_store_set_values (fixture->store, &iter, visible); + } + gtk_tree_path_free (path); +} +#endif + +/* + * The actual tests. + */ + +static void +verify_test_suite (FilterTest *fixture, + gconstpointer user_data) +{ + check_filter_model (fixture); +} + +static void +verify_test_suite_vroot (FilterTest *fixture, + gconstpointer user_data) +{ + check_filter_model_with_root (fixture, (GtkTreePath *)user_data); +} + + +static void +filled_hide_root_level (FilterTest *fixture, + gconstpointer user_data) +{ + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "2"); + set_path_visibility (fixture, "2", FALSE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1); + + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0"); + set_path_visibility (fixture, "0", FALSE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 2); + + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "2"); + set_path_visibility (fixture, "4", FALSE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 3); + + + /* Hide remaining */ + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0"); + + set_path_visibility (fixture, "1", FALSE); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 4); + + set_path_visibility (fixture, "3", FALSE); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 5); + + check_filter_model (fixture); + + /* Show some */ + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "1"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "1"); + + set_path_visibility (fixture, "1", TRUE); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 4); + + set_path_visibility (fixture, "3", TRUE); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 3); + + check_filter_model (fixture); +} + +static void +filled_hide_child_levels (FilterTest *fixture, + gconstpointer user_data) +{ + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:2"); + set_path_visibility (fixture, "0:2", FALSE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH - 1); + + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:3"); + set_path_visibility (fixture, "0:4", FALSE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2); + + set_path_visibility (fixture, "0:4:3", FALSE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2); + + set_path_visibility (fixture, "0:4:0", FALSE); + set_path_visibility (fixture, "0:4:1", FALSE); + set_path_visibility (fixture, "0:4:2", FALSE); + set_path_visibility (fixture, "0:4:4", FALSE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2); + + /* Since "0:2" is hidden, "0:4" must be "0:3" in the filter model */ + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:3"); + /* FIXME: Actually, the filter model should not be emitted the + * row-has-child-toggled signal here. *However* an extraneous emission + * of this signal does not hurt and is allowed. + */ + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:3"); + set_path_visibility (fixture, "0:4", TRUE); + check_filter_model (fixture); + check_level_length (fixture->filter, "0:3", 0); + + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:2"); + set_path_visibility (fixture, "0:2", TRUE); + check_filter_model (fixture); + check_level_length (fixture->filter, "0:2", LEVEL_LENGTH); + check_level_length (fixture->filter, "0:3", LEVEL_LENGTH); + check_level_length (fixture->filter, "0:4", 0); + + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:4:0"); + /* Once 0:4:0 got inserted, 0:4 became a parent */ + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4:0"); + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:4:1"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4:1"); + + set_path_visibility (fixture, "0:4:2", TRUE); + set_path_visibility (fixture, "0:4:4", TRUE); + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, "0:4", 2); +} + + +static void +filled_vroot_hide_root_level (FilterTest *fixture, + gconstpointer user_data) +{ + GtkTreePath *path = (GtkTreePath *)user_data; + + /* These changes do not affect the filter's root level */ + set_path_visibility (fixture, "0", FALSE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH); + + set_path_visibility (fixture, "4", FALSE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH); + + /* Even though we set the virtual root parent node to FALSE, + * the virtual root contents remain. + */ + set_path_visibility (fixture, "2", FALSE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH); + + /* No change */ + set_path_visibility (fixture, "1", FALSE); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH); + + set_path_visibility (fixture, "3", FALSE); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH); + + check_filter_model_with_root (fixture, path); + + /* Show some */ + set_path_visibility (fixture, "2", TRUE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH); + + set_path_visibility (fixture, "1", TRUE); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH); + + set_path_visibility (fixture, "3", TRUE); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH); + + check_filter_model_with_root (fixture, path); + + /* Now test changes in the virtual root level */ + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "2"); + set_path_visibility (fixture, "2:2", FALSE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1); + + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "3"); + set_path_visibility (fixture, "2:4", FALSE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 2); + + set_path_visibility (fixture, "1:4", FALSE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 2); + + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "3"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "3"); + set_path_visibility (fixture, "2:4", TRUE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1); + + set_path_visibility (fixture, "2", FALSE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1); + + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0"); + set_path_visibility (fixture, "2:0", FALSE); + set_path_visibility (fixture, "2:1", FALSE); + set_path_visibility (fixture, "2:2", FALSE); + set_path_visibility (fixture, "2:3", FALSE); + set_path_visibility (fixture, "2:4", FALSE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 0); + + set_path_visibility (fixture, "2", TRUE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 0); + + set_path_visibility (fixture, "1:4", FALSE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 0); + + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); + set_path_visibility (fixture, "2:4", TRUE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 4); + + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0"); + set_path_visibility (fixture, "2:4", FALSE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 0); + + set_path_visibility (fixture, "2", FALSE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 0); + + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "1"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "1"); + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2"); + set_path_visibility (fixture, "2:0", TRUE); + set_path_visibility (fixture, "2:1", TRUE); + set_path_visibility (fixture, "2:2", TRUE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 2); + + set_path_visibility (fixture, "2", TRUE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 2); +} + +static void +filled_vroot_hide_child_levels (FilterTest *fixture, + gconstpointer user_data) +{ + GtkTreePath *path = (GtkTreePath *)user_data; + + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:2"); + set_path_visibility (fixture, "2:0:2", FALSE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH - 1); + + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0:3"); + set_path_visibility (fixture, "2:0:4", FALSE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2); + + set_path_visibility (fixture, "2:0:4:3", FALSE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2); + + set_path_visibility (fixture, "2:0:4:0", FALSE); + set_path_visibility (fixture, "2:0:4:1", FALSE); + set_path_visibility (fixture, "2:0:4:2", FALSE); + set_path_visibility (fixture, "2:0:4:4", FALSE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "0", LEVEL_LENGTH - 2); + + /* Since "0:2" is hidden, "0:4" must be "0:3" in the filter model */ + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:3"); + /* FIXME: Actually, the filter model should not be emitted the + * row-has-child-toggled signal here. *However* an extraneous emission + * of this signal does not hurt and is allowed. + */ + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:3"); + set_path_visibility (fixture, "2:0:4", TRUE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, "0:3", 0); + + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:2"); + set_path_visibility (fixture, "2:0:2", TRUE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, "0:2", LEVEL_LENGTH); + check_level_length (fixture->filter, "0:3", LEVEL_LENGTH); + check_level_length (fixture->filter, "0:4", 0); + + /* FIXME: Inconsistency! For the non-vroot case we also receive two + * row-has-child-toggled signals here. + */ + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:4:0"); + /* Once 0:4:0 got inserted, 0:4 became a parent */ + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:4"); + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:4:1"); + set_path_visibility (fixture, "2:0:4:2", TRUE); + set_path_visibility (fixture, "2:0:4:4", TRUE); + check_level_length (fixture->filter, "0:4", 2); +} + + +static void +empty_show_nodes (FilterTest *fixture, + gconstpointer user_data) +{ + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, 0); + + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); + set_path_visibility (fixture, "3", TRUE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, 1); + check_level_length (fixture->filter, "0", 0); + + set_path_visibility (fixture, "3:2:2", TRUE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, 1); + check_level_length (fixture->filter, "0", 0); + + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:0"); + set_path_visibility (fixture, "3:2", TRUE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, 1); + check_level_length (fixture->filter, "0", 1); + check_level_length (fixture->filter, "0:0", 1); + check_level_length (fixture->filter, "0:0:0", 0); + + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0"); + set_path_visibility (fixture, "3", FALSE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, 0); + + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); + set_path_visibility (fixture, "3:2:1", TRUE); + set_path_visibility (fixture, "3", TRUE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, 1); + check_level_length (fixture->filter, "0", 1); + check_level_length (fixture->filter, "0:0", 2); + check_level_length (fixture->filter, "0:0:0", 0); +} + +static void +empty_show_multiple_nodes (FilterTest *fixture, + gconstpointer user_data) +{ + GtkTreeIter iter; + GtkTreePath *changed_path; + + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, 0); + + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "1"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "1"); + signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "1"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "1"); + + /* We simulate a change in visible func condition with this. The + * visibility state of multiple nodes changes at once, we emit row-changed + * for these nodes (and others) after that. + */ + filter_test_block_signals (fixture); + set_path_visibility (fixture, "3", TRUE); + set_path_visibility (fixture, "4", TRUE); + filter_test_unblock_signals (fixture); + + changed_path = gtk_tree_path_new (); + gtk_tree_path_append_index (changed_path, 2); + gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store), + &iter, changed_path); + gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store), + changed_path, &iter); + + gtk_tree_path_next (changed_path); + gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter); + gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store), + changed_path, &iter); + + gtk_tree_path_next (changed_path); + gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter); + gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store), + changed_path, &iter); + + gtk_tree_path_free (changed_path); + + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, 2); + check_level_length (fixture->filter, "0", 0); + + set_path_visibility (fixture, "3:2:2", TRUE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, 2); + check_level_length (fixture->filter, "0", 0); + + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0:0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0:0"); + set_path_visibility (fixture, "3:2", TRUE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, 2); + check_level_length (fixture->filter, "0", 1); + check_level_length (fixture->filter, "0:0", 1); + check_level_length (fixture->filter, "0:0:0", 0); + + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0"); + set_path_visibility (fixture, "3", FALSE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, 1); + + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); + set_path_visibility (fixture, "3:2:1", TRUE); + set_path_visibility (fixture, "3", TRUE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, 2); + check_level_length (fixture->filter, "0", 1); + check_level_length (fixture->filter, "0:0", 2); + check_level_length (fixture->filter, "0:0:0", 0); +} + +static void +empty_vroot_show_nodes (FilterTest *fixture, + gconstpointer user_data) +{ + GtkTreePath *path = (GtkTreePath *)user_data; + + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 0); + + set_path_visibility (fixture, "2", TRUE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 0); + + set_path_visibility (fixture, "2:2:2", TRUE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 0); + + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); + set_path_visibility (fixture, "2:2", TRUE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 1); + check_level_length (fixture->filter, "0", 1); + check_level_length (fixture->filter, "0:0", 0); + + set_path_visibility (fixture, "3", TRUE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 1); + + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0"); + set_path_visibility (fixture, "2:2", FALSE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 0); + + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); + set_path_visibility (fixture, "2:2:1", TRUE); + set_path_visibility (fixture, "2:2", TRUE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 1); + check_level_length (fixture->filter, "0", 2); + check_level_length (fixture->filter, "0:1", 0); +} + +static void +empty_vroot_show_multiple_nodes (FilterTest *fixture, + gconstpointer user_data) +{ + GtkTreeIter iter; + GtkTreePath *changed_path; + GtkTreePath *path = (GtkTreePath *)user_data; + + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 0); + + /* We simulate a change in visible func condition with this. The + * visibility state of multiple nodes changes at once, we emit row-changed + * for these nodes (and others) after that. + */ + filter_test_block_signals (fixture); + set_path_visibility (fixture, "2", TRUE); + set_path_visibility (fixture, "3", TRUE); + filter_test_unblock_signals (fixture); + + changed_path = gtk_tree_path_new (); + gtk_tree_path_append_index (changed_path, 1); + gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store), + &iter, changed_path); + gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store), + changed_path, &iter); + + gtk_tree_path_next (changed_path); + gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter); + gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store), + changed_path, &iter); + + gtk_tree_path_next (changed_path); + gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter); + gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store), + changed_path, &iter); + + gtk_tree_path_next (changed_path); + gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter); + gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store), + changed_path, &iter); + + gtk_tree_path_free (changed_path); + + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 0); + + set_path_visibility (fixture, "2:2:2", TRUE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 0); + + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "1"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "1"); + + /* Again, we simulate a call to refilter */ + filter_test_block_signals (fixture); + set_path_visibility (fixture, "2:2", TRUE); + set_path_visibility (fixture, "2:3", TRUE); + filter_test_unblock_signals (fixture); + + changed_path = gtk_tree_path_new (); + gtk_tree_path_append_index (changed_path, 2); + gtk_tree_path_append_index (changed_path, 1); + gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store), + &iter, changed_path); + gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store), + changed_path, &iter); + + gtk_tree_path_next (changed_path); + gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter); + gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store), + changed_path, &iter); + + gtk_tree_path_next (changed_path); + gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter); + gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store), + changed_path, &iter); + + gtk_tree_path_next (changed_path); + gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), &iter); + gtk_tree_model_row_changed (GTK_TREE_MODEL (fixture->store), + changed_path, &iter); + + gtk_tree_path_free (changed_path); + + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 2); + check_level_length (fixture->filter, "0", 1); + check_level_length (fixture->filter, "0:0", 0); + + set_path_visibility (fixture, "3", TRUE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 2); + + signal_monitor_append_signal (fixture->monitor, ROW_DELETED, "0"); + set_path_visibility (fixture, "2:2", FALSE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 1); + + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); + set_path_visibility (fixture, "2:2:1", TRUE); + set_path_visibility (fixture, "2:2", TRUE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 2); + check_level_length (fixture->filter, "0", 2); + check_level_length (fixture->filter, "0:1", 0); +} + + +static void +unfiltered_hide_single (FilterTest *fixture, + gconstpointer user_data) + +{ + signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2"); + set_path_visibility (fixture, "2", FALSE); + + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + + /* The view only shows the root level, so the filter model only has + * the first two levels cached. + */ + filter_test_append_refilter_signals (fixture, 2); + filter_test_enable_filter (fixture); + + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1); +} + +static void +unfiltered_hide_single_child (FilterTest *fixture, + gconstpointer user_data) + +{ + signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2"); + set_path_visibility (fixture, "2:2", FALSE); + + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH); + + /* The view only shows the root level, so the filter model only has + * the first two levels cached. + */ + filter_test_append_refilter_signals (fixture, 2); + filter_test_enable_filter (fixture); + + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1); +} + +static void +unfiltered_hide_single_multi_level (FilterTest *fixture, + gconstpointer user_data) + +{ + /* This row is not shown, so its signal is not propagated */ + set_path_visibility (fixture, "2:2:2", FALSE); + + signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2"); + set_path_visibility (fixture, "2:2", FALSE); + + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH); + check_level_length (fixture->filter, "2:2", LEVEL_LENGTH); + + /* The view only shows the root level, so the filter model only has + * the first two levels cached. + */ + filter_test_append_refilter_signals (fixture, 2); + filter_test_enable_filter (fixture); + + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1); + + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "2:2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2"); + set_path_visibility (fixture, "2:2", TRUE); + + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH); + check_level_length (fixture->filter, "2:2", LEVEL_LENGTH - 1); +} + + +static void +unfiltered_vroot_hide_single (FilterTest *fixture, + gconstpointer user_data) + +{ + GtkTreePath *path = (GtkTreePath *)user_data; + + signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2"); + set_path_visibility (fixture, "2:2", FALSE); + + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + + /* The view only shows the root level, so the filter model only has + * the first two levels cached. (We add an additional level to + * take the virtual root into account). + */ + filter_test_append_refilter_signals_with_vroot (fixture, 3, path); + filter_test_enable_filter (fixture); + + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH - 1); +} + +static void +unfiltered_vroot_hide_single_child (FilterTest *fixture, + gconstpointer user_data) + +{ + GtkTreePath *path = (GtkTreePath *)user_data; + + signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2"); + set_path_visibility (fixture, "2:2:2", FALSE); + + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH); + + /* The view only shows the root level, so the filter model only has + * the first two levels cached. (We add an additional level to take + * the virtual root into account). + */ + filter_test_append_refilter_signals_with_vroot (fixture, 3, path); + filter_test_enable_filter (fixture); + + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1); +} + +static void +unfiltered_vroot_hide_single_multi_level (FilterTest *fixture, + gconstpointer user_data) + +{ + GtkTreePath *path = (GtkTreePath *)user_data; + + /* This row is not shown, so its signal is not propagated */ + set_path_visibility (fixture, "2:2:2:2", FALSE); + + signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2"); + set_path_visibility (fixture, "2:2:2", FALSE); + + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH); + check_level_length (fixture->filter, "2:2", LEVEL_LENGTH); + + /* The view only shows the root level, so the filter model only has + * the first two levels cached. + */ + filter_test_append_refilter_signals_with_vroot (fixture, 3, path); + filter_test_enable_filter (fixture); + + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH - 1); + + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "2:2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2"); + set_path_visibility (fixture, "2:2:2", TRUE); + + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH); + check_level_length (fixture->filter, "2:2", LEVEL_LENGTH - 1); +} + + + +static void +unfiltered_show_single (FilterTest *fixture, + gconstpointer user_data) + +{ + signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2"); + set_path_visibility (fixture, "2", TRUE); + + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + + /* The view only shows the root level, so the filter model only has + * the first two levels cached. + */ + filter_test_append_refilter_signals (fixture, 2); + filter_test_enable_filter (fixture); + + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, 1); +} + +static void +unfiltered_show_single_child (FilterTest *fixture, + gconstpointer user_data) + +{ + signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2"); + set_path_visibility (fixture, "2:2", TRUE); + + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH); + + /* The view only shows the root level, so the filter model only has + * the first two levels cached. + */ + filter_test_append_refilter_signals (fixture, 3); + filter_test_enable_filter (fixture); + + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, 0); + + /* From here we are filtered, "2" in the real model is "0" in the filter + * model. + */ + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); + set_path_visibility (fixture, "2", TRUE); + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, 1); + check_level_length (fixture->filter, "0", 1); +} + +static void +unfiltered_show_single_multi_level (FilterTest *fixture, + gconstpointer user_data) + +{ + /* The view is not showing this row (collapsed state), so it is not + * referenced. The signal should not go through. + */ + set_path_visibility (fixture, "2:2:2", TRUE); + + signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2"); + set_path_visibility (fixture, "2:2", TRUE); + + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH); + check_level_length (fixture->filter, "2:2", LEVEL_LENGTH); + + /* The view only shows the root level, so the filter model only has + * the first two levels cached. + */ + filter_test_append_refilter_signals (fixture, 3); + filter_test_enable_filter (fixture); + + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, 0); + + /* From here we are filtered, "2" in the real model is "0" in the filter + * model. + */ + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); + set_path_visibility (fixture, "2", TRUE); + check_filter_model (fixture); + check_level_length (fixture->filter, NULL, 1); + check_level_length (fixture->filter, "0", 1); + check_level_length (fixture->filter, "0:0", 1); +} + + +static void +unfiltered_vroot_show_single (FilterTest *fixture, + gconstpointer user_data) + +{ + GtkTreePath *path = (GtkTreePath *)user_data; + + signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2"); + set_path_visibility (fixture, "2:2", TRUE); + + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + + /* The view only shows the root level, so the filter model only has + * the first two levels cached. + */ + filter_test_append_refilter_signals_with_vroot (fixture, 3, path); + filter_test_enable_filter (fixture); + + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 1); +} + +static void +unfiltered_vroot_show_single_child (FilterTest *fixture, + gconstpointer user_data) + +{ + GtkTreePath *path = (GtkTreePath *)user_data; + + signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2"); + set_path_visibility (fixture, "2:2:2", TRUE); + + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH); + + /* The view only shows the root level, so the filter model only has + * the first two levels cached. + */ + filter_test_append_refilter_signals_with_vroot (fixture, 2, path); + filter_test_enable_filter (fixture); + + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 0); + + /* From here we are filtered, "2" in the real model is "0" in the filter + * model. + */ + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); + set_path_visibility (fixture, "2:2", TRUE); + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, 1); + check_level_length (fixture->filter, "0", 1); +} + +static void +unfiltered_vroot_show_single_multi_level (FilterTest *fixture, + gconstpointer user_data) + +{ + GtkTreePath *path = (GtkTreePath *)user_data; + + /* The view is not showing this row (collapsed state), so it is not + * referenced. The signal should not go through. + */ + set_path_visibility (fixture, "2:2:2:2", TRUE); + + signal_monitor_append_signal (fixture->monitor, ROW_CHANGED, "2:2"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "2:2"); + set_path_visibility (fixture, "2:2:2", TRUE); + + signal_monitor_assert_is_empty (fixture->monitor); + check_level_length (fixture->filter, NULL, LEVEL_LENGTH); + check_level_length (fixture->filter, "2", LEVEL_LENGTH); + check_level_length (fixture->filter, "2:2", LEVEL_LENGTH); + + /* The view only shows the root level, so the filter model only has + * the first two levels cached. + */ + filter_test_append_refilter_signals_with_vroot (fixture, 4, path); + filter_test_enable_filter (fixture); + + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 0); + + /* From here we are filtered, "2" in the real model is "0" in the filter + * model. + */ + signal_monitor_append_signal (fixture->monitor, ROW_INSERTED, "0"); + signal_monitor_append_signal (fixture->monitor, ROW_HAS_CHILD_TOGGLED, "0"); + set_path_visibility (fixture, "2:2", TRUE); + check_filter_model_with_root (fixture, path); + check_level_length (fixture->filter, NULL, 1); + check_level_length (fixture->filter, "0", 1); + check_level_length (fixture->filter, "0:0", 1); +} + + +static gboolean +specific_path_dependent_filter_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + GtkTreePath *path; + + path = gtk_tree_model_get_path (model, iter); + if (gtk_tree_path_get_indices (path)[0] < 4) + return FALSE; + + return TRUE; +} + +static void +specific_path_dependent_filter (void) +{ + int i; + GtkTreeIter iter; + GtkListStore *list; + GtkTreeModel *sort; + GtkTreeModel *filter; + + list = gtk_list_store_new (1, G_TYPE_INT); + gtk_list_store_insert_with_values (list, &iter, 0, 0, 1, -1); + gtk_list_store_insert_with_values (list, &iter, 1, 0, 2, -1); + gtk_list_store_insert_with_values (list, &iter, 2, 0, 3, -1); + gtk_list_store_insert_with_values (list, &iter, 3, 0, 4, -1); + gtk_list_store_insert_with_values (list, &iter, 4, 0, 5, -1); + gtk_list_store_insert_with_values (list, &iter, 5, 0, 6, -1); + gtk_list_store_insert_with_values (list, &iter, 6, 0, 7, -1); + gtk_list_store_insert_with_values (list, &iter, 7, 0, 8, -1); + + sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (list)); + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (sort), NULL); + gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter), + specific_path_dependent_filter_func, + NULL, NULL); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort), 0, + GTK_SORT_DESCENDING); + + for (i = 0; i < 4; i++) + { + if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (list), &iter, + NULL, 1)) + gtk_list_store_remove (list, &iter); + + if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (list), &iter, + NULL, 2)) + gtk_list_store_remove (list, &iter); + } +} + + +static gboolean +specific_append_after_collapse_visible_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + gint number; + gboolean hide_negative_numbers; + + gtk_tree_model_get (model, iter, 1, &number, -1); + hide_negative_numbers = GPOINTER_TO_INT (g_object_get_data (data, "private-hide-negative-numbers")); + + return (number >= 0 || !hide_negative_numbers); +} + +static void +specific_append_after_collapse (void) +{ + /* This test is based on one of the test cases I found in my + * old test cases directory. I unfortunately do not have a record + * from who this test case originated. -Kris. + * + * General idea: + * - Construct tree. + * - Show tree, expand, collapse. + * - Add a row. + */ + + GtkTreeIter iter; + GtkTreeIter child_iter; + GtkTreeIter child_iter2; + GtkTreePath *append_path; + GtkTreeStore *store; + GtkTreeModel *filter; + GtkTreeModel *sort; + + GtkWidget *window; + GtkWidget *tree_view; + + store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_INT); + + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL); + g_object_set_data (G_OBJECT (filter), "private-hide-negative-numbers", + GINT_TO_POINTER (FALSE)); + gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter), + specific_append_after_collapse_visible_func, + filter, NULL); + + sort = gtk_tree_model_sort_new_with_model (filter); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + tree_view = gtk_tree_view_new_with_model (sort); + gtk_container_add (GTK_CONTAINER (window), tree_view); + gtk_widget_realize (tree_view); + + while (gtk_events_pending ()) + gtk_main_iteration (); + + gtk_tree_store_prepend (store, &iter, NULL); + gtk_tree_store_set (store, &iter, + 0, "hallo", 1, 1, -1); + + gtk_tree_store_append (store, &child_iter, &iter); + gtk_tree_store_set (store, &child_iter, + 0, "toemaar", 1, 1, -1); + + gtk_tree_store_append (store, &child_iter2, &child_iter); + gtk_tree_store_set (store, &child_iter2, + 0, "very deep", 1, 1, -1); + + append_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &child_iter2); + + gtk_tree_store_append (store, &child_iter, &iter); + gtk_tree_store_set (store, &child_iter, + 0, "sja", 1, 1, -1); + + gtk_tree_store_append (store, &child_iter, &iter); + gtk_tree_store_set (store, &child_iter, + 0, "some word", 1, -1, -1); + + /* Expand and collapse the tree */ + gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); + while (gtk_events_pending ()) + gtk_main_iteration (); + + gtk_tree_view_collapse_all (GTK_TREE_VIEW (tree_view)); + while (gtk_events_pending ()) + gtk_main_iteration (); + + /* Add another it */ + g_object_set_data (G_OBJECT (filter), "private-hide-negative-numbers", + GINT_TO_POINTER (TRUE)); + + if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, append_path)) + { + gtk_tree_store_append (store, &child_iter, &iter); + gtk_tree_store_set (store, &child_iter, + 0, "new new new !!", 1, 1, -1); + } + gtk_tree_path_free (append_path); + + /* Expand */ + gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); + while (gtk_events_pending ()) + gtk_main_iteration (); +} + + +static gint +specific_sort_filter_remove_node_compare_func (GtkTreeModel *model, + GtkTreeIter *iter1, + GtkTreeIter *iter2, + gpointer data) +{ + return -1; +} + +static gboolean +specific_sort_filter_remove_node_visible_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + char *item = NULL; + + /* Do reference the model */ + gtk_tree_model_get (model, iter, 0, &item, -1); + g_free (item); + + return FALSE; +} + +static void +specific_sort_filter_remove_node (void) +{ + /* This test is based on one of the test cases I found in my + * old test cases directory. I unfortunately do not have a record + * from who this test case originated. -Kris. + * + * General idea: + * - Create tree store, sort, filter models. The sort model has + * a default sort func that is enabled, filter model a visible func + * that defaults to returning FALSE. + * - Remove a node from the tree store. + */ + + GtkTreeIter iter; + GtkTreeStore *store; + GtkTreeModel *filter; + GtkTreeModel *sort; + + GtkWidget *window; + GtkWidget *tree_view; + + store = gtk_tree_store_new (1, G_TYPE_STRING); + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, &iter, 0, "Hello1", -1); + + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, &iter, 0, "Hello2", -1); + + sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store)); + gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (sort), + specific_sort_filter_remove_node_compare_func, NULL, NULL); + + filter = gtk_tree_model_filter_new (sort, NULL); + gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter), + specific_sort_filter_remove_node_visible_func, + filter, NULL); + + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + tree_view = gtk_tree_view_new_with_model (filter); + gtk_container_add (GTK_CONTAINER (window), tree_view); + gtk_widget_realize (tree_view); + + while (gtk_events_pending ()) + gtk_main_iteration (); + + /* Remove a node */ + gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter); + gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter); + gtk_tree_store_remove (store, &iter); + + while (gtk_events_pending ()) + gtk_main_iteration (); +} + + +static void +specific_sort_filter_remove_root (void) +{ + /* This test is based on one of the test cases I found in my + * old test cases directory. I unfortunately do not have a record + * from who this test case originated. -Kris. + */ + + GtkTreeModel *model, *sort, *filter; + GtkTreeIter root, mid, leaf; + GtkTreePath *path; + + model = GTK_TREE_MODEL (gtk_tree_store_new (1, G_TYPE_INT)); + gtk_tree_store_append (GTK_TREE_STORE (model), &root, NULL); + gtk_tree_store_append (GTK_TREE_STORE (model), &mid, &root); + gtk_tree_store_append (GTK_TREE_STORE (model), &leaf, &mid); + + path = gtk_tree_model_get_path (model, &mid); + + sort = gtk_tree_model_sort_new_with_model (model); + filter = gtk_tree_model_filter_new (sort, path); + + gtk_tree_store_remove (GTK_TREE_STORE (model), &root); + + g_object_unref (filter); + g_object_unref (sort); + g_object_unref (model); +} + + +static void +specific_root_mixed_visibility (void) +{ + int i; + GtkTreeModel *filter; + /* A bit nasty, apologies */ + FilterTest fixture; + + fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN); + + for (i = 0; i < LEVEL_LENGTH; i++) + { + GtkTreeIter iter; + + gtk_tree_store_insert (fixture.store, &iter, NULL, i); + if (i % 2 == 0) + create_tree_store_set_values (fixture.store, &iter, TRUE); + else + create_tree_store_set_values (fixture.store, &iter, FALSE); + } + + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL); + fixture.filter = GTK_TREE_MODEL_FILTER (filter); + fixture.monitor = NULL; + + gtk_tree_model_filter_set_visible_column (fixture.filter, 1); + + /* In order to trigger the potential bug, we should not access + * the filter model here (so don't call the check functions). + */ + + /* Change visibility of an odd row to TRUE */ + set_path_visibility (&fixture, "3", TRUE); + check_filter_model (&fixture); + check_level_length (fixture.filter, NULL, 4); +} + + + +static gboolean +specific_has_child_filter_filter_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + return gtk_tree_model_iter_has_child (model, iter); +} + +static void +specific_has_child_filter (void) +{ + GtkTreeModel *filter; + GtkTreeIter iter, root; + /* A bit nasty, apologies */ + FilterTest fixture; + + fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN); + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL); + fixture.filter = GTK_TREE_MODEL_FILTER (filter); + fixture.monitor = NULL; + + /* We will filter on parent state using a filter function. We will + * manually keep the boolean column in sync, so that we can use + * check_filter_model() to check the consistency of the model. + */ + /* FIXME: We need a check_filter_model() that is not tied to LEVEL_LENGTH + * to be able to check the structure here. We keep the calls to + * check_filter_model() commented out until then. + */ + gtk_tree_model_filter_set_visible_func (fixture.filter, + specific_has_child_filter_filter_func, + NULL, NULL); + + gtk_tree_store_append (fixture.store, &root, NULL); + create_tree_store_set_values (fixture.store, &root, FALSE); + + /* check_filter_model (&fixture); */ + check_level_length (fixture.filter, NULL, 0); + + gtk_tree_store_append (fixture.store, &iter, &root); + create_tree_store_set_values (fixture.store, &iter, TRUE); + + /* Parent must now be visible. Do the level length check first, + * to avoid modifying the child model triggering a row-changed to + * the filter model. + */ + check_level_length (fixture.filter, NULL, 1); + check_level_length (fixture.filter, "0", 0); + + set_path_visibility (&fixture, "0", TRUE); + /* check_filter_model (&fixture); */ + + gtk_tree_store_append (fixture.store, &root, NULL); + check_level_length (fixture.filter, NULL, 1); + + gtk_tree_store_append (fixture.store, &iter, &root); + check_level_length (fixture.filter, NULL, 2); + check_level_length (fixture.filter, "1", 0); + + create_tree_store_set_values (fixture.store, &root, TRUE); + create_tree_store_set_values (fixture.store, &iter, TRUE); + + /* check_filter_model (&fixture); */ + + gtk_tree_store_append (fixture.store, &iter, &root); + create_tree_store_set_values (fixture.store, &iter, TRUE); + check_level_length (fixture.filter, NULL, 2); + check_level_length (fixture.filter, "0", 0); + check_level_length (fixture.filter, "1", 0); + + /* Now remove one of the remaining child rows */ + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture.store), + &iter, "0:0"); + gtk_tree_store_remove (fixture.store, &iter); + + check_level_length (fixture.filter, NULL, 1); + check_level_length (fixture.filter, "0", 0); + + set_path_visibility (&fixture, "0", FALSE); + /* check_filter_model (&fixture); */ +} + + +static gboolean +specific_root_has_child_filter_filter_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + int depth; + GtkTreePath *path; + + path = gtk_tree_model_get_path (model, iter); + depth = gtk_tree_path_get_depth (path); + gtk_tree_path_free (path); + + if (depth > 1) + return TRUE; + /* else */ + return gtk_tree_model_iter_has_child (model, iter); +} + +static void +specific_root_has_child_filter (void) +{ + GtkTreeModel *filter; + GtkTreeIter iter, root; + /* A bit nasty, apologies */ + FilterTest fixture; + + /* This is a variation on the above test case wherein the has-child + * check for visibility only applies to root level nodes. + */ + + fixture.store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN); + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (fixture.store), NULL); + fixture.filter = GTK_TREE_MODEL_FILTER (filter); + fixture.monitor = NULL; + + /* We will filter on parent state using a filter function. We will + * manually keep the boolean column in sync, so that we can use + * check_filter_model() to check the consistency of the model. + */ + /* FIXME: We need a check_filter_model() that is not tied to LEVEL_LENGTH + * to be able to check the structure here. We keep the calls to + * check_filter_model() commented out until then. + */ + gtk_tree_model_filter_set_visible_func (fixture.filter, + specific_root_has_child_filter_filter_func, + NULL, NULL); + + gtk_tree_store_append (fixture.store, &root, NULL); + create_tree_store_set_values (fixture.store, &root, FALSE); + + /* check_filter_model (&fixture); */ + check_level_length (fixture.filter, NULL, 0); + + gtk_tree_store_append (fixture.store, &iter, &root); + create_tree_store_set_values (fixture.store, &iter, TRUE); + + /* Parent must now be visible. Do the level length check first, + * to avoid modifying the child model triggering a row-changed to + * the filter model. + */ + check_level_length (fixture.filter, NULL, 1); + check_level_length (fixture.filter, "0", 1); + + set_path_visibility (&fixture, "0", TRUE); + /* check_filter_model (&fixture); */ + + gtk_tree_store_append (fixture.store, &root, NULL); + check_level_length (fixture.filter, NULL, 1); + + gtk_tree_store_append (fixture.store, &iter, &root); + check_level_length (fixture.filter, NULL, 2); + check_level_length (fixture.filter, "1", 1); + + create_tree_store_set_values (fixture.store, &root, TRUE); + create_tree_store_set_values (fixture.store, &iter, TRUE); + + /* check_filter_model (&fixture); */ + + gtk_tree_store_append (fixture.store, &iter, &root); + create_tree_store_set_values (fixture.store, &iter, TRUE); + check_level_length (fixture.filter, NULL, 2); + check_level_length (fixture.filter, "0", 1); + check_level_length (fixture.filter, "1", 2); + + /* Now remove one of the remaining child rows */ + gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (fixture.store), + &iter, "0:0"); + gtk_tree_store_remove (fixture.store, &iter); + + check_level_length (fixture.filter, NULL, 1); + check_level_length (fixture.filter, "0", 2); + + set_path_visibility (&fixture, "0", FALSE); + /* check_filter_model (&fixture); */ +} + + +static void +specific_filter_add_child (void) +{ + /* This test is based on one of the test cases I found in my + * old test cases directory. I unfortunately do not have a record + * from who this test case originated. -Kris. + */ + + GtkTreeIter iter; + GtkTreeIter iter_first; + GtkTreeIter child; + GtkTreeStore *store; + GtkTreeModel *filter; + + store = gtk_tree_store_new (1, G_TYPE_STRING); + + gtk_tree_store_append (store, &iter_first, NULL); + gtk_tree_store_set (store, &iter_first, 0, "Hello", -1); + + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, &iter, 0, "Hello", -1); + + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, &iter, 0, "Hello", -1); + + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, &iter, 0, "Hello", -1); + + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL); + + gtk_tree_store_set (store, &iter, 0, "Hello", -1); + gtk_tree_store_append (store, &child, &iter_first); + gtk_tree_store_set (store, &child, 0, "Hello", -1); +} + +static void +specific_list_store_clear (void) +{ + GtkTreeIter iter; + GtkListStore *list; + GtkTreeModel *filter; + GtkWidget *view; + + list = gtk_list_store_new (1, G_TYPE_INT); + gtk_list_store_insert_with_values (list, &iter, 0, 0, 1, -1); + gtk_list_store_insert_with_values (list, &iter, 1, 0, 2, -1); + gtk_list_store_insert_with_values (list, &iter, 2, 0, 3, -1); + gtk_list_store_insert_with_values (list, &iter, 3, 0, 4, -1); + gtk_list_store_insert_with_values (list, &iter, 4, 0, 5, -1); + gtk_list_store_insert_with_values (list, &iter, 5, 0, 6, -1); + gtk_list_store_insert_with_values (list, &iter, 6, 0, 7, -1); + gtk_list_store_insert_with_values (list, &iter, 7, 0, 8, -1); + + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (list), NULL); + view = gtk_tree_view_new_with_model (filter); + + gtk_list_store_clear (list); +} + +static void +specific_bug_300089 (void) +{ + /* Test case for GNOME Bugzilla bug 300089. Written by + * Matthias Clasen. + */ + GtkTreeModel *sort_model, *child_model; + GtkTreePath *path; + GtkTreeIter iter, iter2, sort_iter; + + child_model = GTK_TREE_MODEL (gtk_tree_store_new (1, G_TYPE_STRING)); + + gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter, NULL); + gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "A", -1); + gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter, NULL); + gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "B", -1); + + gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter2, &iter); + gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter2, 0, "D", -1); + gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter2, &iter); + gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter2, 0, "E", -1); + + gtk_tree_store_append (GTK_TREE_STORE (child_model), &iter, NULL); + gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "C", -1); + + + sort_model = GTK_TREE_MODEL (gtk_tree_model_sort_new_with_model (child_model)); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), + 0, GTK_SORT_ASCENDING); + + path = gtk_tree_path_new_from_indices (1, 1, -1); + + /* make sure a level is constructed */ + gtk_tree_model_get_iter (sort_model, &sort_iter, path); + + /* change the "E" row in a way that causes it to change position */ + gtk_tree_model_get_iter (child_model, &iter, path); + gtk_tree_store_set (GTK_TREE_STORE (child_model), &iter, 0, "A", -1); +} + + +static int +specific_bug_301558_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data) +{ + int i, j; + + gtk_tree_model_get (model, a, 0, &i, -1); + gtk_tree_model_get (model, b, 0, &j, -1); + + return j - i; +} + +static void +specific_bug_301558 (void) +{ + /* Test case for GNOME Bugzilla bug 301558 provided by + * Markku Vire. + */ + GtkTreeStore *tree; + GtkTreeModel *filter; + GtkTreeModel *sort; + GtkTreeIter root, iter, iter2; + GtkWidget *view; + int i; + gboolean add; + + tree = gtk_tree_store_new (2, G_TYPE_INT, G_TYPE_BOOLEAN); + gtk_tree_store_append (tree, &iter, NULL); + gtk_tree_store_set (tree, &iter, 0, 123, 1, TRUE, -1); + gtk_tree_store_append (tree, &iter2, &iter); + gtk_tree_store_set (tree, &iter2, 0, 73, 1, TRUE, -1); + + sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (tree)); + gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (sort), + specific_bug_301558_sort_func, + NULL, NULL); + + filter = gtk_tree_model_filter_new (sort, NULL); + gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (filter), 1); + + view = gtk_tree_view_new_with_model (filter); + + while (gtk_events_pending ()) + gtk_main_iteration (); + + add = TRUE; + + for (i = 0; i < 10; i++) + { + if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (tree), &root)) + g_assert_not_reached (); + + if (add) + { + gtk_tree_store_append (tree, &iter, &root); + gtk_tree_store_set (tree, &iter, 0, 456, 1, TRUE, -1); + } + else + { + int n; + n = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (tree), &root); + gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (tree), &iter, + &root, n - 1); + gtk_tree_store_remove (tree, &iter); + } + + add = !add; + } +} + + +static gboolean +specific_bug_311955_filter_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + int value; + + gtk_tree_model_get (model, iter, 0, &value, -1); + + return (value != 0); +} + +static void +specific_bug_311955 (void) +{ + /* This is a test case for GNOME Bugzilla bug 311955. It was written + * by Markku Vire. + */ + GtkTreeIter iter, child, root; + GtkTreeStore *store; + GtkTreeModel *sort; + GtkTreeModel *filter; + + GtkWidget *window; + GtkWidget *tree_view; + int i; + int n; + + store = gtk_tree_store_new (1, G_TYPE_INT); + + gtk_tree_store_append (store, &root, NULL); + gtk_tree_store_set (store, &root, 0, 33, -1); + + gtk_tree_store_append (store, &iter, &root); + gtk_tree_store_set (store, &iter, 0, 50, -1); + + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, &iter, 0, 22, -1); + + sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store)); + filter = gtk_tree_model_filter_new (sort, NULL); + + gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter), + specific_bug_311955_filter_func, + NULL, NULL); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + tree_view = gtk_tree_view_new_with_model (filter); + g_object_unref (store); + + gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); + + while (gtk_events_pending ()) + gtk_main_iteration (); + + /* Fill model */ + for (i = 0; i < 4; i++) + { + gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &root); + + gtk_tree_store_append (store, &iter, &root); + + if (i < 3) + gtk_tree_store_set (store, &iter, 0, i, -1); + + if (i % 2 == 0) + { + gtk_tree_store_append (store, &child, &iter); + gtk_tree_store_set (store, &child, 0, 10, -1); + } + } + + while (gtk_events_pending ()) + gtk_main_iteration (); + + /* Remove bottommost child from the tree. */ + gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &root); + n = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), &root); + + if (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store), &iter, &root, n - 2)) + { + if (gtk_tree_model_iter_children (GTK_TREE_MODEL (store), &child, &iter)) + gtk_tree_store_remove (store, &child); + } + else + g_assert_not_reached (); +} + +static void +specific_bug_346800 (void) +{ + /* This is a test case for GNOME Bugzilla bug 346800. It was written + * by Jonathan Matthew. + */ + + GtkTreeIter node_iters[50]; + GtkTreeIter child_iters[50]; + GtkTreeModel *model; + GtkTreeModelFilter *filter; + GtkTreeStore *store; + GType *columns; + int i; + int items = 50; + columns = g_new (GType, 2); + columns[0] = G_TYPE_STRING; + columns[1] = G_TYPE_BOOLEAN; + store = gtk_tree_store_newv (2, columns); + model = GTK_TREE_MODEL (store); + + filter = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (model, NULL)); + gtk_tree_model_filter_set_visible_column (filter, 1); + + for (i=0; i<items; i++) + { + /* allocate random amounts of junk, otherwise the filter model's arrays can expand without moving */ + + g_malloc (138); + gtk_tree_store_append (store, &node_iters[i], NULL); + gtk_tree_store_set (store, &node_iters[i], + 0, "something", + 1, ((i%6) == 0) ? FALSE : TRUE, + -1); + + g_malloc (47); + gtk_tree_store_append (store, &child_iters[i], &node_iters[i]); + gtk_tree_store_set (store, &child_iters[i], + 0, "something else", + 1, FALSE, + -1); + gtk_tree_model_filter_refilter (filter); + + if (i > 6) + { + gtk_tree_store_set (GTK_TREE_STORE (model), &child_iters[i-1], 1, + (i & 1) ? TRUE : FALSE, -1); + gtk_tree_model_filter_refilter (filter); + + gtk_tree_store_set (GTK_TREE_STORE (model), &child_iters[i-2], 1, + (i & 1) ? FALSE: TRUE, -1); + gtk_tree_model_filter_refilter (filter); + } + } +} + + +static void +specific_bug_364946 (void) +{ + /* This is a test case for GNOME Bugzilla bug 364946. It was written + * by Andreas Koehler. + */ + GtkTreeStore *store; + GtkTreeIter a, aa, aaa, aab, iter; + GtkTreeModel *s_model; + + store = gtk_tree_store_new (1, G_TYPE_STRING); + + gtk_tree_store_append (store, &a, NULL); + gtk_tree_store_set (store, &a, 0, "0", -1); + + gtk_tree_store_append (store, &aa, &a); + gtk_tree_store_set (store, &aa, 0, "0:0", -1); + + gtk_tree_store_append (store, &aaa, &aa); + gtk_tree_store_set (store, &aaa, 0, "0:0:0", -1); + + gtk_tree_store_append (store, &aab, &aa); + gtk_tree_store_set (store, &aab, 0, "0:0:1", -1); + + s_model = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (store)); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (s_model), 0, + GTK_SORT_ASCENDING); + + gtk_tree_model_get_iter_from_string (s_model, &iter, "0:0:0"); + + gtk_tree_store_set (store, &aaa, 0, "0:0:0", -1); + gtk_tree_store_remove (store, &aaa); + gtk_tree_store_remove (store, &aab); + + gtk_tree_model_sort_clear_cache (GTK_TREE_MODEL_SORT (s_model)); +} + + +static gboolean +specific_bug_464173_visible_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + gboolean *visible = (gboolean *)data; + + return *visible; +} + +static void +specific_bug_464173 (void) +{ + /* Test case for GNOME Bugzilla bug 464173, test case written + * by Andreas Koehler. + */ + GtkTreeStore *model; + GtkTreeModelFilter *f_model; + GtkTreeIter iter1, iter2; + GtkWidget *view; + gboolean visible = TRUE; + + model = gtk_tree_store_new (1, G_TYPE_STRING); + gtk_tree_store_append (model, &iter1, NULL); + gtk_tree_store_set (model, &iter1, 0, "Foo", -1); + gtk_tree_store_append (model, &iter2, &iter1); + gtk_tree_store_set (model, &iter2, 0, "Bar", -1); + + f_model = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (GTK_TREE_MODEL(model), NULL)); + gtk_tree_model_filter_set_visible_func (f_model, + specific_bug_464173_visible_func, + &visible, NULL); + + view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (f_model)); + + visible = FALSE; + gtk_tree_model_filter_refilter (f_model); +} + + +static gboolean +specific_bug_540201_filter_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + gboolean has_children; + + has_children = gtk_tree_model_iter_has_child (model, iter); + + return has_children; +} + +static void +specific_bug_540201 (void) +{ + /* Test case for GNOME Bugzilla bug 540201, steps provided by + * Charles Day. + */ + GtkTreeIter iter, root; + GtkTreeStore *store; + GtkTreeModel *filter; + + GtkWidget *tree_view; + + store = gtk_tree_store_new (1, G_TYPE_INT); + + gtk_tree_store_append (store, &root, NULL); + gtk_tree_store_set (store, &root, 0, 33, -1); + + filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL); + tree_view = gtk_tree_view_new_with_model (filter); + + gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filter), + specific_bug_540201_filter_func, + NULL, NULL); + + gtk_tree_store_append (store, &iter, &root); + gtk_tree_store_set (store, &iter, 0, 50, -1); + + gtk_tree_store_append (store, &iter, &root); + gtk_tree_store_set (store, &iter, 0, 22, -1); + + + gtk_tree_store_append (store, &root, NULL); + gtk_tree_store_set (store, &root, 0, 33, -1); + + gtk_tree_store_append (store, &iter, &root); + gtk_tree_store_set (store, &iter, 0, 22, -1); +} + + +static gboolean +specific_bug_549287_visible_func (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + gboolean result = FALSE; + + result = gtk_tree_model_iter_has_child (model, iter); + + return result; +} + +static void +specific_bug_549287 (void) +{ + /* Test case for GNOME Bugzilla bug 529287, provided by Julient Puydt */ + + int i; + GtkTreeStore *store; + GtkTreeModel *filtered; + GtkWidget *view; + GtkTreeIter iter; + GtkTreeIter *swap, *parent, *child; + + store = gtk_tree_store_new (1, G_TYPE_STRING); + filtered = gtk_tree_model_filter_new (GTK_TREE_MODEL (store), NULL); + gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (filtered), + specific_bug_549287_visible_func, + NULL, NULL); + + view = gtk_tree_view_new_with_model (filtered); + + for (i = 0; i < 4; i++) + { + if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) + { + parent = gtk_tree_iter_copy (&iter); + child = gtk_tree_iter_copy (&iter); + + while (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (store), + child, parent, 0)) + { + + swap = parent; + parent = child; + child = swap; + } + + gtk_tree_store_append (store, child, parent); + gtk_tree_store_set (store, child, + 0, "Something", + -1); + + gtk_tree_iter_free (parent); + gtk_tree_iter_free (child); + } + else + { + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, &iter, + 0, "Something", + -1); + } + + /* since we inserted something, we changed the visibility conditions: */ + gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (filtered)); + } +} + +/* main */ + +int +main (int argc, + char **argv) +{ + gtk_test_init (&argc, &argv, NULL); + + g_test_add ("/FilterModel/self/verify-test-suite", + FilterTest, NULL, + filter_test_setup, + verify_test_suite, + filter_test_teardown); + + g_test_add ("/FilterModel/self/verify-test-suite/vroot/depth-1", + FilterTest, gtk_tree_path_new_from_indices (2, -1), + filter_test_setup, + verify_test_suite_vroot, + filter_test_teardown); + g_test_add ("/FilterModel/self/verify-test-suite/vroot/depth-2", + FilterTest, gtk_tree_path_new_from_indices (2, 3, -1), + filter_test_setup, + verify_test_suite_vroot, + filter_test_teardown); + + + g_test_add ("/FilterModel/filled/hide-root-level", + FilterTest, NULL, + filter_test_setup, + filled_hide_root_level, + filter_test_teardown); + g_test_add ("/FilterModel/filled/hide-child-levels", + FilterTest, NULL, + filter_test_setup, + filled_hide_child_levels, + filter_test_teardown); + + g_test_add ("/FilterModel/filled/hide-root-level/vroot", + FilterTest, gtk_tree_path_new_from_indices (2, -1), + filter_test_setup, + filled_vroot_hide_root_level, + filter_test_teardown); + g_test_add ("/FilterModel/filled/hide-child-levels/vroot", + FilterTest, gtk_tree_path_new_from_indices (2, -1), + filter_test_setup, + filled_vroot_hide_child_levels, + filter_test_teardown); + + + g_test_add ("/FilterModel/empty/show-nodes", + FilterTest, NULL, + filter_test_setup_empty, + empty_show_nodes, + filter_test_teardown); + g_test_add ("/FilterModel/empty/show-multiple-nodes", + FilterTest, NULL, + filter_test_setup_empty, + empty_show_multiple_nodes, + filter_test_teardown); + + g_test_add ("/FilterModel/empty/show-nodes/vroot", + FilterTest, gtk_tree_path_new_from_indices (2, -1), + filter_test_setup_empty, + empty_vroot_show_nodes, + filter_test_teardown); + g_test_add ("/FilterModel/empty/show-multiple-nodes/vroot", + FilterTest, gtk_tree_path_new_from_indices (2, -1), + filter_test_setup_empty, + empty_vroot_show_multiple_nodes, + filter_test_teardown); + + + g_test_add ("/FilterModel/unfiltered/hide-single", + FilterTest, NULL, + filter_test_setup_unfiltered, + unfiltered_hide_single, + filter_test_teardown); + g_test_add ("/FilterModel/unfiltered/hide-single-child", + FilterTest, NULL, + filter_test_setup_unfiltered, + unfiltered_hide_single_child, + filter_test_teardown); + g_test_add ("/FilterModel/unfiltered/hide-single-multi-level", + FilterTest, NULL, + filter_test_setup_unfiltered, + unfiltered_hide_single_multi_level, + filter_test_teardown); + + g_test_add ("/FilterModel/unfiltered/hide-single/vroot", + FilterTest, gtk_tree_path_new_from_indices (2, -1), + filter_test_setup_unfiltered, + unfiltered_vroot_hide_single, + filter_test_teardown); + g_test_add ("/FilterModel/unfiltered/hide-single-child/vroot", + FilterTest, gtk_tree_path_new_from_indices (2, -1), + filter_test_setup_unfiltered, + unfiltered_vroot_hide_single_child, + filter_test_teardown); + g_test_add ("/FilterModel/unfiltered/hide-single-multi-level/vroot", + FilterTest, gtk_tree_path_new_from_indices (2, -1), + filter_test_setup_unfiltered, + unfiltered_vroot_hide_single_multi_level, + filter_test_teardown); + + + + g_test_add ("/FilterModel/unfiltered/show-single", + FilterTest, NULL, + filter_test_setup_empty_unfiltered, + unfiltered_show_single, + filter_test_teardown); + g_test_add ("/FilterModel/unfiltered/show-single-child", + FilterTest, NULL, + filter_test_setup_empty_unfiltered, + unfiltered_show_single_child, + filter_test_teardown); + g_test_add ("/FilterModel/unfiltered/show-single-multi-level", + FilterTest, NULL, + filter_test_setup_empty_unfiltered, + unfiltered_show_single_multi_level, + filter_test_teardown); + + g_test_add ("/FilterModel/unfiltered/show-single/vroot", + FilterTest, gtk_tree_path_new_from_indices (2, -1), + filter_test_setup_empty_unfiltered, + unfiltered_vroot_show_single, + filter_test_teardown); + g_test_add ("/FilterModel/unfiltered/show-single-child/vroot", + FilterTest, gtk_tree_path_new_from_indices (2, -1), + filter_test_setup_empty_unfiltered, + unfiltered_vroot_show_single_child, + filter_test_teardown); + g_test_add ("/FilterModel/unfiltered/show-single-multi-level/vroot", + FilterTest, gtk_tree_path_new_from_indices (2, -1), + filter_test_setup_empty_unfiltered, + unfiltered_vroot_show_single_multi_level, + filter_test_teardown); + + + g_test_add_func ("/FilterModel/specific/path-dependent-filter", + specific_path_dependent_filter); + g_test_add_func ("/FilterModel/specific/append-after-collapse", + specific_append_after_collapse); + g_test_add_func ("/FilterModel/specific/sort-filter-remove-node", + specific_sort_filter_remove_node); + g_test_add_func ("/FilterModel/specific/sort-filter-remove-root", + specific_sort_filter_remove_root); + g_test_add_func ("/FilterModel/specific/root-mixed-visibility", + specific_root_mixed_visibility); + g_test_add_func ("/FilterModel/specific/has-child-filter", + specific_has_child_filter); + g_test_add_func ("/FilterModel/specific/root-has-child-filter", + specific_root_has_child_filter); + g_test_add_func ("/FilterModel/specific/filter-add-child", + specific_filter_add_child); + g_test_add_func ("/FilterModel/specific/list-store-clear", + specific_list_store_clear); + + g_test_add_func ("/FilterModel/specific/bug-300089", + specific_bug_300089); + g_test_add_func ("/FilterModel/specific/bug-301558", + specific_bug_301558); + g_test_add_func ("/FilterModel/specific/bug-311955", + specific_bug_311955); + g_test_add_func ("/FilterModel/specific/bug-346800", + specific_bug_346800); + g_test_add_func ("/FilterModel/specific/bug-364946", + specific_bug_364946); + g_test_add_func ("/FilterModel/specific/bug-464173", + specific_bug_464173); + g_test_add_func ("/FilterModel/specific/bug-540201", + specific_bug_540201); + g_test_add_func ("/FilterModel/specific/bug-549287", + specific_bug_549287); + + return g_test_run (); +} diff --git a/gtk/tests/liststore.c b/gtk/tests/liststore.c index 588f725f3d..6452fc2903 100644 --- a/gtk/tests/liststore.c +++ b/gtk/tests/liststore.c @@ -850,6 +850,72 @@ list_store_test_move_before_single (void) g_object_unref (store); } + +/* iter invalidation */ + +static void +list_store_test_iter_next_invalid (ListStore *fixture, + gconstpointer user_data) +{ + GtkTreePath *path; + GtkTreeIter iter; + + path = gtk_tree_path_new_from_indices (4, -1); + gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store), &iter, path); + gtk_tree_path_free (path); + + g_assert (gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), + &iter) == FALSE); + g_assert (gtk_list_store_iter_is_valid (fixture->store, &iter) == FALSE); + g_assert (iter.stamp == 0); +} + +static void +list_store_test_iter_children_invalid (ListStore *fixture, + gconstpointer user_data) +{ + GtkTreeIter iter, child; + + gtk_tree_model_get_iter_first (GTK_TREE_MODEL (fixture->store), &iter); + g_assert (gtk_list_store_iter_is_valid (fixture->store, &iter) == TRUE); + + g_assert (gtk_tree_model_iter_children (GTK_TREE_MODEL (fixture->store), + &child, &iter) == FALSE); + g_assert (gtk_list_store_iter_is_valid (fixture->store, &child) == FALSE); + g_assert (child.stamp == 0); +} + +static void +list_store_test_iter_nth_child_invalid (ListStore *fixture, + gconstpointer user_data) +{ + GtkTreeIter iter, child; + + gtk_tree_model_get_iter_first (GTK_TREE_MODEL (fixture->store), &iter); + g_assert (gtk_list_store_iter_is_valid (fixture->store, &iter) == TRUE); + + g_assert (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (fixture->store), + &child, &iter, 0) == FALSE); + g_assert (gtk_list_store_iter_is_valid (fixture->store, &child) == FALSE); + g_assert (child.stamp == 0); +} + +static void +list_store_test_iter_parent_invalid (ListStore *fixture, + gconstpointer user_data) +{ + GtkTreeIter iter, child; + + gtk_tree_model_get_iter_first (GTK_TREE_MODEL (fixture->store), &child); + g_assert (gtk_list_store_iter_is_valid (fixture->store, &child) == TRUE); + + g_assert (gtk_tree_model_iter_parent (GTK_TREE_MODEL (fixture->store), + &iter, &child) == FALSE); + g_assert (gtk_list_store_iter_is_valid (fixture->store, &iter) == FALSE); + g_assert (iter.stamp == 0); +} + + /* main */ int @@ -958,5 +1024,19 @@ main (int argc, g_test_add_func ("/list-store/move-before-single", list_store_test_move_before_single); + /* iter invalidation */ + g_test_add ("/list-store/iter-next-invalid", ListStore, NULL, + list_store_setup, list_store_test_iter_next_invalid, + list_store_teardown); + g_test_add ("/list-store/iter-children-invalid", ListStore, NULL, + list_store_setup, list_store_test_iter_children_invalid, + list_store_teardown); + g_test_add ("/list-store/iter-nth-child-invalid", ListStore, NULL, + list_store_setup, list_store_test_iter_nth_child_invalid, + list_store_teardown); + g_test_add ("/list-store/iter-parent-invalid", ListStore, NULL, + list_store_setup, list_store_test_iter_parent_invalid, + list_store_teardown); + return g_test_run (); } diff --git a/gtk/tests/textbuffer.c b/gtk/tests/textbuffer.c index 822b04c752..010776d478 100644 --- a/gtk/tests/textbuffer.c +++ b/gtk/tests/textbuffer.c @@ -879,6 +879,8 @@ split_r_n_separators_test (void) gtk_text_buffer_get_iter_at_offset (buffer, &iter, 3); g_assert (gtk_text_iter_ends_line (&iter)); + + g_object_unref (buffer); } static void @@ -913,6 +915,40 @@ test_line_separator (void) } static void +test_backspace (void) +{ + GtkTextBuffer *buffer; + GtkTextIter iter; + gboolean ret; + + buffer = gtk_text_buffer_new (NULL); + + gtk_text_buffer_set_text (buffer, "foo", -1); + gtk_text_buffer_get_iter_at_offset (buffer, &iter, 2); + ret = gtk_text_buffer_backspace (buffer, &iter, TRUE, TRUE); + g_assert (ret); + g_assert_cmpint (1, ==, gtk_text_iter_get_offset (&iter)); + g_assert_cmpint (2, ==, gtk_text_buffer_get_char_count (buffer)); + + gtk_text_buffer_set_text (buffer, "foo", -1); + gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0); + ret = gtk_text_buffer_backspace (buffer, &iter, TRUE, TRUE); + g_assert (!ret); + g_assert_cmpint (0, ==, gtk_text_iter_get_offset (&iter)); + g_assert_cmpint (3, ==, gtk_text_buffer_get_char_count (buffer)); + + /* test bug #544724 */ + gtk_text_buffer_set_text (buffer, "foo\r\n\r\nbar", -1); + gtk_text_buffer_get_iter_at_offset (buffer, &iter, 5); + ret = gtk_text_buffer_backspace (buffer, &iter, TRUE, TRUE); + g_assert (ret); + g_assert_cmpint (0, ==, gtk_text_iter_get_line (&iter)); + g_assert_cmpint (8, ==, gtk_text_buffer_get_char_count (buffer)); + + g_object_unref (buffer); +} + +static void test_logical_motion (void) { char *str; @@ -1297,6 +1333,7 @@ main (int argc, char** argv) g_test_add_func ("/TextBuffer/UTF8 unknown char", test_utf8); g_test_add_func ("/TextBuffer/Line separator", test_line_separator); + g_test_add_func ("/TextBuffer/Backspace", test_backspace); g_test_add_func ("/TextBuffer/Logical motion", test_logical_motion); g_test_add_func ("/TextBuffer/Marks", test_marks); g_test_add_func ("/TextBuffer/Empty buffer", test_empty_buffer); diff --git a/gtk/tests/treestore.c b/gtk/tests/treestore.c index e3a85c92fe..c9dbcffba8 100644 --- a/gtk/tests/treestore.c +++ b/gtk/tests/treestore.c @@ -853,6 +853,72 @@ tree_store_test_move_before_single (void) g_object_unref (store); } + +/* iter invalidation */ + +static void +tree_store_test_iter_next_invalid (TreeStore *fixture, + gconstpointer user_data) +{ + GtkTreePath *path; + GtkTreeIter iter; + + path = gtk_tree_path_new_from_indices (4, -1); + gtk_tree_model_get_iter (GTK_TREE_MODEL (fixture->store), &iter, path); + gtk_tree_path_free (path); + + g_assert (gtk_tree_model_iter_next (GTK_TREE_MODEL (fixture->store), + &iter) == FALSE); + g_assert (gtk_tree_store_iter_is_valid (fixture->store, &iter) == FALSE); + g_assert (iter.stamp == 0); +} + +static void +tree_store_test_iter_children_invalid (TreeStore *fixture, + gconstpointer user_data) +{ + GtkTreeIter iter, child; + + gtk_tree_model_get_iter_first (GTK_TREE_MODEL (fixture->store), &iter); + g_assert (gtk_tree_store_iter_is_valid (fixture->store, &iter) == TRUE); + + g_assert (gtk_tree_model_iter_children (GTK_TREE_MODEL (fixture->store), + &child, &iter) == FALSE); + g_assert (gtk_tree_store_iter_is_valid (fixture->store, &child) == FALSE); + g_assert (child.stamp == 0); +} + +static void +tree_store_test_iter_nth_child_invalid (TreeStore *fixture, + gconstpointer user_data) +{ + GtkTreeIter iter, child; + + gtk_tree_model_get_iter_first (GTK_TREE_MODEL (fixture->store), &iter); + g_assert (gtk_tree_store_iter_is_valid (fixture->store, &iter) == TRUE); + + g_assert (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (fixture->store), + &child, &iter, 0) == FALSE); + g_assert (gtk_tree_store_iter_is_valid (fixture->store, &child) == FALSE); + g_assert (child.stamp == 0); +} + +static void +tree_store_test_iter_parent_invalid (TreeStore *fixture, + gconstpointer user_data) +{ + GtkTreeIter iter, child; + + gtk_tree_model_get_iter_first (GTK_TREE_MODEL (fixture->store), &child); + g_assert (gtk_tree_store_iter_is_valid (fixture->store, &child) == TRUE); + + g_assert (gtk_tree_model_iter_parent (GTK_TREE_MODEL (fixture->store), + &iter, &child) == FALSE); + g_assert (gtk_tree_store_iter_is_valid (fixture->store, &iter) == FALSE); + g_assert (iter.stamp == 0); +} + + /* main */ int @@ -961,5 +1027,19 @@ main (int argc, g_test_add_func ("/tree-store/move-before-single", tree_store_test_move_before_single); + /* iter invalidation */ + g_test_add ("/tree-store/iter-next-invalid", TreeStore, NULL, + tree_store_setup, tree_store_test_iter_next_invalid, + tree_store_teardown); + g_test_add ("/tree-store/iter-children-invalid", TreeStore, NULL, + tree_store_setup, tree_store_test_iter_children_invalid, + tree_store_teardown); + g_test_add ("/tree-store/iter-nth-child-invalid", TreeStore, NULL, + tree_store_setup, tree_store_test_iter_nth_child_invalid, + tree_store_teardown); + g_test_add ("/tree-store/iter-parent-invalid", TreeStore, NULL, + tree_store_setup, tree_store_test_iter_parent_invalid, + tree_store_teardown); + return g_test_run (); } diff --git a/gtk/tests/treeview-scrolling.c b/gtk/tests/treeview-scrolling.c index 0207dbcff2..81dfd8d752 100644 --- a/gtk/tests/treeview-scrolling.c +++ b/gtk/tests/treeview-scrolling.c @@ -1,6 +1,7 @@ /* Scrolling test suite for GtkTreeView * Copyright (C) 2006 Kristian Rietveld <kris@gtk.org> * Copyright (C) 2007 Imendio AB, Kristian Rietveld + * Copyright (C) 2009 Kristian Rietveld <kris@gtk.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -37,6 +38,8 @@ * - Test that nothing happens if the row is fully visible. * - The tests are dependent on the theme/font (size measurements, * chosen paths). + * - Convert to proper GTK+ coding style. + * - Briefly test scrolling in tree stores as well. */ @@ -175,6 +178,34 @@ scroll_fixture_single_setup (ScrollFixture *fixture, scroll_fixture_setup (fixture, GTK_TREE_MODEL (store), test_data); } +/* sets up a fixture with a tree store */ +static void +scroll_fixture_tree_setup (ScrollFixture *fixture, + gconstpointer test_data) +{ + GtkTreeStore *store; + GtkTreeIter iter, child; + int i; + + store = gtk_tree_store_new (1, G_TYPE_STRING); + + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, &iter, 0, "Root node", -1); + + for (i = 0; i < 5; i++) { + gtk_tree_store_append (store, &child, &iter); + gtk_tree_store_set (store, &child, 0, "Child node", -1); + } + + for (i = 0; i < 5; i++) { + gtk_tree_store_append (store, &iter, NULL); + gtk_tree_store_set (store, &iter, 0, "Other node", -1); + } + + /* The teardown will also destroy the model */ + scroll_fixture_setup (fixture, GTK_TREE_MODEL (store), test_data); +} + static void scroll_fixture_teardown (ScrollFixture *fixture, gconstpointer test_data) @@ -752,6 +783,56 @@ scroll_new_row (ScrollFixture *fixture, gtk_tree_path_free (scroll_path); } +static void +scroll_new_row_tree (ScrollFixture *fixture, + gconstpointer test_data) +{ + GtkTreeModel *model; + GtkAdjustment *vadjustment; + int i; + + /* The goal of this test is to append new rows at the end of a tree + * store and immediately scroll to them. If there is a parent + * node with a couple of childs in the "area above" to explore, + * this used to lead to unexpected results due to a bug. + * + * This issue has been reported by Miroslav Rajcic on + * gtk-app-devel-list: + * http://mail.gnome.org/archives/gtk-app-devel-list/2008-December/msg00068.html + */ + + gtk_widget_show_all (fixture->window); + + gtk_tree_view_expand_all (GTK_TREE_VIEW (fixture->tree_view)); + + while (gtk_events_pending ()) + gtk_main_iteration (); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (fixture->tree_view)); + vadjustment = gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (fixture->tree_view)); + + for (i = 0; i < 5; i++) { + GtkTreeIter scroll_iter; + GtkTreePath *scroll_path; + + gtk_tree_store_append (GTK_TREE_STORE (model), &scroll_iter, + NULL); + gtk_tree_store_set (GTK_TREE_STORE (model), &scroll_iter, + 0, "New node", -1); + + scroll_path = gtk_tree_model_get_path (model, &scroll_iter); + gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (fixture->tree_view), + scroll_path, NULL, FALSE, 0.0, 0.0); + gtk_tree_path_free (scroll_path); + + while (gtk_events_pending ()) + gtk_main_iteration (); + + /* Test position, the scroll bar must be at the end */ + g_assert (vadjustment->value == vadjustment->upper - vadjustment->page_size); + } +} + /* Test for GNOME bugzilla bug 359231; tests "recovery when removing a bunch of * rows at the bottom. */ @@ -874,10 +955,10 @@ test_type_string (int test_type) { switch (test_type) { case BEFORE: - return "before"; + return "before-realize"; case AFTER: - return "after"; + return "after-realize"; case BOTH: return "both"; @@ -913,7 +994,7 @@ add_test (const char *path, align = align_string (use_align, row_align); - test_path = g_strdup_printf ("/treeview/scrolling/%s-%s-path-%s-%s", + test_path = g_strdup_printf ("/TreeView/scrolling/%s/%s-height/path-%s-%s", test_type_string (test_type), mixed ? "mixed" : "constant", path, align); @@ -1013,42 +1094,46 @@ main (int argc, char **argv) } /* Test different alignments in view with single row */ - g_test_add ("/treeview/scrolling/single-no-align", ScrollFixture, "0", + g_test_add ("/TreeView/scrolling/single-row/no-align", + ScrollFixture, "0", scroll_fixture_single_setup, scroll_no_align, scroll_fixture_teardown); - g_test_add ("/treeview/scrolling/single-align-0.0", ScrollFixture, "0", + g_test_add ("/TreeView/scrolling/single-row/align-0.0", + ScrollFixture, "0", scroll_fixture_single_setup, scroll_align_0_0, scroll_fixture_teardown); - g_test_add ("/treeview/scrolling/single-align-0.5", ScrollFixture, "0", + g_test_add ("/TreeView/scrolling/single-row/align-0.5", + ScrollFixture, "0", scroll_fixture_single_setup, scroll_align_0_5, scroll_fixture_teardown); - g_test_add ("/treeview/scrolling/single-align-1.0", ScrollFixture, "0", + g_test_add ("/TreeView/scrolling/single-row/align-1.0", + ScrollFixture, "0", scroll_fixture_single_setup, scroll_align_1_0, scroll_fixture_teardown); /* Test scrolling in a very large model; also very slow */ if (g_test_slow ()) { - g_test_add ("/treeview/scrolling/constant-big-middle-no-align", + g_test_add ("/TreeView/scrolling/large-model/constant-height/middle-no-align", ScrollFixture, "50000", scroll_fixture_constant_big_setup, scroll_no_align, scroll_fixture_teardown); - g_test_add ("/treeview/scrolling/constant-big-end-no-align", + g_test_add ("/TreeView/scrolling/large-model/constant-height/end-no-align", ScrollFixture, "99999", scroll_fixture_constant_big_setup, scroll_no_align, scroll_fixture_teardown); - g_test_add ("/treeview/scrolling/mixed-big-middle-no-align", + g_test_add ("/TreeView/scrolling/large-model/mixed-height/middle-no-align", ScrollFixture, "50000", scroll_fixture_mixed_big_setup, scroll_no_align, scroll_fixture_teardown); - g_test_add ("/treeview/scrolling/mixed-big-end-no-align", + g_test_add ("/TreeView/scrolling/large-model/mixed-height/end-no-align", ScrollFixture, "99999", scroll_fixture_mixed_big_setup, scroll_no_align, @@ -1056,12 +1141,12 @@ main (int argc, char **argv) } /* Test scrolling to a newly created row */ - g_test_add ("/treeview/scrolling/new-row-path-0", ScrollFixture, + g_test_add ("/TreeView/scrolling/new-row/path-0", ScrollFixture, GINT_TO_POINTER (0), scroll_fixture_constant_setup, scroll_new_row, scroll_fixture_teardown); - g_test_add ("/treeview/scrolling/new-row-path-4", ScrollFixture, + g_test_add ("/TreeView/scrolling/new-row/path-4", ScrollFixture, GINT_TO_POINTER (4), scroll_fixture_constant_setup, scroll_new_row, @@ -1070,27 +1155,35 @@ main (int argc, char **argv) * based on my font setting of "Vera Sans 11" and * the separators set to 0. (This should be made dynamic; FIXME). */ - g_test_add ("/treeview/scrolling/new-row-path-8", ScrollFixture, + g_test_add ("/TreeView/scrolling/new-row/path-8", ScrollFixture, GINT_TO_POINTER (8), scroll_fixture_constant_setup, scroll_new_row, scroll_fixture_teardown); - g_test_add ("/treeview/scrolling/new-row-path-500", ScrollFixture, + g_test_add ("/TreeView/scrolling/new-row/path-500", ScrollFixture, GINT_TO_POINTER (500), scroll_fixture_constant_setup, scroll_new_row, scroll_fixture_teardown); - g_test_add ("/treeview/scrolling/new-row-path-999", ScrollFixture, + g_test_add ("/TreeView/scrolling/new-row/path-999", ScrollFixture, GINT_TO_POINTER (999), scroll_fixture_constant_setup, scroll_new_row, scroll_fixture_teardown); + g_test_add ("/TreeView/scrolling/new-row/tree", ScrollFixture, + NULL, + scroll_fixture_tree_setup, + scroll_new_row_tree, + scroll_fixture_teardown); + /* Misc. tests */ - g_test_add ("/treeview/scrolling/bug-316689", ScrollFixture, NULL, + g_test_add ("/TreeView/scrolling/specific/bug-316689", + ScrollFixture, NULL, scroll_fixture_constant_setup, test_bug316689, scroll_fixture_teardown); - g_test_add_func ("/treeview/scrolling/bug-359231", test_bug359231); + g_test_add_func ("/TreeView/scrolling/specific/bug-359231", + test_bug359231); return g_test_run (); } diff --git a/gtk/tests/treeview.c b/gtk/tests/treeview.c new file mode 100644 index 0000000000..3dcc44287f --- /dev/null +++ b/gtk/tests/treeview.c @@ -0,0 +1,165 @@ +/* Basic GtkTreeView unit tests. + * Copyright (C) 2009 Kristian Rietveld <kris@gtk.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> + +static void +test_bug_546005 (void) +{ + GtkTreeIter iter; + GtkTreePath *path; + GtkTreePath *cursor_path; + GtkListStore *list_store; + GtkWidget *view; + + /* Tests provided by Bjorn Lindqvist, Paul Pogonyshev */ + view = gtk_tree_view_new (); + + /* Invalid path on tree view without model */ + path = gtk_tree_path_new_from_indices (1, -1); + gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), path, + NULL, FALSE); + gtk_tree_path_free (path); + + list_store = gtk_list_store_new (1, G_TYPE_STRING); + gtk_tree_view_set_model (GTK_TREE_VIEW (view), + GTK_TREE_MODEL (list_store)); + + /* Invalid path on tree view with empty model */ + path = gtk_tree_path_new_from_indices (1, -1); + gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), path, + NULL, FALSE); + gtk_tree_path_free (path); + + /* Valid path */ + gtk_list_store_insert_with_values (list_store, &iter, 0, + 0, "hi", + -1); + + path = gtk_tree_path_new_from_indices (0, -1); + gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), path, + NULL, FALSE); + + gtk_tree_view_get_cursor (GTK_TREE_VIEW (view), &cursor_path, NULL); + g_assert (gtk_tree_path_compare (cursor_path, path) == 0); + + gtk_tree_path_free (path); + gtk_tree_path_free (cursor_path); + + /* Invalid path on tree view with model */ + path = gtk_tree_path_new_from_indices (1, -1); + gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), path, + NULL, FALSE); + gtk_tree_path_free (path); +} + +static void +test_bug_539377 (void) +{ + GtkWidget *view; + GtkTreePath *path; + GtkListStore *list_store; + + /* Test provided by Bjorn Lindqvist */ + + /* Non-realized view, no model */ + view = gtk_tree_view_new (); + g_assert (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (view), 10, 10, &path, + NULL, NULL, NULL) == FALSE); + g_assert (gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (view), 10, 10, + &path, NULL) == FALSE); + + /* Non-realized view, with model */ + list_store = gtk_list_store_new (1, G_TYPE_STRING); + gtk_tree_view_set_model (GTK_TREE_VIEW (view), + GTK_TREE_MODEL (list_store)); + + g_assert (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (view), 10, 10, &path, + NULL, NULL, NULL) == FALSE); + g_assert (gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (view), 10, 10, + &path, NULL) == FALSE); +} + +static void +test_select_collapsed_row (void) +{ + GtkTreeIter child, parent; + GtkTreePath *path; + GtkTreeStore *tree_store; + GtkTreeSelection *selection; + GtkWidget *view; + + /* Reported by Michael Natterer */ + tree_store = gtk_tree_store_new (1, G_TYPE_STRING); + view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (tree_store)); + + gtk_tree_store_insert_with_values (tree_store, &parent, NULL, 0, + 0, "Parent", + -1); + + gtk_tree_store_insert_with_values (tree_store, &child, &parent, 0, + 0, "Child", + -1); + gtk_tree_store_insert_with_values (tree_store, &child, &parent, 0, + 0, "Child", + -1); + + + /* Try to select a child path. */ + path = gtk_tree_path_new_from_indices (0, 1, -1); + gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), path, NULL, FALSE); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); + + /* Check that the parent is not selected. */ + gtk_tree_path_up (path); + g_return_if_fail (gtk_tree_selection_path_is_selected (selection, path) == FALSE); + + /* Nothing should be selected at this point. */ + g_return_if_fail (gtk_tree_selection_count_selected_rows (selection) == 0); + + /* Check that selection really still works. */ + gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), path, NULL, FALSE); + g_return_if_fail (gtk_tree_selection_path_is_selected (selection, path) == TRUE); + g_return_if_fail (gtk_tree_selection_count_selected_rows (selection) == 1); + + /* Expand and select child node now. */ + gtk_tree_path_append_index (path, 1); + gtk_tree_view_expand_all (GTK_TREE_VIEW (view)); + + gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), path, NULL, FALSE); + g_return_if_fail (gtk_tree_selection_path_is_selected (selection, path) == TRUE); + g_return_if_fail (gtk_tree_selection_count_selected_rows (selection) == 1); + + gtk_tree_path_free (path); +} + +int +main (int argc, + char **argv) +{ + gtk_test_init (&argc, &argv, NULL); + + g_test_add_func ("/TreeView/cursor/bug-546005", test_bug_546005); + g_test_add_func ("/TreeView/cursor/bug-539377", test_bug_539377); + g_test_add_func ("/TreeView/cursor/select-collapsed_row", + test_select_collapsed_row); + + return g_test_run (); +} diff --git a/gtk/updateiconcache.c b/gtk/updateiconcache.c index 627f49eb80..3b927011cc 100644 --- a/gtk/updateiconcache.c +++ b/gtk/updateiconcache.c @@ -1516,9 +1516,11 @@ opentmp: g_unlink (bak_cache_path); if (g_rename (cache_path, bak_cache_path) == -1) { + int errsv = errno; + g_printerr (_("Could not rename %s to %s: %s, removing %s then.\n"), cache_path, bak_cache_path, - g_strerror (errno), + g_strerror (errsv), cache_path); g_unlink (cache_path); bak_cache_path = NULL; @@ -1528,16 +1530,22 @@ opentmp: if (g_rename (tmp_cache_path, cache_path) == -1) { + int errsv = errno; + g_printerr (_("Could not rename %s to %s: %s\n"), tmp_cache_path, cache_path, - g_strerror (errno)); + g_strerror (errsv)); g_unlink (tmp_cache_path); #ifdef G_OS_WIN32 if (bak_cache_path != NULL) if (g_rename (bak_cache_path, cache_path) == -1) - g_printerr (_("Could not rename %s back to %s: %s.\n"), - bak_cache_path, cache_path, - g_strerror (errno)); + { + errsv = errno; + + g_printerr (_("Could not rename %s back to %s: %s.\n"), + bak_cache_path, cache_path, + g_strerror (errsv)); + } #endif exit (1); } @@ -1648,8 +1656,12 @@ main (int argc, char **argv) setlocale (LC_ALL, ""); +#ifdef ENABLE_NLS bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR); +#ifdef HAVE_BIND_TEXTDOMAIN_CODESET bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); +#endif +#endif context = g_option_context_new ("ICONPATH"); g_option_context_add_main_entries (context, args, GETTEXT_PACKAGE); |