Index: thunar/thunar-pango-extensions.c =================================================================== --- thunar/thunar-pango-extensions.c (revision 19880) +++ thunar/thunar-pango-extensions.c (working copy) @@ -157,3 +157,21 @@ } + +/** + * thunar_pango_attr_list_underline_single: + * + * Returns a #PangoAttrList for underlining text using a single line. + * The returned list is owned by the callee and must not be freed + * or modified by the caller. + * + * Return value: a #PangoAttrList for underlining text using a single line. + **/ +PangoAttrList* +thunar_pango_attr_list_underline_single (void) +{ + static PangoAttrList *attr_list = NULL; + if (G_UNLIKELY (attr_list == NULL)) + attr_list = thunar_pango_attr_list_wrap (pango_attr_underline_new (PANGO_UNDERLINE_SINGLE), NULL); + return attr_list; +} Index: thunar/thunar-pango-extensions.h =================================================================== --- thunar/thunar-pango-extensions.h (revision 19880) +++ thunar/thunar-pango-extensions.h (working copy) @@ -24,11 +24,12 @@ G_BEGIN_DECLS; -PangoAttrList *thunar_pango_attr_list_big (void) G_GNUC_CONST G_GNUC_INTERNAL; -PangoAttrList *thunar_pango_attr_list_big_bold (void) G_GNUC_CONST G_GNUC_INTERNAL; -PangoAttrList *thunar_pango_attr_list_bold (void) G_GNUC_CONST G_GNUC_INTERNAL; -PangoAttrList *thunar_pango_attr_list_italic (void) G_GNUC_CONST G_GNUC_INTERNAL; -PangoAttrList *thunar_pango_attr_list_small_italic (void) G_GNUC_CONST G_GNUC_INTERNAL; +PangoAttrList *thunar_pango_attr_list_big (void) G_GNUC_CONST G_GNUC_INTERNAL; +PangoAttrList *thunar_pango_attr_list_big_bold (void) G_GNUC_CONST G_GNUC_INTERNAL; +PangoAttrList *thunar_pango_attr_list_bold (void) G_GNUC_CONST G_GNUC_INTERNAL; +PangoAttrList *thunar_pango_attr_list_italic (void) G_GNUC_CONST G_GNUC_INTERNAL; +PangoAttrList *thunar_pango_attr_list_small_italic (void) G_GNUC_CONST G_GNUC_INTERNAL; +PangoAttrList *thunar_pango_attr_list_underline_single (void) G_GNUC_CONST G_GNUC_INTERNAL; G_END_DECLS; Index: thunar/thunar-text-renderer.c =================================================================== --- thunar/thunar-text-renderer.c (revision 19880) +++ thunar/thunar-text-renderer.c (working copy) @@ -32,6 +32,7 @@ #include #include +#include #include @@ -39,6 +40,7 @@ enum { PROP_0, + PROP_FOLLOW_PRELIT, PROP_FOLLOW_STATE, PROP_TEXT, PROP_WRAP_MODE, @@ -129,6 +131,9 @@ gboolean follow_state; gint focus_width;; + /* underline prelited rows */ + gboolean follow_prelit; + /* cell editing support */ GtkWidget *entry; gboolean entry_menu_active; @@ -191,6 +196,20 @@ gtkcell_renderer_class->start_editing = thunar_text_renderer_start_editing; /** + * ThunarTextRenderer:follow-prelit: + * + * Whether to underline prelited cells. This is used for the single + * click support in the detailed list view. + **/ + g_object_class_install_property (gobject_class, + PROP_FOLLOW_PRELIT, + g_param_spec_boolean ("follow-prelit", + "follow-prelit", + "follow-prelit", + FALSE, + EXO_PARAM_READWRITE)); + + /** * ThunarTextRenderer:follow-state: * * Specifies whether the text renderer should render text @@ -307,6 +326,10 @@ switch (prop_id) { + case PROP_FOLLOW_PRELIT: + g_value_set_boolean (value, text_renderer->follow_prelit); + break; + case PROP_FOLLOW_STATE: g_value_set_boolean (value, text_renderer->follow_state); break; @@ -342,6 +365,10 @@ switch (prop_id) { + case PROP_FOLLOW_PRELIT: + text_renderer->follow_prelit = g_value_get_boolean (value); + break; + case PROP_FOLLOW_STATE: text_renderer->follow_state = g_value_get_boolean (value); break; @@ -489,6 +516,12 @@ state = GTK_STATE_NORMAL; } + /* check if we should follow the prelit state (used for single click support) */ + if (text_renderer->follow_prelit && (flags & GTK_CELL_RENDERER_PRELIT) != 0) + pango_layout_set_attributes (text_renderer->layout, thunar_pango_attr_list_underline_single ()); + else + pango_layout_set_attributes (text_renderer->layout, NULL); + /* setup the wrapping */ if (text_renderer->wrap_width < 0) { Index: thunar/thunar-view.c =================================================================== --- thunar/thunar-view.c (revision 19880) +++ thunar/thunar-view.c (working copy) @@ -82,6 +82,32 @@ EXO_PARAM_READABLE)); /** + * ThunarView:show-hidden: + * + * Tells whether to display hidden and backup files in the + * #ThunarView or whether to hide them. + **/ + g_object_interface_install_property (klass, + g_param_spec_boolean ("show-hidden", + "show-hidden", + "show-hidden", + FALSE, + EXO_PARAM_READWRITE)); + + /** + * ThunarView:single-click: + * + * Tells whether the #ThunarView should use single click navigation + * or double-click navigation. + **/ + g_object_interface_install_property (klass, + g_param_spec_boolean ("single-click", + "single-click", + "single-click", + FALSE, + EXO_PARAM_READWRITE)); + + /** * ThunarView:statusbar-text: * * The text to be displayed in the status bar, which is associated @@ -98,19 +124,6 @@ EXO_PARAM_READABLE)); /** - * ThunarView:show-hidden: - * - * Tells whether to display hidden and backup files in the - * #ThunarView or whether to hide them. - **/ - g_object_interface_install_property (klass, - g_param_spec_boolean ("show-hidden", - "show-hidden", - "show-hidden", - FALSE, - EXO_PARAM_READWRITE)); - - /** * ThunarView:zoom-level: * * The #ThunarZoomLevel at which the items within this @@ -202,6 +215,43 @@ /** + * thunar_view_get_single_click: + * @view : a #ThunarView instance. + * + * Returns %TRUE if @view uses single click navigation, %FALSE + * if double click navigation is used. + * + * Return value: %TRUE if @view uses single click navigation. + **/ +gboolean +thunar_view_get_single_click (ThunarView *view) +{ + g_return_val_if_fail (THUNAR_IS_VIEW (view), FALSE); + return (*THUNAR_VIEW_GET_IFACE (view)->get_single_click) (view); +} + + + +/** + * thunar_view_set_single_click: + * @view : a #ThunarView instance. + * @single_click : %TRUE to use single click navigation. + * + * If @single_click is %TRUE, @view should use single click + * navigation. Otherwise double click navigation will be + * used. + **/ +void +thunar_view_set_single_click (ThunarView *view, + gboolean single_click) +{ + g_return_if_fail (THUNAR_IS_VIEW (view)); + (*THUNAR_VIEW_GET_IFACE (view)->set_single_click) (view, single_click); +} + + + +/** * thunar_view_get_zoom_level: * @view : a #ThunarView instance. * Index: thunar/thunar-view.h =================================================================== --- thunar/thunar-view.h (revision 19880) +++ thunar/thunar-view.h (working copy) @@ -46,6 +46,10 @@ void (*set_show_hidden) (ThunarView *view, gboolean show_hidden); + gboolean (*get_single_click) (ThunarView *view); + void (*set_single_click) (ThunarView *view, + gboolean single_click); + ThunarZoomLevel (*get_zoom_level) (ThunarView *view); void (*set_zoom_level) (ThunarView *view, ThunarZoomLevel zoom_level); @@ -63,6 +67,10 @@ void thunar_view_set_show_hidden (ThunarView *view, gboolean show_hidden); +gboolean thunar_view_get_single_click (ThunarView *view); +void thunar_view_set_single_click (ThunarView *view, + gboolean single_click); + ThunarZoomLevel thunar_view_get_zoom_level (ThunarView *view); void thunar_view_set_zoom_level (ThunarView *view, ThunarZoomLevel zoom_level); Index: thunar/thunar-window.c =================================================================== --- thunar/thunar-window.c (revision 19880) +++ thunar/thunar-window.c (working copy) @@ -1252,6 +1252,9 @@ gtk_widget_grab_focus (window->view); gtk_widget_show (window->view); + // FIXME FIXME FIXME + thunar_view_set_single_click (THUNAR_VIEW (window->view), TRUE); + /* connect to the sidepane (if any) */ if (G_LIKELY (window->sidepane != NULL)) exo_binding_new (G_OBJECT (window->view), "selected-files", G_OBJECT (window->sidepane), "selected-files"); Index: thunar/thunar-standard-view.c =================================================================== --- thunar/thunar-standard-view.c (revision 19880) +++ thunar/thunar-standard-view.c (working copy) @@ -58,6 +58,7 @@ PROP_LOADING, PROP_SELECTED_FILES, PROP_SHOW_HIDDEN, + PROP_SINGLE_CLICK, PROP_STATUSBAR_TEXT, PROP_UI_MANAGER, PROP_ZOOM_LEVEL, @@ -116,6 +117,9 @@ static gboolean thunar_standard_view_get_show_hidden (ThunarView *view); static void thunar_standard_view_set_show_hidden (ThunarView *view, gboolean show_hidden); +static gboolean thunar_standard_view_get_single_click (ThunarView *view); +static void thunar_standard_view_set_single_click (ThunarView *view, + gboolean single_click); static ThunarZoomLevel thunar_standard_view_get_zoom_level (ThunarView *view); static void thunar_standard_view_set_zoom_level (ThunarView *view, ThunarZoomLevel zoom_level); @@ -248,6 +252,9 @@ /* support for file manager extensions */ ThunarxProviderFactory *provider_factory; + /* single-click support */ + gboolean single_click; + /* zoom-level support */ ThunarZoomLevel zoom_level; @@ -419,6 +426,7 @@ /* override ThunarView's properties */ g_object_class_override_property (gobject_class, PROP_STATUSBAR_TEXT, "statusbar-text"); + g_object_class_override_property (gobject_class, PROP_SINGLE_CLICK, "single-click"); g_object_class_override_property (gobject_class, PROP_SHOW_HIDDEN, "show-hidden"); g_object_class_override_property (gobject_class, PROP_ZOOM_LEVEL, "zoom-level"); @@ -474,6 +482,8 @@ iface->get_statusbar_text = thunar_standard_view_get_statusbar_text; iface->get_show_hidden = thunar_standard_view_get_show_hidden; iface->set_show_hidden = thunar_standard_view_set_show_hidden; + iface->get_single_click = thunar_standard_view_get_single_click; + iface->set_single_click = thunar_standard_view_set_single_click; iface->get_zoom_level = thunar_standard_view_get_zoom_level; iface->set_zoom_level = thunar_standard_view_set_zoom_level; iface->reset_zoom_level = thunar_standard_view_reset_zoom_level; @@ -727,6 +737,10 @@ g_value_set_boolean (value, thunar_view_get_show_hidden (THUNAR_VIEW (object))); break; + case PROP_SINGLE_CLICK: + g_value_set_boolean (value, thunar_view_get_single_click (THUNAR_VIEW (object))); + break; + case PROP_STATUSBAR_TEXT: g_value_set_static_string (value, thunar_view_get_statusbar_text (THUNAR_VIEW (object))); break; @@ -780,6 +794,10 @@ thunar_view_set_show_hidden (THUNAR_VIEW (object), g_value_get_boolean (value)); break; + case PROP_SINGLE_CLICK: + thunar_view_set_single_click (THUNAR_VIEW (object), g_value_get_boolean (value)); + break; + case PROP_UI_MANAGER: thunar_component_set_ui_manager (THUNAR_COMPONENT (object), g_value_get_object (value)); break; @@ -1150,6 +1168,30 @@ +static gboolean +thunar_standard_view_get_single_click (ThunarView *view) +{ + return THUNAR_STANDARD_VIEW (view)->priv->single_click; +} + + + +static void +thunar_standard_view_set_single_click (ThunarView *view, + gboolean single_click) +{ + ThunarStandardView *standard_view = THUNAR_STANDARD_VIEW (view); + + /* check if we have a new single click setting */ + if (G_LIKELY (standard_view->priv->single_click != single_click)) + { + standard_view->priv->single_click = single_click; + g_object_notify (G_OBJECT (standard_view), "single-click"); + } +} + + + static ThunarZoomLevel thunar_standard_view_get_zoom_level (ThunarView *view) { Index: thunar/thunar-details-view.c =================================================================== --- thunar/thunar-details-view.c (revision 19880) +++ thunar/thunar-details-view.c (working copy) @@ -28,38 +28,47 @@ #include -static void thunar_details_view_class_init (ThunarDetailsViewClass *klass); -static void thunar_details_view_init (ThunarDetailsView *details_view); -static AtkObject *thunar_details_view_get_accessible (GtkWidget *widget); -static GList *thunar_details_view_get_selected_items (ThunarStandardView *standard_view); -static void thunar_details_view_select_all (ThunarStandardView *standard_view); -static void thunar_details_view_unselect_all (ThunarStandardView *standard_view); -static void thunar_details_view_select_path (ThunarStandardView *standard_view, - GtkTreePath *path); -static void thunar_details_view_set_cursor (ThunarStandardView *standard_view, - GtkTreePath *path, - gboolean start_editing); -static void thunar_details_view_scroll_to_path (ThunarStandardView *standard_view, - GtkTreePath *path); -static GtkTreePath *thunar_details_view_get_path_at_pos (ThunarStandardView *standard_view, - gint x, - gint y); -static void thunar_details_view_highlight_path (ThunarStandardView *standard_view, - GtkTreePath *path); -static void thunar_details_view_notify_model (GtkTreeView *tree_view, - GParamSpec *pspec, - ThunarDetailsView *details_view); -static gboolean thunar_details_view_button_press_event (GtkTreeView *tree_view, - GdkEventButton *event, - ThunarDetailsView *details_view); -static gboolean thunar_details_view_key_press_event (GtkTreeView *tree_view, - GdkEventKey *event, - ThunarDetailsView *details_view); -static void thunar_details_view_row_activated (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - ThunarDetailsView *details_view); -static void thunar_details_view_zoom_level_changed (ThunarDetailsView *details_view); +static void thunar_details_view_class_init (ThunarDetailsViewClass *klass); +static void thunar_details_view_init (ThunarDetailsView *details_view); +static AtkObject *thunar_details_view_get_accessible (GtkWidget *widget); +static GList *thunar_details_view_get_selected_items (ThunarStandardView *standard_view); +static void thunar_details_view_select_all (ThunarStandardView *standard_view); +static void thunar_details_view_unselect_all (ThunarStandardView *standard_view); +static void thunar_details_view_select_path (ThunarStandardView *standard_view, + GtkTreePath *path); +static void thunar_details_view_set_cursor (ThunarStandardView *standard_view, + GtkTreePath *path, + gboolean start_editing); +static void thunar_details_view_scroll_to_path (ThunarStandardView *standard_view, + GtkTreePath *path); +static GtkTreePath *thunar_details_view_get_path_at_pos (ThunarStandardView *standard_view, + gint x, + gint y); +static void thunar_details_view_highlight_path (ThunarStandardView *standard_view, + GtkTreePath *path); +static void thunar_details_view_notify_model (GtkTreeView *tree_view, + GParamSpec *pspec, + ThunarDetailsView *details_view); +static gboolean thunar_details_view_button_press_event (GtkTreeView *tree_view, + GdkEventButton *event, + ThunarDetailsView *details_view); +static gboolean thunar_details_view_button_release_event (GtkTreeView *tree_view, + GdkEventButton *event, + ThunarDetailsView *details_view); +static gboolean thunar_details_view_motion_notify_event (GtkTreeView *tree_view, + GdkEventMotion *event, + ThunarDetailsView *details_view); +static gboolean thunar_details_view_key_press_event (GtkTreeView *tree_view, + GdkEventKey *event, + ThunarDetailsView *details_view); +static void thunar_details_view_drag_begin (GtkWidget *tree_view, + GdkDragContext *context, + ThunarDetailsView *details_view); +static void thunar_details_view_row_activated (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + ThunarDetailsView *details_view); +static void thunar_details_view_zoom_level_changed (ThunarDetailsView *details_view); @@ -71,6 +80,11 @@ struct _ThunarDetailsView { ThunarStandardView __parent__; + + /* TRUE if the next button_release_event should activate + * the row below the pointer (for single click support). + */ + gboolean button_release_activates; }; @@ -153,8 +167,14 @@ G_CALLBACK (thunar_details_view_notify_model), details_view); g_signal_connect (G_OBJECT (tree_view), "button-press-event", G_CALLBACK (thunar_details_view_button_press_event), details_view); + g_signal_connect (G_OBJECT (tree_view), "button-release-event", + G_CALLBACK (thunar_details_view_button_release_event), details_view); + g_signal_connect (G_OBJECT (tree_view), "motion-notify-event", + G_CALLBACK (thunar_details_view_motion_notify_event), details_view); g_signal_connect (G_OBJECT (tree_view), "key-press-event", G_CALLBACK (thunar_details_view_key_press_event), details_view); + g_signal_connect (G_OBJECT (tree_view), "drag-begin", + G_CALLBACK (thunar_details_view_drag_begin), details_view); g_signal_connect (G_OBJECT (tree_view), "row-activated", G_CALLBACK (thunar_details_view_row_activated), details_view); gtk_container_add (GTK_CONTAINER (details_view), tree_view); @@ -164,6 +184,9 @@ gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tree_view), TRUE); gtk_tree_view_set_enable_search (GTK_TREE_VIEW (tree_view), TRUE); + /* tell the name cell renderer to follow the prelit state (single click support) */ + exo_binding_new (G_OBJECT (details_view), "single-click", G_OBJECT (THUNAR_STANDARD_VIEW (details_view)->name_renderer), "follow-prelit"); + /* first column (icon, name) */ column = g_object_new (GTK_TYPE_TREE_VIEW_COLUMN, "expand", TRUE, @@ -413,6 +436,11 @@ ThunarFile *file; GtkAction *action; + /* check if the next button-release-event should activate the selected row (single click support) */ + details_view->button_release_activates = (event->type == GDK_BUTTON_PRESS && event->button == 1 && event->state == 0 + && thunar_view_get_single_click (THUNAR_VIEW (details_view))); + + /* we unselect all selected items if the user clicks on an empty * area of the treeview and no modifier key is active. */ @@ -460,8 +488,10 @@ gtk_tree_selection_unselect_all (selection); gtk_tree_selection_select_path (selection, path); - /* if the event was a double-click, then we'll open the file or folder (folder's are opened in new windows) */ - if (G_LIKELY (event->type == GDK_2BUTTON_PRESS)) + /* if the event was a double-click or we are in single-click mode, then + * we'll open the file or folder (folder's are opened in new windows) + */ + if (G_LIKELY (event->type == GDK_2BUTTON_PRESS || thunar_view_get_single_click (THUNAR_VIEW (details_view)))) { /* determine the file for the path */ gtk_tree_model_get_iter (GTK_TREE_MODEL (THUNAR_STANDARD_VIEW (details_view)->model), &iter, path); @@ -494,6 +524,66 @@ static gboolean +thunar_details_view_button_release_event (GtkTreeView *tree_view, + GdkEventButton *event, + ThunarDetailsView *details_view) +{ + GtkTreeViewColumn *column; + GtkTreePath *path; + + /* check if this button release should generate a "row-activated" event (single click support) */ + if (thunar_view_get_single_click (THUNAR_VIEW (details_view)) && details_view->button_release_activates) + { + /* release button_release_activates state */ + details_view->button_release_activates = FALSE; + + /* determine the path to the row that should be activated */ + if (gtk_tree_view_get_path_at_pos (tree_view, event->x, event->y, &path, &column, NULL, NULL)) + { + /* emit row-activated for the determined row */ + gtk_tree_view_row_activated (tree_view, path, column); + + /* cleanup */ + gtk_tree_path_free (path); + } + } + + return FALSE; +} + + + +static gboolean +thunar_details_view_motion_notify_event (GtkTreeView *tree_view, + GdkEventMotion *event, + ThunarDetailsView *details_view) +{ + GdkCursor *cursor; + + /* check if the event occurred on the tree view internal window */ + if (event->window != gtk_tree_view_get_bin_window (tree_view)) + return FALSE; + + /* check if we are in single click mode and check if a row is located at the event coordinates */ + if (thunar_view_get_single_click (THUNAR_VIEW (details_view)) && gtk_tree_view_get_path_at_pos (tree_view, event->x, event->y, NULL, NULL, NULL, NULL)) + { + /* setup a hand cursor */ + cursor = gdk_cursor_new (GDK_HAND2); + gdk_window_set_cursor (GTK_WIDGET (tree_view)->window, cursor); + gdk_cursor_unref (cursor); + } + else + { + /* reset the cursor to its default */ + gdk_window_set_cursor (GTK_WIDGET (tree_view)->window, NULL); + } + + return FALSE; +} + + + +static gboolean thunar_details_view_key_press_event (GtkTreeView *tree_view, GdkEventKey *event, ThunarDetailsView *details_view) @@ -511,6 +601,19 @@ static void +thunar_details_view_drag_begin (GtkWidget *tree_view, + GdkDragContext *context, + ThunarDetailsView *details_view) +{ + /* Do not activate the selected row on the next button_release_event, + * as the user started a drag operation with the mouse instead. + */ + details_view->button_release_activates = FALSE; +} + + + +static void thunar_details_view_row_activated (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column,