Exemple #1
0
static void
filename_cell_data_func (GtkTreeViewColumn *column,
                         GtkCellRenderer   *renderer,
                         GtkTreeModel      *model,
                         GtkTreeIter       *iter,
                         FMListView        *view)
{
    char *text;
    char *color;
    GdkRGBA rgba;

    GtkTreePath *path, *hover_path;
    PangoUnderline underline;

    gtk_tree_model_get (model, iter,
                        FM_LIST_MODEL_FILENAME, &text,
                        -1);

    gtk_tree_model_get (model, iter,
                        FM_LIST_MODEL_COLOR, &color,
                        -1);
    if (color != NULL) {
        gdk_rgba_parse (&rgba, color);
        //rgba.alpha = 0.85;
    }

    /*if (color) {
      GList *lrenderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(column));
      GList *l;
      for (l=lrenderers; l != NULL; l=l->next)
      g_object_set(l->data, "cell-background", color, NULL);
      g_list_free (lrenderers);
      }
      g_free (color);*/

	underline = PANGO_UNDERLINE_NONE;
    if (exo_tree_view_get_single_click (EXO_TREE_VIEW (view->tree))) {
		path = gtk_tree_model_get_path (model, iter);
        hover_path = exo_tree_view_get_hover_path (EXO_TREE_VIEW (view->tree));

		if (hover_path == NULL || gtk_tree_path_compare (path, hover_path)) {
			underline = PANGO_UNDERLINE_NONE;
		} else {
			underline = PANGO_UNDERLINE_SINGLE;
		}

		gtk_tree_path_free (path);
	}

    g_object_set (G_OBJECT (renderer),
                  "text", text,
                  "underline", underline,
                  //"cell-background", color,
                  "cell-background-rgba", &rgba,
                  NULL);
    g_free (text);
}
Exemple #2
0
static void
fm_list_view_init (FMListView *view)
{
    view->details = g_new0 (FMListViewDetails, 1);
    view->details->selection = NULL;

    create_and_set_up_tree_view (view);

    g_settings_bind (settings, "single-click", 
                     EXO_TREE_VIEW (view->tree), "single-click", 0);
    g_settings_bind (settings, "single-click-timeout", 
                     EXO_TREE_VIEW (view->tree), "single-click-timeout", 0);
    g_settings_bind (marlin_list_view_settings, "zoom-level", 
                     view, "zoom-level", 0);
}
static gboolean
exo_tree_view_move_cursor (GtkTreeView    *view,
                           GtkMovementStep step,
                           gint            count)
{
  ExoTreeView *tree_view = EXO_TREE_VIEW (view);

  /* be sure to cancel any pending single-click timeout */
  if (G_UNLIKELY (tree_view->priv->single_click_timeout_id >= 0))
    g_source_remove (tree_view->priv->single_click_timeout_id);

  /* release and reset the hover path (if any) */
  if (tree_view->priv->hover_path != NULL)
    {
      gtk_tree_path_free (tree_view->priv->hover_path);
      tree_view->priv->hover_path = NULL;
    }

  /* reset the cursor for the tree view internal window */
  if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
    gdk_window_set_cursor (gtk_tree_view_get_bin_window (GTK_TREE_VIEW (tree_view)), NULL);

  /* call the parent's handler */
  return (*GTK_TREE_VIEW_CLASS (exo_tree_view_parent_class)->move_cursor) (view, step, count);
}
static gboolean
exo_tree_view_leave_notify_event (GtkWidget        *widget,
                                  GdkEventCrossing *event)
{
  ExoTreeView *tree_view = EXO_TREE_VIEW (widget);

  /* be sure to cancel any pending single-click timeout */
  if (G_UNLIKELY (tree_view->priv->single_click_timeout_id >= 0))
    g_source_remove (tree_view->priv->single_click_timeout_id);

  /* release and reset the hover path (if any) */
  if (tree_view->priv->hover_path != NULL)
    {
      gtk_tree_path_free (tree_view->priv->hover_path);
      tree_view->priv->hover_path = NULL;
    }

  /* reset the cursor for the tree view internal window */
  if (gtk_widget_get_realized (GTK_WIDGET (tree_view)))
    gdk_window_set_cursor (gtk_tree_view_get_bin_window (GTK_TREE_VIEW (tree_view)), NULL);

  /* the next button-release-event should not activate */
  tree_view->priv->button_release_activates = FALSE;

  /* call the parent's leave notify handler */
  return (*GTK_WIDGET_CLASS (exo_tree_view_parent_class)->leave_notify_event) (widget, event);
}
static void
exo_tree_view_drag_begin (GtkWidget      *widget,
                          GdkDragContext *context)
{
  ExoTreeView *tree_view = EXO_TREE_VIEW (widget);

  /* the next button-release-event should not activate */
  tree_view->priv->button_release_activates = FALSE;

  /* call the parent's drag begin handler */
  (*GTK_WIDGET_CLASS (exo_tree_view_parent_class)->drag_begin) (widget, context);
}
static void
exo_tree_view_finalize (GObject *object)
{
  ExoTreeView *tree_view = EXO_TREE_VIEW (object);

  /* be sure to cancel any single-click timeout */
  if (G_UNLIKELY (tree_view->priv->single_click_timeout_id >= 0))
    g_source_remove (tree_view->priv->single_click_timeout_id);

  /* be sure to release the hover path */
  if (G_UNLIKELY (tree_view->priv->hover_path == NULL))
    gtk_tree_path_free (tree_view->priv->hover_path);

  (*G_OBJECT_CLASS (exo_tree_view_parent_class)->finalize) (object);
}
static void
exo_tree_view_set_property (GObject      *object,
                            guint         prop_id,
                            const GValue *value,
                            GParamSpec   *pspec)
{
  ExoTreeView *tree_view = EXO_TREE_VIEW (object);

  switch (prop_id)
    {
    case PROP_SINGLE_CLICK:
      exo_tree_view_set_single_click (tree_view, g_value_get_boolean (value));
      break;

    case PROP_SINGLE_CLICK_TIMEOUT:
      exo_tree_view_set_single_click_timeout (tree_view, g_value_get_uint (value));
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}
Exemple #8
0
gboolean on_btn_pressed(GtkWidget* view, GdkEventButton* evt, FmFolderView* fv)
{
    GList* sels;
    FmFolderViewClickType type = 0;
    GtkTreePath* tp;

    if(!fv->model)
        return FALSE;

    /* FIXME: handle single click activation */
    if( evt->type == GDK_BUTTON_PRESS )
    {
        /* special handling for ExoIconView */
        if(evt->button != 1)
        {
            if(fv->mode==FM_FV_ICON_VIEW || fv->mode==FM_FV_COMPACT_VIEW || fv->mode==FM_FV_THUMBNAIL_VIEW)
            {
                /* select the item on right click for ExoIconView */
                if(exo_icon_view_get_item_at_pos(EXO_ICON_VIEW(view), evt->x, evt->y, &tp, NULL))
                {
                    /* if the hit item is not currently selected */
                    if(!exo_icon_view_path_is_selected(EXO_ICON_VIEW(view), tp))
                    {
                        sels = exo_icon_view_get_selected_items((const struct ExoIconView *)(const struct ExoIconView *)view);
                        if( sels ) /* if there are selected items */
                        {
                            exo_icon_view_unselect_all(EXO_ICON_VIEW(view)); /* unselect all items */
                            g_list_foreach(sels, (GFunc)gtk_tree_path_free, NULL);
                            g_list_free(sels);
                        }
                        exo_icon_view_select_path(EXO_ICON_VIEW(view), tp);
                        exo_icon_view_set_cursor(EXO_ICON_VIEW(view), tp, NULL, FALSE);
                    }
                    gtk_tree_path_free(tp);
                }
            }
            else if( fv->mode == FM_FV_LIST_VIEW
                     && evt->window == gtk_tree_view_get_bin_window(GTK_TREE_VIEW(view)))
            {
                /* special handling for ExoTreeView */
                /* Fix #2986834: MAJOR PROBLEM: Deletes Wrong File Frequently. */
                GtkTreeViewColumn* col;
                if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(view), evt->x, evt->y, &tp, &col, NULL, NULL))
                {
                    GtkTreeSelection* tree_sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
                    if(!gtk_tree_selection_path_is_selected(tree_sel, tp))
                    {
                        gtk_tree_selection_unselect_all(tree_sel);
                        if(col == exo_tree_view_get_activable_column(EXO_TREE_VIEW(view)))
                        {
                            gtk_tree_selection_select_path(tree_sel, tp);
                            gtk_tree_view_set_cursor(GTK_TREE_VIEW(view), tp, NULL, FALSE);
                        }
                    }
                    gtk_tree_path_free(tp);
                }
            }
        }

        if(evt->button == 2) /* middle click */
            type = FM_FV_MIDDLE_CLICK;
        else if(evt->button == 3) /* right click */
            type = FM_FV_CONTEXT_MENU;
    }

    if( type != FM_FV_CLICK_NONE )
    {
        sels = fm_folder_view_get_selected_tree_paths(fv);
        if( sels || type == FM_FV_CONTEXT_MENU )
        {
            item_clicked(fv, sels ? sels->data : NULL, type);
            if(sels)
            {
                g_list_foreach(sels, (GFunc)gtk_tree_path_free, NULL);
                g_list_free(sels);
            }
        }
    }
    return FALSE;
}
Exemple #9
0
void fm_find_files( const char** search_dirs )
{
    FindFile* data = g_slice_new0(FindFile);
    GtkTreeIter it;
    GtkTreeViewColumn* col;
    GtkWidget *add_folder_btn, *remove_folder_btn, *img;

    GtkBuilder* builder = _gtk_builder_new_from_file( PACKAGE_UI_DIR "/find-files.ui", NULL );
    data->win = (GtkWidget*)gtk_builder_get_object( builder, "win" );
    g_object_set_data_full( G_OBJECT( data->win ), "find-files", data, (GDestroyNotify)free_data );

    GdkPixbuf* icon = NULL;
    GtkIconTheme* theme = gtk_icon_theme_get_default();
    if ( theme )
        icon = gtk_icon_theme_load_icon( theme, "spacefm-find", 48, 0, NULL );
    if ( icon )
    {
        gtk_window_set_icon( GTK_WINDOW( data->win ), icon );
        g_object_unref( icon );
    }
    else
        gtk_window_set_icon_name( GTK_WINDOW( data->win ), GTK_STOCK_FIND );

    /* search criteria pane */
    data->search_criteria = (GtkWidget*)gtk_builder_get_object( builder, "search_criteria" );

    data->fn_pattern = (GtkWidget*)gtk_builder_get_object( builder, "fn_pattern" );
    data->fn_pattern_entry = gtk_bin_get_child( GTK_BIN( data->fn_pattern ) );
    data->fn_case_sensitive = (GtkWidget*)gtk_builder_get_object( builder, "fn_case_sensitive" );
    gtk_entry_set_activates_default( (GtkEntry*)data->fn_pattern_entry, TRUE );

    /* file content */
    data->fc_pattern = (GtkWidget*)gtk_builder_get_object( builder, "fc_pattern" );
    data->fc_case_sensitive = (GtkWidget*)gtk_builder_get_object( builder, "fc_case_sensitive" );
    data->fc_use_regexp = (GtkWidget*)gtk_builder_get_object( builder, "fc_use_regexp" );

    /* advanced options */
    data->search_hidden = (GtkWidget*)gtk_builder_get_object( builder, "search_hidden" );

    /* size & date */
    data->use_size_lower = (GtkWidget*)gtk_builder_get_object( builder, "use_size_lower" );
    data->use_size_upper = (GtkWidget*)gtk_builder_get_object( builder, "use_size_upper" );
    data->size_lower = (GtkWidget*)gtk_builder_get_object( builder, "size_lower" );
    data->size_upper = (GtkWidget*)gtk_builder_get_object( builder, "size_upper" );
    data->size_lower_unit = (GtkWidget*)gtk_builder_get_object( builder, "size_lower_unit" );
    data->size_upper_unit = (GtkWidget*)gtk_builder_get_object( builder, "size_upper_unit" );
    g_signal_connect( data->use_size_lower, "toggled",
                            G_CALLBACK( on_use_size_lower_toggled ), data );
    g_signal_connect( data->use_size_upper, "toggled",
                            G_CALLBACK( on_use_size_upper_toggled ), data );
    on_use_size_lower_toggled( data->use_size_lower, data );
    on_use_size_upper_toggled( data->use_size_upper, data );

    data->date_limit = (GtkWidget*)gtk_builder_get_object( builder, "date_limit" );
    data->date1 = (GtkWidget*)gtk_builder_get_object( builder, "date1" );
    data->date2 = (GtkWidget*)gtk_builder_get_object( builder, "date2" );
    g_signal_connect( data->date_limit, "changed", G_CALLBACK( on_date_limit_changed ), data );

    /* file types */
    data->all_files = (GtkWidget*)gtk_builder_get_object( builder, "all_files" );
    data->text_files = (GtkWidget*)gtk_builder_get_object( builder, "text_files" );
    data->img_files = (GtkWidget*)gtk_builder_get_object( builder, "img_files" );
    data->audio_files = (GtkWidget*)gtk_builder_get_object( builder, "audio_files" );
    data->video_files = (GtkWidget*)gtk_builder_get_object( builder, "video_files" );

    /* places */
    data->places_list = gtk_list_store_new( 1, G_TYPE_STRING );
    data->places_view = (GtkWidget*)gtk_builder_get_object( builder, "places_view" );
    add_folder_btn = (GtkWidget*)gtk_builder_get_object( builder, "add_folder_btn" );
    remove_folder_btn = (GtkWidget*)gtk_builder_get_object( builder, "remove_folder_btn" );
    data->include_sub = (GtkWidget*)gtk_builder_get_object( builder, "include_sub" );

    if( search_dirs )
    {
        const char** dir;
        for( dir = search_dirs; *dir; ++dir )
        {
            if( g_file_test( *dir, G_FILE_TEST_IS_DIR ) )
                gtk_list_store_insert_with_values( data->places_list, &it, 0, 0, *dir, -1 );
        }
    }

    gtk_tree_view_set_model( (GtkTreeView*)data->places_view, (GtkTreeModel*)data->places_list );
    g_object_unref( data->places_list );
    col = gtk_tree_view_column_new_with_attributes(NULL, gtk_cell_renderer_text_new(), "text", 0, NULL );
    gtk_tree_view_append_column( (GtkTreeView*)data->places_view, col );

    g_signal_connect(add_folder_btn, "clicked", G_CALLBACK( on_add_search_folder ), data );
    g_signal_connect(remove_folder_btn, "clicked", G_CALLBACK( on_remove_search_folder ), data );

    /* search result pane */
    data->search_result = (GtkWidget*)gtk_builder_get_object( builder, "search_result" );
    /* replace the problematic GtkTreeView with ExoTreeView */
    data->result_view = exo_tree_view_new();
    if( app_settings.single_click )
    {
        exo_tree_view_set_single_click( EXO_TREE_VIEW( data->result_view ), TRUE );
        exo_tree_view_set_single_click_timeout( EXO_TREE_VIEW( data->result_view ),
                                                        SINGLE_CLICK_TIMEOUT );
    }
    gtk_widget_show( data->result_view );
    gtk_container_add( (GtkContainer*)gtk_builder_get_object(builder, "result_scroll"), data->result_view );
    init_search_result( data );
    g_signal_connect(data->result_view, "button-press-event", G_CALLBACK( on_view_button_press ), data );

    /* buttons */
    data->start_btn = (GtkWidget*)gtk_builder_get_object( builder, "start_btn" );
    data->stop_btn = (GtkWidget*)gtk_builder_get_object( builder, "stop_btn" );
    data->again_btn = (GtkWidget*)gtk_builder_get_object( builder, "again_btn" );
    img = gtk_image_new_from_icon_name( GTK_STOCK_REFRESH, GTK_ICON_SIZE_BUTTON );
    gtk_button_set_image( (GtkButton*)data->again_btn, img );

    g_signal_connect(data->start_btn, "clicked", G_CALLBACK( on_start_search ), data );
    g_signal_connect(data->stop_btn, "clicked", G_CALLBACK( on_stop_search ), data );
    g_signal_connect(data->again_btn, "clicked", G_CALLBACK( on_search_again ), data );


    gtk_entry_set_text( (GtkEntry*)data->fn_pattern_entry, "*" );
    gtk_editable_select_region( (GtkEditable*)data->fn_pattern_entry, 0, -1 );

    gtk_combo_box_set_active( (GtkComboBox*)data->size_lower_unit, 1 );
    gtk_spin_button_set_range( (GtkSpinButton*)data->size_lower, 0, G_MAXINT );
    gtk_combo_box_set_active( (GtkComboBox*)data->size_upper_unit, 2 );
    gtk_spin_button_set_range( (GtkSpinButton*)data->size_upper, 0, G_MAXINT );

    gtk_combo_box_set_active( (GtkComboBox*)data->date_limit, 0 );

    g_signal_connect( data->win, "delete-event", G_CALLBACK(gtk_widget_destroy), NULL );

    pcmanfm_ref();
    g_signal_connect( data->win, "destroy", G_CALLBACK(pcmanfm_unref), NULL );

    int width = xset_get_int( "main_search", "x" );
    int height = xset_get_int( "main_search", "y" );
    if ( width && height )
        gtk_window_set_default_size( GTK_WINDOW( data->win ), width, height );
    
    gtk_widget_show( data->win );

}
static void
exo_tree_view_single_click_timeout_destroy (gpointer user_data)
{
  EXO_TREE_VIEW (user_data)->priv->single_click_timeout_id = -1;
}
static gboolean
exo_tree_view_single_click_timeout (gpointer user_data)
{
  GtkTreeViewColumn *cursor_column;
  GtkTreeSelection  *selection;
  GtkTreeModel      *model;
  GtkTreePath       *cursor_path;
  GtkTreeIter        iter;
  ExoTreeView       *tree_view = EXO_TREE_VIEW (user_data);
  gboolean           hover_path_selected;
  GList             *rows;
  GList             *lp;

  //GDK_THREADS_ENTER ();  //sfm not needed because called from g_idle?

  /* verify that we are in single-click mode, have focus and a hover path */
  if (gtk_widget_has_focus (GTK_WIDGET (tree_view)) && tree_view->priv->single_click && tree_view->priv->hover_path != NULL)
    {
      /* transform the hover_path to a tree iterator */
      model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view));
      if (model != NULL && gtk_tree_model_get_iter (model, &iter, tree_view->priv->hover_path))
        {
          /* determine the current cursor path/column */
          gtk_tree_view_get_cursor (GTK_TREE_VIEW (tree_view), &cursor_path, &cursor_column);

          /* be sure the row is fully visible */
          gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (tree_view),
                            tree_view->priv->hover_path, 
                            0, //sfm was cursor_column - caused horizontal scroll
                            FALSE, 0.0f, 0.0f);

          /* determine the selection and change it appropriately */
          selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
          if (gtk_tree_selection_get_mode (selection) == GTK_SELECTION_NONE)
            {
              /* just place the cursor on the row */
              gtk_tree_view_set_cursor (GTK_TREE_VIEW (tree_view), tree_view->priv->hover_path, cursor_column, FALSE);
            }
          else if ((tree_view->priv->single_click_timeout_state & GDK_SHIFT_MASK) != 0
                && gtk_tree_selection_get_mode (selection) == GTK_SELECTION_MULTIPLE)
            {
              /* check if the item is not already selected (otherwise do nothing) */
              if (!gtk_tree_selection_path_is_selected (selection, tree_view->priv->hover_path))
                {
                  /* unselect all previously selected items */
                  gtk_tree_selection_unselect_all (selection);

                  /* since we cannot access the anchor of a GtkTreeView, we
                   * use the cursor instead which is usually the same row.
                   */
                  if (G_UNLIKELY (cursor_path == NULL))
                    {
                      /* place the cursor on the new row */
                      gtk_tree_view_set_cursor (GTK_TREE_VIEW (tree_view), tree_view->priv->hover_path, cursor_column, FALSE);
                    }
                  else
                    {
                      /* select all between the cursor and the current row */
                      gtk_tree_selection_select_range (selection, tree_view->priv->hover_path, cursor_path);
                    }
                }
            }
          else
            {
              /* remember the previously selected rows as set_cursor() clears the selection */
              rows = gtk_tree_selection_get_selected_rows (selection, NULL);

              /* check if the hover path is selected (as it will be selected after the set_cursor() call) */
              hover_path_selected = gtk_tree_selection_path_is_selected (selection, tree_view->priv->hover_path);

              /* place the cursor on the hover row */
              gtk_tree_view_set_cursor (GTK_TREE_VIEW (tree_view), tree_view->priv->hover_path, cursor_column, FALSE);

              /* restore the previous selection */
              for (lp = rows; lp != NULL; lp = lp->next)
                {
                  gtk_tree_selection_select_path (selection, lp->data);
                  gtk_tree_path_free (lp->data);
                }
              g_list_free (rows);

              /* check what to do */
              if ((gtk_tree_selection_get_mode (selection) == GTK_SELECTION_MULTIPLE ||
                  (gtk_tree_selection_get_mode (selection) == GTK_SELECTION_SINGLE && hover_path_selected))
                  && (tree_view->priv->single_click_timeout_state & GDK_CONTROL_MASK) != 0)
                {
                  /* toggle the selection state of the row */
                  if (G_UNLIKELY (hover_path_selected))
                    gtk_tree_selection_unselect_path (selection, tree_view->priv->hover_path);
                  else
                    gtk_tree_selection_select_path (selection, tree_view->priv->hover_path);
                }
              else if (G_UNLIKELY (!hover_path_selected))
                {
                  /* unselect all other rows */
                  gtk_tree_selection_unselect_all (selection);

                  /* select only the hover row */
                  gtk_tree_selection_select_path (selection, tree_view->priv->hover_path);
                }
            }

          /* cleanup */
          if (G_LIKELY (cursor_path != NULL))
            gtk_tree_path_free (cursor_path);
        }
    }

  //GDK_THREADS_LEAVE ();

  return FALSE;
}
static gboolean
exo_tree_view_motion_notify_event (GtkWidget      *widget,
                                   GdkEventMotion *event)
{
  ExoTreeView *tree_view = EXO_TREE_VIEW (widget);
  GtkTreePath *path;
  GdkCursor   *cursor;
  GtkTreeViewColumn *column;

  /* check if the event occurred on the tree view internal window and we are in single-click mode */
  if (event->window == gtk_tree_view_get_bin_window (GTK_TREE_VIEW (tree_view)) && tree_view->priv->single_click)
    {
      /* check if we're doing a rubberband selection right now (which means DnD is blocked) */
      if (G_UNLIKELY (tree_view->priv->button_release_unblocks_dnd))
        {
          /* we're doing a rubberband selection, so don't activate anything */
          tree_view->priv->button_release_activates = FALSE;

          /* also be sure to reset the cursor */
          gdk_window_set_cursor (event->window, NULL);
        }
      else
        {
          /* determine the path at the event coordinates */
          if (!gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (tree_view), event->x, event->y, &path, &column, NULL, NULL))
            path = NULL;

          /* determine if the column is activable */
          if( tree_view->priv->activable_column && column != tree_view->priv->activable_column )
           {
             if(path)
               {
                 gtk_tree_path_free(path);
                 path = NULL;
               }
           }

          /* check if we have a new path */
          if ((path == NULL && tree_view->priv->hover_path != NULL) || (path != NULL && tree_view->priv->hover_path == NULL)
              || (path != NULL && tree_view->priv->hover_path != NULL && gtk_tree_path_compare (path, tree_view->priv->hover_path) != 0))
            {
              /* release the previous hover path */
              if (tree_view->priv->hover_path != NULL)
                gtk_tree_path_free (tree_view->priv->hover_path);

              /* setup the new path */
              tree_view->priv->hover_path = path;

              /* check if we're over a row right now */
              if (G_LIKELY (path != NULL))
                {
                  /* setup the hand cursor to indicate that the row at the pointer can be activated with a single click */
                  cursor = gdk_cursor_new (GDK_HAND2);
                  gdk_window_set_cursor (event->window, cursor);
                  gdk_cursor_unref (cursor);
                }
              else
                {
                  /* reset the cursor to its default */
                  gdk_window_set_cursor (event->window, NULL);
                }

              /* check if autoselection is enabled and the pointer is over a row */
              if (G_LIKELY (tree_view->priv->single_click_timeout > 0 && tree_view->priv->hover_path != NULL))
                {
                  /* cancel any previous single-click timeout */
                  if (G_LIKELY (tree_view->priv->single_click_timeout_id >= 0))
                    g_source_remove (tree_view->priv->single_click_timeout_id);

                  /* remember the current event state */
                  tree_view->priv->single_click_timeout_state = event->state;

                  /* schedule a new single-click timeout */
                  tree_view->priv->single_click_timeout_id = g_timeout_add_full (G_PRIORITY_LOW, tree_view->priv->single_click_timeout,
                                                                                 exo_tree_view_single_click_timeout, tree_view,
                                                                                 exo_tree_view_single_click_timeout_destroy);
                }
            }
          else
            {
              /* release the path resources */
              if (path != NULL)
                gtk_tree_path_free (path);
            }
        }
    }

  /* call the parent's motion notify handler */
  return (*GTK_WIDGET_CLASS (exo_tree_view_parent_class)->motion_notify_event) (widget, event);
}
static gboolean
exo_tree_view_button_release_event (GtkWidget      *widget,
                                    GdkEventButton *event)
{
  GtkTreeViewColumn *column;
  GtkTreeSelection  *selection;
  GtkTreePath       *path;
  ExoTreeView       *tree_view = EXO_TREE_VIEW (widget);

  /* verify that the release event is for the internal tree view window */
  if (G_LIKELY (event->window == gtk_tree_view_get_bin_window (GTK_TREE_VIEW (tree_view))))
    {
      /* check if we're in single-click mode and the button-release-event should emit a "row-activate" */
      if (G_UNLIKELY (tree_view->priv->single_click && tree_view->priv->button_release_activates))
        {
          /* reset the "release-activates" flag */
          tree_view->priv->button_release_activates = FALSE;

          /* determine the path to the row that should be activated */
          if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (tree_view), event->x, event->y, &path, &column, NULL, NULL))
            {
              /* emit row-activated for the determined row */
              if( ! tree_view->priv->activable_column || tree_view->priv->activable_column == column )
                gtk_tree_view_row_activated (GTK_TREE_VIEW (tree_view), path, column);

              /* cleanup */
              gtk_tree_path_free (path);
            }
        }
      else if ((event->state & gtk_accelerator_get_default_mod_mask ()) == 0 && !tree_view->priv->button_release_unblocks_dnd)
        {
          /* determine the path on which the button was released and select only this row, this way
           * the user is still able to alter the selection easily if all rows are selected.
           */
          if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (tree_view), event->x, event->y, &path, &column, NULL, NULL))
            {
              /* check if the path is selected */
              selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
              if (gtk_tree_selection_path_is_selected (selection, path))
                {
                  /* unselect all rows */
                  gtk_tree_selection_unselect_all (selection);

                  /* select the row and place the cursor on it */
                  gtk_tree_view_set_cursor (GTK_TREE_VIEW (tree_view), path, column, FALSE);
                }

              /* cleanup */
              gtk_tree_path_free (path);
            }
        }
    }

  /* check if we need to re-enable drag and drop */
  if (G_LIKELY (tree_view->priv->button_release_unblocks_dnd))
    {
      gpointer drag_data = g_object_get_data (G_OBJECT (tree_view), I_("gtk-site-data"));
      if (G_LIKELY (drag_data != NULL))
        {
          g_signal_handlers_unblock_matched (G_OBJECT (tree_view),
                                             G_SIGNAL_MATCH_DATA,
                                             0, 0, NULL, NULL,
                                             drag_data);
        }
      tree_view->priv->button_release_unblocks_dnd = FALSE;
    }

  /* check if we need to re-enable rubberbanding */
  if (G_UNLIKELY (tree_view->priv->button_release_enables_rubber_banding))
    {
      gtk_tree_view_set_rubber_banding (GTK_TREE_VIEW (tree_view), TRUE);
      tree_view->priv->button_release_enables_rubber_banding = FALSE;
    }

  /* call the parent's button release handler */
  return (*GTK_WIDGET_CLASS (exo_tree_view_parent_class)->button_release_event) (widget, event);
}
static gboolean
exo_tree_view_button_press_event (GtkWidget      *widget,
                                  GdkEventButton *event)
{
  GtkTreeSelection *selection;
  ExoTreeView      *tree_view = EXO_TREE_VIEW (widget);
  GtkTreePath      *path = NULL;
  gboolean          result;
  GList            *selected_paths = NULL;
  GList            *lp;
  GtkTreeViewColumn* col;
  gboolean treat_as_blank = FALSE;

  /* by default we won't emit "row-activated" on button-release-events */
  tree_view->priv->button_release_activates = FALSE;

  /* grab the tree selection */
  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));

  /* be sure to cancel any pending single-click timeout */
  if (G_UNLIKELY (tree_view->priv->single_click_timeout_id >= 0))
    g_source_remove (tree_view->priv->single_click_timeout_id);

  /* check if the button press was on the internal tree view window */
  if (G_LIKELY (event->window == gtk_tree_view_get_bin_window (GTK_TREE_VIEW (tree_view))))
    {
      /* determine the path at the event coordinates */
      if (!gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (tree_view), event->x, event->y, &path, &col, NULL, NULL))
        path = NULL;

      if( tree_view->priv->activable_column && col != tree_view->priv->activable_column )
        {
          treat_as_blank = TRUE;
          if( path )
            {
              gtk_tree_path_free( path );
              path = NULL;
            }
          gtk_tree_selection_unselect_all (selection);
        }

      /* we unselect all selected items if the user clicks on an empty
       * area of the tree view and no modifier key is active.
       */
      if (path == NULL && (event->state & gtk_accelerator_get_default_mod_mask ()) == 0)
        gtk_tree_selection_unselect_all (selection);

      /* completely ignore double-clicks in single-click mode */
      if (tree_view->priv->single_click && event->type == GDK_2BUTTON_PRESS)
        {
          /* make sure we ignore the GDK_BUTTON_RELEASE
           * event for this GDK_2BUTTON_PRESS event.
           */
          gtk_tree_path_free (path);
          return TRUE;
        }

      /* check if the next button-release-event should activate the selected row (single click support) */
      tree_view->priv->button_release_activates = (tree_view->priv->single_click && event->type == GDK_BUTTON_PRESS && event->button == 1
                                                   && (event->state & gtk_accelerator_get_default_mod_mask ()) == 0);
    }

  /* unfortunately GtkTreeView will unselect rows except the clicked one,
   * which makes dragging from a GtkTreeView problematic. That's why we
   * remember the selected paths here and restore them later.
   */
  if (event->type == GDK_BUTTON_PRESS && (event->state & gtk_accelerator_get_default_mod_mask ()) == 0
      && path != NULL && gtk_tree_selection_path_is_selected (selection, path))
    {
      /* if no custom select function is set, we simply use exo_noop_false here,
       * to tell the tree view that it may not alter the selection.
       */
//MOD disabled exo_noop_false due to GTK 2.20 bug https://bugzilla.gnome.org/show_bug.cgi?id=612802
/*      if (G_LIKELY (selection->user_func == NULL))
        gtk_tree_selection_set_select_function (selection, (GtkTreeSelectionFunc) exo_noop_false, NULL, NULL);
      else
*/
        selected_paths = gtk_tree_selection_get_selected_rows (selection, NULL);
    }

  /* Rubberbanding in GtkTreeView 2.9.0 and above is rather buggy, unfortunately, and
   * doesn't interact properly with GTKs own DnD mechanism. So we need to block all
   * dragging here when pressing the mouse button on a not yet selected row if
   * rubberbanding is active, or disable rubberbanding when starting a drag.
   */
  if (gtk_tree_selection_get_mode (selection) == GTK_SELECTION_MULTIPLE
      && gtk_tree_view_get_rubber_banding (GTK_TREE_VIEW (tree_view))
      && event->button == 1 && event->type == GDK_BUTTON_PRESS)
    {
      /* check if clicked on empty area or on a not yet selected row */
      if (G_LIKELY (path == NULL || !gtk_tree_selection_path_is_selected (selection, path)))
        {
          /* need to disable drag and drop because we're rubberbanding now */
          gpointer drag_data = g_object_get_data (G_OBJECT (tree_view), I_("gtk-site-data"));
          if (G_LIKELY (drag_data != NULL))
            {
              g_signal_handlers_block_matched (G_OBJECT (tree_view),
                                               G_SIGNAL_MATCH_DATA,
                                               0, 0, NULL, NULL,
                                               drag_data);
            }

          /* remember to re-enable drag and drop later */
          tree_view->priv->button_release_unblocks_dnd = TRUE;
        }
      else
        {
          /* need to disable rubberbanding because we're dragging now */
          gtk_tree_view_set_rubber_banding (GTK_TREE_VIEW (tree_view), FALSE);

          /* remember to re-enable rubberbanding later */
          tree_view->priv->button_release_enables_rubber_banding = TRUE;
        }
    }

  /* call the parent's button press handler */
  result = (*GTK_WIDGET_CLASS (exo_tree_view_parent_class)->button_press_event) (widget, event);

  if( treat_as_blank )
    gtk_tree_selection_unselect_all( selection );

  /* restore previous selection if the path is still selected */
  if (event->type == GDK_BUTTON_PRESS && (event->state & gtk_accelerator_get_default_mod_mask ()) == 0
      && path != NULL && gtk_tree_selection_path_is_selected (selection, path))
    {
      /* check if we have to restore paths */
      if (G_LIKELY (gtk_tree_selection_get_select_function (selection) == (GtkTreeSelectionFunc) exo_noop_false))
        {
          /* just reset the select function (previously set to exo_noop_false),
           * there's no clean way to do this, so what the heck.
           */
          gtk_tree_selection_set_select_function (selection, NULL, NULL, NULL);
        }
      else
        {
          /* select all previously selected paths */
          for (lp = selected_paths; lp != NULL; lp = lp->next)
            gtk_tree_selection_select_path (selection, lp->data);
        }
    }

  /* release the path (if any) */
  if (G_LIKELY (path != NULL))
    gtk_tree_path_free (path);

  /* release the selected paths list */
  g_list_foreach (selected_paths, (GFunc) gtk_tree_path_free, NULL);
  g_list_free (selected_paths);

  return result;
}