forked from AuroraMiddleware/gtk
155c45eba5
This was silently broken - the code was just assuming that the text cell renderer is item no. 6 on the list of all cells. That doesn't work so well if the cell renderers are set up elsewhere and get rearranged. Fix this by keeping an explicit pointer to the the text cell.
4938 lines
150 KiB
C
4938 lines
150 KiB
C
/*
|
||
* GtkPlacesSidebar - sidebar widget for places in the filesystem
|
||
*
|
||
* This code comes from Nautilus, GNOME’s file manager.
|
||
*
|
||
* This library is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU General Public License as
|
||
* published by the Free Software Foundation; either version 2 of the
|
||
* License, or (at your option) any later version.
|
||
*
|
||
* This library is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
* General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License
|
||
* along with this library; if not, write to the Free Software
|
||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
*
|
||
* Authors : Mr Jamie McCracken (jamiemcc at blueyonder dot co dot uk)
|
||
* Cosimo Cecchi <cosimoc@gnome.org>
|
||
* Federico Mena Quintero <federico@gnome.org>
|
||
*
|
||
*/
|
||
|
||
/* TODO:
|
||
*
|
||
* * Fix instances of "#if 0"
|
||
*
|
||
* * Fix FIXMEs
|
||
*
|
||
* * Grep for "NULL-GError" and see if they should be taken care of
|
||
*
|
||
* * Although we do g_mount_unmount_with_operation(), Nautilus used to do
|
||
* nautilus_file_operations_unmount_mount_full() to unmount a volume. With
|
||
* that, Nautilus does the "volume has trash, empty it first?" dance. Cosimo
|
||
* suggests that this logic should be part of GtkMountOperation, which can
|
||
* have Unix-specific code for emptying trash.
|
||
*
|
||
* * Sync nautilus commit 17a85b78acc78b573c2e1776b348ed348e19adb7
|
||
*
|
||
*/
|
||
|
||
#include "config.h"
|
||
|
||
#include <gio/gio.h>
|
||
|
||
#include "gdk/gdkkeysyms.h"
|
||
#include "gtkbookmarksmanager.h"
|
||
#include "gtkcelllayout.h"
|
||
#include "gtkcellrenderertext.h"
|
||
#include "gtkcellrendererpixbuf.h"
|
||
#include "gtkfilesystem.h"
|
||
#include "gtkicontheme.h"
|
||
#include "gtkintl.h"
|
||
#include "gtkmain.h"
|
||
#include "gtkmarshalers.h"
|
||
#include "gtkmenuitem.h"
|
||
#include "gtkmountoperation.h"
|
||
#include "gtkplacessidebar.h"
|
||
#include "gtkscrolledwindow.h"
|
||
#include "gtkseparatormenuitem.h"
|
||
#include "gtksettings.h"
|
||
#include "gtktrashmonitor.h"
|
||
#include "gtktreeselection.h"
|
||
#include "gtktreednd.h"
|
||
#include "gtktypebuiltins.h"
|
||
#include "gtkwindow.h"
|
||
|
||
/**
|
||
* SECTION:gtkplacessidebar
|
||
* @Short_description: Sidebar that displays frequently-used places in the file system
|
||
* @Title: GtkPlacesSidebar
|
||
* @See_also: #GtkFileChooser
|
||
*
|
||
* #GtkPlacesSidebar is a widget that displays a list of frequently-used places in the
|
||
* file system: the user’s home directory, the user’s bookmarks, and volumes and drives.
|
||
* This widget is used as a sidebar in #GtkFileChooser and may be used by file managers
|
||
* and similar programs.
|
||
*
|
||
* The places sidebar displays drives and volumes, and will automatically mount
|
||
* or unmount them when the user selects them.
|
||
*
|
||
* Applications can hook to various signals in the places sidebar to customize
|
||
* its behavior. For example, they can add extra commands to the context menu
|
||
* of the sidebar.
|
||
*
|
||
* While bookmarks are completely in control of the user, the places sidebar also
|
||
* allows individual applications to provide extra shortcut folders that are unique
|
||
* to each application. For example, a Paint program may want to add a shortcut
|
||
* for a Clipart folder. You can do this with gtk_places_sidebar_add_shortcut().
|
||
*
|
||
* To make use of the places sidebar, an application at least needs to connect
|
||
* to the #GtkPlacesSidebar::open-location signal. This is emitted when the
|
||
* user selects in the sidebar a location to open. The application should also
|
||
* call gtk_places_sidebar_set_location() when it changes the currently-viewed
|
||
* location.
|
||
*/
|
||
|
||
#define EJECT_BUTTON_XPAD 6
|
||
#define ICON_CELL_XPAD 6
|
||
#define TIMEOUT_EXPAND 500
|
||
|
||
/* These are used when a destination-side DND operation is taking place.
|
||
* Normally, when a file is being hovered directly over a bookmark,
|
||
* we’ll be in DROP_STATE_NORMAL.
|
||
*
|
||
* But when a file is being hovered between bookmarks, this means the user
|
||
* may want to create a new bookmark for that file between those bookmarks.
|
||
* In that case, the drop state will be *not* DROP_STATE_NORMAL.
|
||
*
|
||
* When the drop state is FADING_OUT, it means that the user is hovering
|
||
* directly over an existing bookmark and an immediate drop will cause the
|
||
* file being dragged to be dropped on the bookmark, instead of causing
|
||
* a new bookmark to be created.
|
||
*/
|
||
typedef enum {
|
||
DROP_STATE_NORMAL,
|
||
DROP_STATE_NEW_BOOKMARK_FADING_IN,
|
||
DROP_STATE_NEW_BOOKMARK_ARMED,
|
||
DROP_STATE_NEW_BOOKMARK_FADING_OUT
|
||
} DropState;
|
||
|
||
struct _GtkPlacesSidebar {
|
||
GtkScrolledWindow parent;
|
||
|
||
GtkTreeView *tree_view;
|
||
GtkCellRenderer *eject_icon_cell_renderer;
|
||
GtkCellRenderer *text_cell_renderer;
|
||
GtkListStore *store;
|
||
GtkBookmarksManager *bookmarks_manager;
|
||
GVolumeMonitor *volume_monitor;
|
||
GtkTrashMonitor *trash_monitor;
|
||
GtkSettings *gtk_settings;
|
||
|
||
gulong trash_monitor_changed_id;
|
||
|
||
gboolean devices_header_added;
|
||
gboolean bookmarks_header_added;
|
||
|
||
/* DND */
|
||
GList *drag_list; /* list of GFile */
|
||
gint drag_data_info;
|
||
|
||
/* volume mounting - delayed open process */
|
||
GtkPlacesOpenFlags go_to_after_mount_open_flags;
|
||
|
||
GtkWidget *popup_menu;
|
||
GSList *shortcuts;
|
||
|
||
GDBusProxy *hostnamed_proxy;
|
||
GCancellable *hostnamed_cancellable;
|
||
gchar *hostname;
|
||
|
||
GtkPlacesOpenFlags open_flags;
|
||
|
||
DropState drop_state;
|
||
gint new_bookmark_index;
|
||
guint drag_leave_timeout_id;
|
||
gchar *drop_target_uri;
|
||
guint switch_location_timer;
|
||
|
||
guint mounting : 1;
|
||
guint drag_data_received : 1;
|
||
guint drop_occured : 1;
|
||
guint show_desktop_set : 1;
|
||
guint show_desktop : 1;
|
||
guint show_connect_to_server : 1;
|
||
guint show_enter_location : 1;
|
||
guint local_only : 1;
|
||
};
|
||
|
||
struct _GtkPlacesSidebarClass {
|
||
GtkScrolledWindowClass parent;
|
||
|
||
void (* open_location) (GtkPlacesSidebar *sidebar,
|
||
GFile *location,
|
||
GtkPlacesOpenFlags open_flags);
|
||
void (* populate_popup) (GtkPlacesSidebar *sidebar,
|
||
GtkMenu *menu,
|
||
GFile *selected_item,
|
||
GVolume *selected_volume);
|
||
void (* show_error_message) (GtkPlacesSidebar *sidebar,
|
||
const gchar *primary,
|
||
const gchar *secondary);
|
||
void (* show_connect_to_server) (GtkPlacesSidebar *sidebar);
|
||
GdkDragAction (* drag_action_requested) (GtkPlacesSidebar *sidebar,
|
||
GdkDragContext *context,
|
||
GFile *dest_file,
|
||
GList *source_file_list);
|
||
GdkDragAction (* drag_action_ask) (GtkPlacesSidebar *sidebar,
|
||
GdkDragAction actions);
|
||
void (* drag_perform_drop) (GtkPlacesSidebar *sidebar,
|
||
GFile *dest_file,
|
||
GList *source_file_list,
|
||
GdkDragAction action);
|
||
void (* show_enter_location) (GtkPlacesSidebar *sidebar);
|
||
};
|
||
|
||
enum {
|
||
PLACES_SIDEBAR_COLUMN_ROW_TYPE,
|
||
PLACES_SIDEBAR_COLUMN_URI,
|
||
PLACES_SIDEBAR_COLUMN_DRIVE,
|
||
PLACES_SIDEBAR_COLUMN_VOLUME,
|
||
PLACES_SIDEBAR_COLUMN_MOUNT,
|
||
PLACES_SIDEBAR_COLUMN_NAME,
|
||
PLACES_SIDEBAR_COLUMN_GICON,
|
||
PLACES_SIDEBAR_COLUMN_INDEX,
|
||
PLACES_SIDEBAR_COLUMN_EJECT,
|
||
PLACES_SIDEBAR_COLUMN_NO_EJECT,
|
||
PLACES_SIDEBAR_COLUMN_BOOKMARK,
|
||
PLACES_SIDEBAR_COLUMN_TOOLTIP,
|
||
PLACES_SIDEBAR_COLUMN_SECTION_TYPE,
|
||
PLACES_SIDEBAR_COLUMN_HEADING_TEXT,
|
||
PLACES_SIDEBAR_COLUMN_COUNT
|
||
};
|
||
|
||
typedef enum {
|
||
PLACES_BUILT_IN,
|
||
PLACES_XDG_DIR,
|
||
PLACES_MOUNTED_VOLUME,
|
||
PLACES_BOOKMARK,
|
||
PLACES_HEADING,
|
||
PLACES_CONNECT_TO_SERVER,
|
||
PLACES_ENTER_LOCATION,
|
||
PLACES_DROP_FEEDBACK
|
||
} PlaceType;
|
||
|
||
typedef enum {
|
||
SECTION_DEVICES,
|
||
SECTION_BOOKMARKS,
|
||
SECTION_COMPUTER,
|
||
SECTION_NETWORK
|
||
} SectionType;
|
||
|
||
enum {
|
||
OPEN_LOCATION,
|
||
POPULATE_POPUP,
|
||
SHOW_ERROR_MESSAGE,
|
||
SHOW_CONNECT_TO_SERVER,
|
||
SHOW_ENTER_LOCATION,
|
||
DRAG_ACTION_REQUESTED,
|
||
DRAG_ACTION_ASK,
|
||
DRAG_PERFORM_DROP,
|
||
LAST_SIGNAL
|
||
};
|
||
|
||
enum {
|
||
PROP_LOCATION = 1,
|
||
PROP_OPEN_FLAGS,
|
||
PROP_SHOW_DESKTOP,
|
||
PROP_SHOW_CONNECT_TO_SERVER,
|
||
PROP_SHOW_ENTER_LOCATION,
|
||
PROP_LOCAL_ONLY,
|
||
NUM_PROPERTIES
|
||
};
|
||
|
||
/* Names for themed icons */
|
||
#define ICON_NAME_HOME "user-home-symbolic"
|
||
#define ICON_NAME_DESKTOP "user-desktop-symbolic"
|
||
#define ICON_NAME_FILESYSTEM "drive-harddisk-symbolic"
|
||
#define ICON_NAME_EJECT "media-eject-symbolic"
|
||
#define ICON_NAME_NETWORK "network-workgroup-symbolic"
|
||
#define ICON_NAME_NETWORK_SERVER "network-server-symbolic"
|
||
#define ICON_NAME_FOLDER_NETWORK "folder-remote-symbolic"
|
||
|
||
#define ICON_NAME_FOLDER "folder-symbolic"
|
||
#define ICON_NAME_FOLDER_DESKTOP "user-desktop-symbolic"
|
||
#define ICON_NAME_FOLDER_DOCUMENTS "folder-documents-symbolic"
|
||
#define ICON_NAME_FOLDER_DOWNLOAD "folder-download-symbolic"
|
||
#define ICON_NAME_FOLDER_MUSIC "folder-music-symbolic"
|
||
#define ICON_NAME_FOLDER_PICTURES "folder-pictures-symbolic"
|
||
#define ICON_NAME_FOLDER_PUBLIC_SHARE "folder-publicshare-symbolic"
|
||
#define ICON_NAME_FOLDER_TEMPLATES "folder-templates-symbolic"
|
||
#define ICON_NAME_FOLDER_VIDEOS "folder-videos-symbolic"
|
||
#define ICON_NAME_FOLDER_SAVED_SEARCH "folder-saved-search-symbolic"
|
||
|
||
static guint places_sidebar_signals [LAST_SIGNAL] = { 0 };
|
||
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
|
||
|
||
static void open_selected_bookmark (GtkPlacesSidebar *sidebar,
|
||
GtkTreeModel *model,
|
||
GtkTreeIter *iter,
|
||
GtkPlacesOpenFlags open_flags);
|
||
static gboolean eject_or_unmount_bookmark (GtkPlacesSidebar *sidebar,
|
||
GtkTreePath *path);
|
||
static gboolean eject_or_unmount_selection (GtkPlacesSidebar *sidebar);
|
||
static void check_unmount_and_eject (GMount *mount,
|
||
GVolume *volume,
|
||
GDrive *drive,
|
||
gboolean *show_unmount,
|
||
gboolean *show_eject);
|
||
|
||
/* Identifiers for target types */
|
||
enum {
|
||
GTK_TREE_MODEL_ROW,
|
||
TEXT_URI_LIST
|
||
};
|
||
|
||
/* Target types for dragging from the shortcuts list */
|
||
static const GtkTargetEntry dnd_source_targets[] = {
|
||
{ "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW }
|
||
};
|
||
|
||
/* Target types for dropping into the shortcuts list */
|
||
static const GtkTargetEntry dnd_drop_targets [] = {
|
||
{ "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW }
|
||
};
|
||
|
||
/* Drag and drop interface declarations */
|
||
typedef struct {
|
||
GtkListStore parent;
|
||
|
||
GtkPlacesSidebar *sidebar;
|
||
} ShortcutsModel;
|
||
|
||
typedef struct {
|
||
GtkListStoreClass parent_class;
|
||
} ShortcutsModelClass;
|
||
|
||
#define SHORTCUTS_MODEL_TYPE (shortcuts_model_get_type ())
|
||
#define SHORTCUTS_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHORTCUTS_MODEL_TYPE, ShortcutsModel))
|
||
|
||
static GType shortcuts_model_get_type (void);
|
||
static void shortcuts_model_drag_source_iface_init (GtkTreeDragSourceIface *iface);
|
||
|
||
G_DEFINE_TYPE_WITH_CODE (ShortcutsModel, shortcuts_model, GTK_TYPE_LIST_STORE,
|
||
G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
|
||
shortcuts_model_drag_source_iface_init));
|
||
|
||
static GtkListStore *shortcuts_model_new (GtkPlacesSidebar *sidebar);
|
||
|
||
G_DEFINE_TYPE (GtkPlacesSidebar, gtk_places_sidebar, GTK_TYPE_SCROLLED_WINDOW);
|
||
|
||
static void
|
||
emit_open_location (GtkPlacesSidebar *sidebar,
|
||
GFile *location,
|
||
GtkPlacesOpenFlags open_flags)
|
||
{
|
||
if ((open_flags & sidebar->open_flags) == 0)
|
||
open_flags = GTK_PLACES_OPEN_NORMAL;
|
||
|
||
g_signal_emit (sidebar, places_sidebar_signals[OPEN_LOCATION], 0,
|
||
location, open_flags);
|
||
}
|
||
|
||
static void
|
||
emit_populate_popup (GtkPlacesSidebar *sidebar,
|
||
GtkMenu *menu,
|
||
GFile *selected_item,
|
||
GVolume *selected_volume)
|
||
{
|
||
g_signal_emit (sidebar, places_sidebar_signals[POPULATE_POPUP], 0,
|
||
menu, selected_item, selected_volume);
|
||
}
|
||
|
||
static void
|
||
emit_show_error_message (GtkPlacesSidebar *sidebar,
|
||
const gchar *primary,
|
||
const gchar *secondary)
|
||
{
|
||
g_signal_emit (sidebar, places_sidebar_signals[SHOW_ERROR_MESSAGE], 0,
|
||
primary, secondary);
|
||
}
|
||
|
||
static void
|
||
emit_show_connect_to_server (GtkPlacesSidebar *sidebar)
|
||
{
|
||
g_signal_emit (sidebar, places_sidebar_signals[SHOW_CONNECT_TO_SERVER], 0);
|
||
}
|
||
|
||
static void
|
||
emit_show_enter_location (GtkPlacesSidebar *sidebar)
|
||
{
|
||
g_signal_emit (sidebar, places_sidebar_signals[SHOW_ENTER_LOCATION], 0);
|
||
}
|
||
|
||
static GdkDragAction
|
||
emit_drag_action_requested (GtkPlacesSidebar *sidebar,
|
||
GdkDragContext *context,
|
||
GFile *dest_file,
|
||
GList *source_file_list)
|
||
{
|
||
GdkDragAction ret_action = 0;
|
||
|
||
g_signal_emit (sidebar, places_sidebar_signals[DRAG_ACTION_REQUESTED], 0,
|
||
context, dest_file, source_file_list, &ret_action);
|
||
|
||
return ret_action;
|
||
}
|
||
|
||
static GdkDragAction
|
||
emit_drag_action_ask (GtkPlacesSidebar *sidebar,
|
||
GdkDragAction actions)
|
||
{
|
||
GdkDragAction ret_action = 0;
|
||
|
||
g_signal_emit (sidebar, places_sidebar_signals[DRAG_ACTION_ASK], 0,
|
||
actions, &ret_action);
|
||
|
||
return ret_action;
|
||
}
|
||
|
||
static void
|
||
emit_drag_perform_drop (GtkPlacesSidebar *sidebar,
|
||
GFile *dest_file,
|
||
GList *source_file_list,
|
||
GdkDragAction action)
|
||
{
|
||
g_signal_emit (sidebar, places_sidebar_signals[DRAG_PERFORM_DROP], 0,
|
||
dest_file, source_file_list, action);
|
||
}
|
||
|
||
static gint
|
||
get_icon_size (GtkPlacesSidebar *sidebar)
|
||
{
|
||
gint width, height;
|
||
|
||
if (gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, &height))
|
||
return MAX (width, height);
|
||
else
|
||
return 16;
|
||
}
|
||
|
||
static GtkTreeIter
|
||
add_heading (GtkPlacesSidebar *sidebar,
|
||
SectionType section_type,
|
||
const gchar *title)
|
||
{
|
||
GtkTreeIter iter;
|
||
|
||
gtk_list_store_append (sidebar->store, &iter);
|
||
gtk_list_store_set (sidebar->store, &iter,
|
||
PLACES_SIDEBAR_COLUMN_ROW_TYPE, PLACES_HEADING,
|
||
PLACES_SIDEBAR_COLUMN_SECTION_TYPE, section_type,
|
||
PLACES_SIDEBAR_COLUMN_HEADING_TEXT, title,
|
||
PLACES_SIDEBAR_COLUMN_EJECT, FALSE,
|
||
PLACES_SIDEBAR_COLUMN_NO_EJECT, TRUE,
|
||
-1);
|
||
|
||
return iter;
|
||
}
|
||
|
||
static void
|
||
check_heading_for_section (GtkPlacesSidebar *sidebar,
|
||
SectionType section_type)
|
||
{
|
||
switch (section_type)
|
||
{
|
||
case SECTION_DEVICES:
|
||
if (!sidebar->devices_header_added)
|
||
{
|
||
add_heading (sidebar, SECTION_DEVICES, _("Devices"));
|
||
sidebar->devices_header_added = TRUE;
|
||
}
|
||
break;
|
||
|
||
case SECTION_BOOKMARKS:
|
||
if (!sidebar->bookmarks_header_added)
|
||
{
|
||
add_heading (sidebar, SECTION_BOOKMARKS, _("Bookmarks"));
|
||
sidebar->bookmarks_header_added = TRUE;
|
||
}
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void
|
||
add_place (GtkPlacesSidebar *sidebar,
|
||
PlaceType place_type,
|
||
SectionType section_type,
|
||
const gchar *name,
|
||
GIcon *icon,
|
||
const gchar *uri,
|
||
GDrive *drive,
|
||
GVolume *volume,
|
||
GMount *mount,
|
||
const gint index,
|
||
const gchar *tooltip)
|
||
{
|
||
GtkTreeIter iter;
|
||
gboolean show_eject, show_unmount;
|
||
gboolean show_eject_button;
|
||
|
||
check_heading_for_section (sidebar, section_type);
|
||
|
||
check_unmount_and_eject (mount, volume, drive,
|
||
&show_unmount, &show_eject);
|
||
|
||
if (show_unmount || show_eject)
|
||
g_assert (place_type != PLACES_BOOKMARK);
|
||
|
||
if (mount == NULL)
|
||
show_eject_button = FALSE;
|
||
else
|
||
show_eject_button = (show_unmount || show_eject);
|
||
|
||
gtk_list_store_append (sidebar->store, &iter);
|
||
gtk_list_store_set (sidebar->store, &iter,
|
||
PLACES_SIDEBAR_COLUMN_GICON, icon,
|
||
PLACES_SIDEBAR_COLUMN_NAME, name,
|
||
PLACES_SIDEBAR_COLUMN_URI, uri,
|
||
PLACES_SIDEBAR_COLUMN_DRIVE, drive,
|
||
PLACES_SIDEBAR_COLUMN_VOLUME, volume,
|
||
PLACES_SIDEBAR_COLUMN_MOUNT, mount,
|
||
PLACES_SIDEBAR_COLUMN_ROW_TYPE, place_type,
|
||
PLACES_SIDEBAR_COLUMN_INDEX, index,
|
||
PLACES_SIDEBAR_COLUMN_EJECT, show_eject_button,
|
||
PLACES_SIDEBAR_COLUMN_NO_EJECT, !show_eject_button,
|
||
PLACES_SIDEBAR_COLUMN_BOOKMARK, place_type != PLACES_BOOKMARK,
|
||
PLACES_SIDEBAR_COLUMN_TOOLTIP, tooltip,
|
||
PLACES_SIDEBAR_COLUMN_SECTION_TYPE, section_type,
|
||
-1);
|
||
}
|
||
|
||
static GIcon *
|
||
special_directory_get_gicon (GUserDirectory directory)
|
||
{
|
||
#define ICON_CASE(x) \
|
||
case G_USER_DIRECTORY_ ## x: \
|
||
return g_themed_icon_new_with_default_fallbacks (ICON_NAME_FOLDER_ ## x);
|
||
|
||
switch (directory)
|
||
{
|
||
|
||
ICON_CASE (DESKTOP);
|
||
ICON_CASE (DOCUMENTS);
|
||
ICON_CASE (DOWNLOAD);
|
||
ICON_CASE (MUSIC);
|
||
ICON_CASE (PICTURES);
|
||
ICON_CASE (PUBLIC_SHARE);
|
||
ICON_CASE (TEMPLATES);
|
||
ICON_CASE (VIDEOS);
|
||
|
||
default:
|
||
return g_themed_icon_new_with_default_fallbacks (ICON_NAME_FOLDER);
|
||
}
|
||
|
||
#undef ICON_CASE
|
||
}
|
||
|
||
static gboolean
|
||
recent_files_setting_is_enabled (GtkPlacesSidebar *sidebar)
|
||
{
|
||
GtkSettings *settings;
|
||
gboolean enabled;
|
||
|
||
if (gtk_widget_has_screen (GTK_WIDGET (sidebar)))
|
||
settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (sidebar)));
|
||
else
|
||
settings = gtk_settings_get_default ();
|
||
|
||
g_object_get (settings, "gtk-recent-files-enabled", &enabled, NULL);
|
||
|
||
return enabled;
|
||
}
|
||
|
||
static gboolean
|
||
recent_scheme_is_supported (void)
|
||
{
|
||
const gchar * const *supported;
|
||
gint i;
|
||
|
||
supported = g_vfs_get_supported_uri_schemes (g_vfs_get_default ());
|
||
if (supported == NULL)
|
||
return FALSE;
|
||
|
||
for (i = 0; supported[i] != NULL; i++)
|
||
{
|
||
if (strcmp ("recent", supported[i]) == 0)
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static gboolean
|
||
should_show_recent (GtkPlacesSidebar *sidebar)
|
||
{
|
||
return recent_files_setting_is_enabled (sidebar) && recent_scheme_is_supported ();
|
||
}
|
||
|
||
static gboolean
|
||
path_is_home_dir (const gchar *path)
|
||
{
|
||
GFile *home_dir;
|
||
GFile *location;
|
||
const gchar *home_path;
|
||
gboolean res;
|
||
|
||
home_path = g_get_home_dir ();
|
||
if (!home_path)
|
||
return FALSE;
|
||
|
||
home_dir = g_file_new_for_path (home_path);
|
||
location = g_file_new_for_path (path);
|
||
res = g_file_equal (home_dir, location);
|
||
|
||
g_object_unref (home_dir);
|
||
g_object_unref (location);
|
||
|
||
return res;
|
||
}
|
||
|
||
static void
|
||
add_special_dirs (GtkPlacesSidebar *sidebar)
|
||
{
|
||
GList *dirs;
|
||
gint index;
|
||
|
||
dirs = NULL;
|
||
for (index = 0; index < G_USER_N_DIRECTORIES; index++)
|
||
{
|
||
const gchar *path;
|
||
GFile *root;
|
||
GIcon *icon;
|
||
gchar *name;
|
||
gchar *mount_uri;
|
||
gchar *tooltip;
|
||
|
||
if (!_gtk_bookmarks_manager_get_is_xdg_dir_builtin (index))
|
||
continue;
|
||
|
||
path = g_get_user_special_dir (index);
|
||
|
||
/* XDG resets special dirs to the home directory in case
|
||
* it's not finiding what it expects. We don't want the home
|
||
* to be added multiple times in that weird configuration.
|
||
*/
|
||
if (path == NULL ||
|
||
path_is_home_dir (path) ||
|
||
g_list_find_custom (dirs, path, (GCompareFunc) g_strcmp0) != NULL)
|
||
continue;
|
||
|
||
|
||
root = g_file_new_for_path (path);
|
||
|
||
name = _gtk_bookmarks_manager_get_bookmark_label (sidebar->bookmarks_manager, root);
|
||
if (!name)
|
||
name = g_file_get_basename (root);
|
||
|
||
icon = special_directory_get_gicon (index);
|
||
mount_uri = g_file_get_uri (root);
|
||
tooltip = g_file_get_parse_name (root);
|
||
|
||
add_place (sidebar, PLACES_XDG_DIR,
|
||
SECTION_COMPUTER,
|
||
name, icon, mount_uri,
|
||
NULL, NULL, NULL, 0,
|
||
tooltip);
|
||
g_free (name);
|
||
g_object_unref (root);
|
||
g_object_unref (icon);
|
||
g_free (mount_uri);
|
||
g_free (tooltip);
|
||
|
||
dirs = g_list_prepend (dirs, (gchar *)path);
|
||
}
|
||
|
||
g_list_free (dirs);
|
||
}
|
||
|
||
static gchar *
|
||
get_home_directory_uri (void)
|
||
{
|
||
const gchar *home;
|
||
|
||
home = g_get_home_dir ();
|
||
if (!home)
|
||
return NULL;
|
||
|
||
return g_strconcat ("file://", home, NULL);
|
||
}
|
||
|
||
static gchar *
|
||
get_desktop_directory_uri (void)
|
||
{
|
||
const gchar *name;
|
||
|
||
name = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
|
||
|
||
/* "To disable a directory, point it to the homedir."
|
||
* See http://freedesktop.org/wiki/Software/xdg-user-dirs
|
||
*/
|
||
if (path_is_home_dir (name))
|
||
return NULL;
|
||
|
||
return g_strconcat ("file://", name, NULL);
|
||
}
|
||
|
||
static gboolean
|
||
should_show_file (GtkPlacesSidebar *sidebar,
|
||
GFile *file)
|
||
{
|
||
gchar *path;
|
||
|
||
if (!sidebar->local_only)
|
||
return TRUE;
|
||
|
||
path = g_file_get_path (file);
|
||
if (path)
|
||
{
|
||
g_free (path);
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static gboolean
|
||
file_is_shown (GtkPlacesSidebar *sidebar,
|
||
GFile *file)
|
||
{
|
||
GtkTreeIter iter;
|
||
gchar *uri;
|
||
|
||
if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (sidebar->store), &iter))
|
||
return FALSE;
|
||
|
||
do
|
||
{
|
||
gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter,
|
||
PLACES_SIDEBAR_COLUMN_URI, &uri,
|
||
-1);
|
||
if (uri)
|
||
{
|
||
GFile *other;
|
||
gboolean found;
|
||
other = g_file_new_for_uri (uri);
|
||
g_free (uri);
|
||
found = g_file_equal (file, other);
|
||
g_object_unref (other);
|
||
if (found)
|
||
return TRUE;
|
||
}
|
||
}
|
||
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (sidebar->store), &iter));
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static void
|
||
add_application_shortcuts (GtkPlacesSidebar *sidebar)
|
||
{
|
||
GSList *l;
|
||
|
||
for (l = sidebar->shortcuts; l; l = l->next)
|
||
{
|
||
GFile *file;
|
||
GFileInfo *info;
|
||
|
||
file = G_FILE (l->data);
|
||
|
||
if (!should_show_file (sidebar, file))
|
||
continue;
|
||
|
||
if (file_is_shown (sidebar, file))
|
||
continue;
|
||
|
||
/* FIXME: we are getting file info synchronously. We may want to do it async at some point. */
|
||
info = g_file_query_info (file,
|
||
"standard::display-name,standard::symbolic-icon",
|
||
G_FILE_QUERY_INFO_NONE,
|
||
NULL,
|
||
NULL); /* NULL-GError */
|
||
|
||
if (info)
|
||
{
|
||
gchar *uri;
|
||
gchar *tooltip;
|
||
const gchar *name;
|
||
GIcon *icon;
|
||
|
||
name = g_file_info_get_display_name (info);
|
||
icon = g_file_info_get_symbolic_icon (info);
|
||
uri = g_file_get_uri (file);
|
||
tooltip = g_file_get_parse_name (file);
|
||
|
||
add_place (sidebar, PLACES_BUILT_IN,
|
||
SECTION_COMPUTER,
|
||
name, icon, uri,
|
||
NULL, NULL, NULL, 0,
|
||
tooltip);
|
||
|
||
g_free (uri);
|
||
g_free (tooltip);
|
||
|
||
g_object_unref (info);
|
||
}
|
||
}
|
||
}
|
||
|
||
static gboolean
|
||
get_selected_iter (GtkPlacesSidebar *sidebar,
|
||
GtkTreeIter *iter)
|
||
{
|
||
GtkTreeSelection *selection;
|
||
|
||
selection = gtk_tree_view_get_selection (sidebar->tree_view);
|
||
|
||
return gtk_tree_selection_get_selected (selection, NULL, iter);
|
||
}
|
||
|
||
static void
|
||
update_places (GtkPlacesSidebar *sidebar)
|
||
{
|
||
GtkTreeIter iter;
|
||
GVolumeMonitor *volume_monitor;
|
||
GList *mounts, *l, *ll;
|
||
GMount *mount;
|
||
GList *drives;
|
||
GDrive *drive;
|
||
GList *volumes;
|
||
GVolume *volume;
|
||
GSList *bookmarks, *sl;
|
||
gint index;
|
||
gchar *original_uri, *mount_uri, *name, *identifier;
|
||
gchar *home_uri;
|
||
gchar *bookmark_name;
|
||
GIcon *icon;
|
||
GFile *root;
|
||
gchar *tooltip;
|
||
GList *network_mounts, *network_volumes;
|
||
|
||
/* save original selection */
|
||
if (get_selected_iter (sidebar, &iter))
|
||
gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store),
|
||
&iter,
|
||
PLACES_SIDEBAR_COLUMN_URI, &original_uri, -1);
|
||
else
|
||
original_uri = NULL;
|
||
|
||
gtk_list_store_clear (sidebar->store);
|
||
|
||
sidebar->devices_header_added = FALSE;
|
||
sidebar->bookmarks_header_added = FALSE;
|
||
|
||
network_mounts = network_volumes = NULL;
|
||
volume_monitor = sidebar->volume_monitor;
|
||
|
||
/* add built-in bookmarks */
|
||
|
||
if (should_show_recent (sidebar))
|
||
{
|
||
mount_uri = "recent:///";
|
||
icon = g_themed_icon_new_with_default_fallbacks ("document-open-recent-symbolic");
|
||
add_place (sidebar, PLACES_BUILT_IN,
|
||
SECTION_COMPUTER,
|
||
_("Recent"), icon, mount_uri,
|
||
NULL, NULL, NULL, 0,
|
||
_("Recent files"));
|
||
g_object_unref (icon);
|
||
}
|
||
|
||
/* home folder */
|
||
home_uri = get_home_directory_uri ();
|
||
icon = g_themed_icon_new_with_default_fallbacks (ICON_NAME_HOME);
|
||
add_place (sidebar, PLACES_BUILT_IN,
|
||
SECTION_COMPUTER,
|
||
_("Home"), icon, home_uri,
|
||
NULL, NULL, NULL, 0,
|
||
_("Open your personal folder"));
|
||
g_object_unref (icon);
|
||
g_free (home_uri);
|
||
|
||
/* desktop */
|
||
if (sidebar->show_desktop)
|
||
{
|
||
mount_uri = get_desktop_directory_uri ();
|
||
if (mount_uri)
|
||
{
|
||
icon = g_themed_icon_new_with_default_fallbacks (ICON_NAME_DESKTOP);
|
||
add_place (sidebar, PLACES_BUILT_IN,
|
||
SECTION_COMPUTER,
|
||
_("Desktop"), icon, mount_uri,
|
||
NULL, NULL, NULL, 0,
|
||
_("Open the contents of your desktop in a folder"));
|
||
g_object_unref (icon);
|
||
g_free (mount_uri);
|
||
}
|
||
}
|
||
|
||
/* XDG directories */
|
||
add_special_dirs (sidebar);
|
||
|
||
if (sidebar->show_enter_location)
|
||
{
|
||
icon = g_themed_icon_new_with_default_fallbacks (ICON_NAME_NETWORK_SERVER);
|
||
add_place (sidebar, PLACES_ENTER_LOCATION,
|
||
SECTION_COMPUTER,
|
||
_("Enter Location"), icon, NULL,
|
||
NULL, NULL, NULL, 0,
|
||
_("Manually enter a location"));
|
||
g_object_unref (icon);
|
||
}
|
||
|
||
/* Trash */
|
||
if (!sidebar->local_only)
|
||
{
|
||
mount_uri = "trash:///"; /* No need to strdup */
|
||
icon = _gtk_trash_monitor_get_icon (sidebar->trash_monitor);
|
||
add_place (sidebar, PLACES_BUILT_IN,
|
||
SECTION_COMPUTER,
|
||
_("Trash"), icon, mount_uri,
|
||
NULL, NULL, NULL, 0,
|
||
_("Open the trash"));
|
||
g_object_unref (icon);
|
||
}
|
||
|
||
/* Application-side shortcuts */
|
||
add_application_shortcuts (sidebar);
|
||
|
||
/* go through all connected drives */
|
||
drives = g_volume_monitor_get_connected_drives (volume_monitor);
|
||
|
||
for (l = drives; l != NULL; l = l->next)
|
||
{
|
||
drive = l->data;
|
||
|
||
volumes = g_drive_get_volumes (drive);
|
||
if (volumes != NULL)
|
||
{
|
||
for (ll = volumes; ll != NULL; ll = ll->next)
|
||
{
|
||
volume = ll->data;
|
||
identifier = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_CLASS);
|
||
|
||
if (g_strcmp0 (identifier, "network") == 0)
|
||
{
|
||
g_free (identifier);
|
||
network_volumes = g_list_prepend (network_volumes, volume);
|
||
continue;
|
||
}
|
||
g_free (identifier);
|
||
|
||
mount = g_volume_get_mount (volume);
|
||
if (mount != NULL)
|
||
{
|
||
/* Show mounted volume in the sidebar */
|
||
icon = g_mount_get_symbolic_icon (mount);
|
||
root = g_mount_get_default_location (mount);
|
||
mount_uri = g_file_get_uri (root);
|
||
name = g_mount_get_name (mount);
|
||
tooltip = g_file_get_parse_name (root);
|
||
|
||
add_place (sidebar, PLACES_MOUNTED_VOLUME,
|
||
SECTION_DEVICES,
|
||
name, icon, mount_uri,
|
||
drive, volume, mount, 0, tooltip);
|
||
g_object_unref (root);
|
||
g_object_unref (mount);
|
||
g_object_unref (icon);
|
||
g_free (tooltip);
|
||
g_free (name);
|
||
g_free (mount_uri);
|
||
}
|
||
else
|
||
{
|
||
/* Do show the unmounted volumes in the sidebar;
|
||
* this is so the user can mount it (in case automounting
|
||
* is off).
|
||
*
|
||
* Also, even if automounting is enabled, this gives a visual
|
||
* cue that the user should remember to yank out the media if
|
||
* he just unmounted it.
|
||
*/
|
||
icon = g_volume_get_symbolic_icon (volume);
|
||
name = g_volume_get_name (volume);
|
||
tooltip = g_strdup_printf (_("Mount and open %s"), name);
|
||
|
||
add_place (sidebar, PLACES_MOUNTED_VOLUME,
|
||
SECTION_DEVICES,
|
||
name, icon, NULL,
|
||
drive, volume, NULL, 0, tooltip);
|
||
g_object_unref (icon);
|
||
g_free (name);
|
||
g_free (tooltip);
|
||
}
|
||
g_object_unref (volume);
|
||
}
|
||
g_list_free (volumes);
|
||
}
|
||
else
|
||
{
|
||
if (g_drive_is_media_removable (drive) && !g_drive_is_media_check_automatic (drive))
|
||
{
|
||
/* If the drive has no mountable volumes and we cannot detect media change.. we
|
||
* display the drive in the sidebar so the user can manually poll the drive by
|
||
* right clicking and selecting "Rescan..."
|
||
*
|
||
* This is mainly for drives like floppies where media detection doesn't
|
||
* work.. but it's also for human beings who like to turn off media detection
|
||
* in the OS to save battery juice.
|
||
*/
|
||
icon = g_drive_get_symbolic_icon (drive);
|
||
name = g_drive_get_name (drive);
|
||
tooltip = g_strdup_printf (_("Mount and open %s"), name);
|
||
|
||
add_place (sidebar, PLACES_BUILT_IN,
|
||
SECTION_DEVICES,
|
||
name, icon, NULL,
|
||
drive, NULL, NULL, 0, tooltip);
|
||
g_object_unref (icon);
|
||
g_free (tooltip);
|
||
g_free (name);
|
||
}
|
||
}
|
||
g_object_unref (drive);
|
||
}
|
||
g_list_free (drives);
|
||
|
||
/* add all volumes that is not associated with a drive */
|
||
volumes = g_volume_monitor_get_volumes (volume_monitor);
|
||
for (l = volumes; l != NULL; l = l->next)
|
||
{
|
||
volume = l->data;
|
||
drive = g_volume_get_drive (volume);
|
||
if (drive != NULL)
|
||
{
|
||
g_object_unref (volume);
|
||
g_object_unref (drive);
|
||
continue;
|
||
}
|
||
|
||
identifier = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_CLASS);
|
||
|
||
if (g_strcmp0 (identifier, "network") == 0)
|
||
{
|
||
g_free (identifier);
|
||
network_volumes = g_list_prepend (network_volumes, volume);
|
||
continue;
|
||
}
|
||
g_free (identifier);
|
||
|
||
mount = g_volume_get_mount (volume);
|
||
if (mount != NULL)
|
||
{
|
||
icon = g_mount_get_symbolic_icon (mount);
|
||
root = g_mount_get_default_location (mount);
|
||
mount_uri = g_file_get_uri (root);
|
||
tooltip = g_file_get_parse_name (root);
|
||
name = g_mount_get_name (mount);
|
||
add_place (sidebar, PLACES_MOUNTED_VOLUME,
|
||
SECTION_DEVICES,
|
||
name, icon, mount_uri,
|
||
NULL, volume, mount, 0, tooltip);
|
||
g_object_unref (mount);
|
||
g_object_unref (root);
|
||
g_object_unref (icon);
|
||
g_free (name);
|
||
g_free (tooltip);
|
||
g_free (mount_uri);
|
||
}
|
||
else
|
||
{
|
||
/* see comment above in why we add an icon for an unmounted mountable volume */
|
||
icon = g_volume_get_symbolic_icon (volume);
|
||
name = g_volume_get_name (volume);
|
||
add_place (sidebar, PLACES_MOUNTED_VOLUME,
|
||
SECTION_DEVICES,
|
||
name, icon, NULL,
|
||
NULL, volume, NULL, 0, name);
|
||
g_object_unref (icon);
|
||
g_free (name);
|
||
}
|
||
g_object_unref (volume);
|
||
}
|
||
g_list_free (volumes);
|
||
|
||
/* file system root */
|
||
|
||
mount_uri = "file:///"; /* No need to strdup */
|
||
icon = g_themed_icon_new_with_default_fallbacks (ICON_NAME_FILESYSTEM);
|
||
add_place (sidebar, PLACES_BUILT_IN,
|
||
SECTION_DEVICES,
|
||
sidebar->hostname, icon, mount_uri,
|
||
NULL, NULL, NULL, 0,
|
||
_("Open the contents of the file system"));
|
||
g_object_unref (icon);
|
||
|
||
/* add mounts that has no volume (/etc/mtab mounts, ftp, sftp,...) */
|
||
mounts = g_volume_monitor_get_mounts (volume_monitor);
|
||
|
||
for (l = mounts; l != NULL; l = l->next)
|
||
{
|
||
mount = l->data;
|
||
if (g_mount_is_shadowed (mount))
|
||
{
|
||
g_object_unref (mount);
|
||
continue;
|
||
}
|
||
volume = g_mount_get_volume (mount);
|
||
if (volume != NULL)
|
||
{
|
||
g_object_unref (volume);
|
||
g_object_unref (mount);
|
||
continue;
|
||
}
|
||
root = g_mount_get_default_location (mount);
|
||
|
||
if (!g_file_is_native (root))
|
||
{
|
||
network_mounts = g_list_prepend (network_mounts, mount);
|
||
g_object_unref (root);
|
||
continue;
|
||
}
|
||
|
||
icon = g_mount_get_symbolic_icon (mount);
|
||
mount_uri = g_file_get_uri (root);
|
||
name = g_mount_get_name (mount);
|
||
tooltip = g_file_get_parse_name (root);
|
||
add_place (sidebar, PLACES_MOUNTED_VOLUME,
|
||
SECTION_COMPUTER,
|
||
name, icon, mount_uri,
|
||
NULL, NULL, mount, 0, tooltip);
|
||
g_object_unref (root);
|
||
g_object_unref (mount);
|
||
g_object_unref (icon);
|
||
g_free (name);
|
||
g_free (mount_uri);
|
||
g_free (tooltip);
|
||
}
|
||
g_list_free (mounts);
|
||
|
||
/* add bookmarks */
|
||
|
||
bookmarks = _gtk_bookmarks_manager_list_bookmarks (sidebar->bookmarks_manager);
|
||
|
||
for (sl = bookmarks, index = 0; sl; sl = sl->next, index++)
|
||
{
|
||
GFileInfo *info;
|
||
gboolean is_native;
|
||
|
||
root = sl->data;
|
||
is_native = g_file_is_native (root);
|
||
|
||
#if 0
|
||
/* FIXME: remove this? If we *do* show bookmarks for nonexistent files, the user will eventually clean them up */
|
||
if (!nautilus_bookmark_get_exists (bookmark))
|
||
continue;
|
||
#endif
|
||
|
||
if (_gtk_bookmarks_manager_get_is_builtin (sidebar->bookmarks_manager, root))
|
||
continue;
|
||
|
||
if (sidebar->local_only && !is_native)
|
||
continue;
|
||
|
||
/* FIXME: we are getting file info synchronously. We may want to do it async at some point. */
|
||
info = g_file_query_info (root,
|
||
"standard::display-name,standard::symbolic-icon",
|
||
G_FILE_QUERY_INFO_NONE,
|
||
NULL,
|
||
NULL); /* NULL-GError */
|
||
|
||
bookmark_name = _gtk_bookmarks_manager_get_bookmark_label (sidebar->bookmarks_manager, root);
|
||
if (bookmark_name == NULL && info != NULL)
|
||
bookmark_name = g_strdup (g_file_info_get_display_name (info));
|
||
else if (bookmark_name == NULL)
|
||
{
|
||
/* Don't add non-UTF-8 bookmarks */
|
||
bookmark_name = g_file_get_basename (root);
|
||
if (!g_utf8_validate (bookmark_name, -1, NULL))
|
||
{
|
||
g_free (bookmark_name);
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if (info)
|
||
icon = g_object_ref (g_file_info_get_symbolic_icon (info));
|
||
else
|
||
icon = g_themed_icon_new_with_default_fallbacks (is_native ? ICON_NAME_FOLDER : ICON_NAME_FOLDER_NETWORK);
|
||
|
||
mount_uri = g_file_get_uri (root);
|
||
tooltip = g_file_get_parse_name (root);
|
||
|
||
add_place (sidebar, PLACES_BOOKMARK,
|
||
SECTION_BOOKMARKS,
|
||
bookmark_name, icon, mount_uri,
|
||
NULL, NULL, NULL, index,
|
||
tooltip);
|
||
|
||
g_free (mount_uri);
|
||
g_free (tooltip);
|
||
g_free (bookmark_name);
|
||
|
||
if (info)
|
||
g_object_unref (info);
|
||
}
|
||
|
||
g_slist_foreach (bookmarks, (GFunc) g_object_unref, NULL);
|
||
g_slist_free (bookmarks);
|
||
|
||
/* network */
|
||
if (!sidebar->local_only)
|
||
{
|
||
add_heading (sidebar, SECTION_NETWORK, _("Network"));
|
||
|
||
mount_uri = "network:///";
|
||
icon = g_themed_icon_new_with_default_fallbacks (ICON_NAME_NETWORK);
|
||
add_place (sidebar, PLACES_BUILT_IN,
|
||
SECTION_NETWORK,
|
||
_("Browse Network"), icon, mount_uri,
|
||
NULL, NULL, NULL, 0,
|
||
_("Browse the contents of the network"));
|
||
g_object_unref (icon);
|
||
|
||
if (sidebar->show_connect_to_server)
|
||
{
|
||
icon = g_themed_icon_new_with_default_fallbacks (ICON_NAME_NETWORK_SERVER);
|
||
add_place (sidebar, PLACES_CONNECT_TO_SERVER,
|
||
SECTION_NETWORK,
|
||
_("Connect to Server"), icon, NULL,
|
||
NULL, NULL, NULL, 0,
|
||
_("Connect to a network server address"));
|
||
g_object_unref (icon);
|
||
}
|
||
|
||
network_volumes = g_list_reverse (network_volumes);
|
||
for (l = network_volumes; l != NULL; l = l->next)
|
||
{
|
||
volume = l->data;
|
||
mount = g_volume_get_mount (volume);
|
||
|
||
if (mount != NULL)
|
||
{
|
||
network_mounts = g_list_prepend (network_mounts, mount);
|
||
continue;
|
||
}
|
||
else
|
||
{
|
||
icon = g_volume_get_symbolic_icon (volume);
|
||
name = g_volume_get_name (volume);
|
||
tooltip = g_strdup_printf (_("Mount and open %s"), name);
|
||
|
||
add_place (sidebar, PLACES_MOUNTED_VOLUME,
|
||
SECTION_NETWORK,
|
||
name, icon, NULL,
|
||
NULL, volume, NULL, 0, tooltip);
|
||
g_object_unref (icon);
|
||
g_free (name);
|
||
g_free (tooltip);
|
||
}
|
||
}
|
||
|
||
network_mounts = g_list_reverse (network_mounts);
|
||
for (l = network_mounts; l != NULL; l = l->next)
|
||
{
|
||
mount = l->data;
|
||
root = g_mount_get_default_location (mount);
|
||
icon = g_mount_get_symbolic_icon (mount);
|
||
mount_uri = g_file_get_uri (root);
|
||
name = g_mount_get_name (mount);
|
||
tooltip = g_file_get_parse_name (root);
|
||
add_place (sidebar, PLACES_MOUNTED_VOLUME,
|
||
SECTION_NETWORK,
|
||
name, icon, mount_uri,
|
||
NULL, NULL, mount, 0, tooltip);
|
||
g_object_unref (root);
|
||
g_object_unref (icon);
|
||
g_free (name);
|
||
g_free (mount_uri);
|
||
g_free (tooltip);
|
||
}
|
||
}
|
||
|
||
g_list_free_full (network_volumes, g_object_unref);
|
||
g_list_free_full (network_mounts, g_object_unref);
|
||
|
||
/* restore original selection */
|
||
if (original_uri)
|
||
{
|
||
GFile *restore;
|
||
|
||
restore = g_file_new_for_uri (original_uri);
|
||
gtk_places_sidebar_set_location (sidebar, restore);
|
||
g_object_unref (restore);
|
||
|
||
g_free (original_uri);
|
||
}
|
||
}
|
||
|
||
static gboolean
|
||
over_eject_button (GtkPlacesSidebar *sidebar,
|
||
gint x,
|
||
gint y,
|
||
GtkTreePath **path)
|
||
{
|
||
GtkTreeViewColumn *column;
|
||
gint width, x_offset, hseparator;
|
||
gint eject_button_size;
|
||
gboolean show_eject;
|
||
GtkTreeIter iter;
|
||
GtkTreeModel *model;
|
||
|
||
*path = NULL;
|
||
model = gtk_tree_view_get_model (sidebar->tree_view);
|
||
|
||
if (gtk_tree_view_get_path_at_pos (sidebar->tree_view,
|
||
x, y, path, &column, NULL, NULL))
|
||
{
|
||
gtk_tree_model_get_iter (model, &iter, *path);
|
||
gtk_tree_model_get (model, &iter,
|
||
PLACES_SIDEBAR_COLUMN_EJECT, &show_eject,
|
||
-1);
|
||
|
||
if (!show_eject)
|
||
goto out;
|
||
|
||
gtk_widget_style_get (GTK_WIDGET (sidebar->tree_view),
|
||
"horizontal-separator", &hseparator,
|
||
NULL);
|
||
|
||
/* Reload cell attributes for this particular row */
|
||
gtk_tree_view_column_cell_set_cell_data (column,
|
||
model, &iter, FALSE, FALSE);
|
||
|
||
gtk_tree_view_column_cell_get_position (column,
|
||
sidebar->eject_icon_cell_renderer,
|
||
&x_offset, &width);
|
||
|
||
eject_button_size = get_icon_size (sidebar);
|
||
|
||
/* This is kinda weird, but we have to do it to workaround expanding
|
||
* the eject cell renderer (even thought we told it not to) and we
|
||
* then had to set it right-aligned
|
||
*/
|
||
x_offset += width - hseparator - EJECT_BUTTON_XPAD - eject_button_size;
|
||
|
||
if (x - x_offset >= 0 && x - x_offset <= eject_button_size)
|
||
return TRUE;
|
||
}
|
||
|
||
out:
|
||
g_clear_pointer (path, gtk_tree_path_free);
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static gboolean
|
||
clicked_eject_button (GtkPlacesSidebar *sidebar,
|
||
GtkTreePath **path)
|
||
{
|
||
GdkEvent *event = gtk_get_current_event ();
|
||
GdkEventButton *button_event = (GdkEventButton *) event;
|
||
|
||
if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
|
||
over_eject_button (sidebar, button_event->x, button_event->y, path))
|
||
return TRUE;
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static gboolean
|
||
pos_is_into_or_before (GtkTreeViewDropPosition pos)
|
||
{
|
||
return (pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE);
|
||
}
|
||
|
||
/* Computes the appropriate row and position for dropping */
|
||
static gboolean
|
||
compute_drop_position (GtkTreeView *tree_view,
|
||
gint x,
|
||
gint y,
|
||
GtkTreePath **path,
|
||
GtkTreeViewDropPosition *pos,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
GtkTreeModel *model;
|
||
GtkTreeIter iter;
|
||
PlaceType place_type;
|
||
SectionType section_type;
|
||
gboolean drop_possible;
|
||
|
||
if (!gtk_tree_view_get_dest_row_at_pos (tree_view, x, y, path, pos))
|
||
return FALSE;
|
||
|
||
model = gtk_tree_view_get_model (tree_view);
|
||
|
||
gtk_tree_model_get_iter (model, &iter, *path);
|
||
gtk_tree_model_get (model, &iter,
|
||
PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type,
|
||
PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type,
|
||
-1);
|
||
|
||
drop_possible = TRUE;
|
||
|
||
/* Normalize drops on the feedback row */
|
||
if (place_type == PLACES_DROP_FEEDBACK)
|
||
{
|
||
*pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE;
|
||
goto out;
|
||
}
|
||
|
||
/* Never drop on headings, but special case the bookmarks heading,
|
||
* so we can drop bookmarks in between it and the first bookmark.
|
||
*/
|
||
if (place_type == PLACES_HEADING && section_type != SECTION_BOOKMARKS)
|
||
drop_possible = FALSE;
|
||
|
||
/* Dragging a bookmark? */
|
||
if (sidebar->drag_data_received &&
|
||
sidebar->drag_data_info == GTK_TREE_MODEL_ROW)
|
||
{
|
||
/* Don't allow reordering bookmarks into non-bookmark areas */
|
||
if (section_type != SECTION_BOOKMARKS)
|
||
drop_possible = FALSE;
|
||
|
||
/* Bookmarks can only be reordered. Disallow dropping directly
|
||
* into them; only allow dropping between them.
|
||
*/
|
||
if (place_type == PLACES_HEADING)
|
||
{
|
||
if (pos_is_into_or_before (*pos))
|
||
drop_possible = FALSE;
|
||
else
|
||
*pos = GTK_TREE_VIEW_DROP_AFTER;
|
||
}
|
||
else
|
||
{
|
||
if (pos_is_into_or_before (*pos))
|
||
*pos = GTK_TREE_VIEW_DROP_BEFORE;
|
||
else
|
||
*pos = GTK_TREE_VIEW_DROP_AFTER;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* Dragging a file */
|
||
|
||
/* Outside the bookmarks section, URIs can only be dropped
|
||
* directly into places items. Inside the bookmarks section,
|
||
* they can be dropped between items (to create new bookmarks)
|
||
* or in items themselves (to request a move/copy file
|
||
* operation).
|
||
*/
|
||
if (section_type != SECTION_BOOKMARKS)
|
||
*pos = GTK_TREE_VIEW_DROP_INTO_OR_BEFORE;
|
||
else
|
||
{
|
||
if (place_type == PLACES_HEADING)
|
||
{
|
||
if (pos_is_into_or_before (*pos))
|
||
drop_possible = FALSE;
|
||
else
|
||
*pos = GTK_TREE_VIEW_DROP_AFTER;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Disallow drops on recent:/// */
|
||
if (place_type == PLACES_BUILT_IN)
|
||
{
|
||
gchar *uri;
|
||
|
||
gtk_tree_model_get (model, &iter,
|
||
PLACES_SIDEBAR_COLUMN_URI, &uri,
|
||
-1);
|
||
|
||
if (g_strcmp0 (uri, "recent:///") == 0)
|
||
drop_possible = FALSE;
|
||
|
||
g_free (uri);
|
||
}
|
||
|
||
out:
|
||
|
||
if (!drop_possible)
|
||
{
|
||
gtk_tree_path_free (*path);
|
||
*path = NULL;
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static gboolean
|
||
get_drag_data (GtkTreeView *tree_view,
|
||
GdkDragContext *context,
|
||
guint time)
|
||
{
|
||
GdkAtom target;
|
||
|
||
target = gtk_drag_dest_find_target (GTK_WIDGET (tree_view),
|
||
context,
|
||
NULL);
|
||
|
||
if (target == GDK_NONE)
|
||
return FALSE;
|
||
|
||
gtk_drag_get_data (GTK_WIDGET (tree_view),
|
||
context, target, time);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static void
|
||
remove_switch_location_timer (GtkPlacesSidebar *sidebar)
|
||
{
|
||
if (sidebar->switch_location_timer != 0)
|
||
{
|
||
g_source_remove (sidebar->switch_location_timer);
|
||
sidebar->switch_location_timer = 0;
|
||
}
|
||
}
|
||
|
||
static void
|
||
free_drag_data (GtkPlacesSidebar *sidebar)
|
||
{
|
||
sidebar->drag_data_received = FALSE;
|
||
|
||
if (sidebar->drag_list)
|
||
{
|
||
g_list_free_full (sidebar->drag_list, g_object_unref);
|
||
sidebar->drag_list = NULL;
|
||
}
|
||
|
||
remove_switch_location_timer (sidebar);
|
||
|
||
g_free (sidebar->drop_target_uri);
|
||
sidebar->drop_target_uri = NULL;
|
||
}
|
||
|
||
static gboolean
|
||
switch_location_timer (gpointer user_data)
|
||
{
|
||
GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (user_data);
|
||
GFile *location;
|
||
|
||
sidebar->switch_location_timer = 0;
|
||
|
||
location = g_file_new_for_uri (sidebar->drop_target_uri);
|
||
emit_open_location (sidebar, location, 0);
|
||
g_object_unref (location);
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static void
|
||
check_switch_location_timer (GtkPlacesSidebar *sidebar,
|
||
const gchar *uri)
|
||
{
|
||
if (g_strcmp0 (uri, sidebar->drop_target_uri) == 0)
|
||
return;
|
||
|
||
remove_switch_location_timer (sidebar);
|
||
|
||
g_free (sidebar->drop_target_uri);
|
||
sidebar->drop_target_uri = NULL;
|
||
|
||
if (uri != NULL)
|
||
{
|
||
sidebar->drop_target_uri = g_strdup (uri);
|
||
sidebar->switch_location_timer = gdk_threads_add_timeout (TIMEOUT_EXPAND, switch_location_timer, sidebar);
|
||
g_source_set_name_by_id (sidebar->switch_location_timer, "[gtk+] switch_location_timer");
|
||
}
|
||
}
|
||
|
||
static void
|
||
remove_drop_bookmark_feedback_row (GtkPlacesSidebar *sidebar)
|
||
{
|
||
if (sidebar->drop_state != DROP_STATE_NORMAL)
|
||
{
|
||
gboolean success;
|
||
GtkTreeIter iter;
|
||
|
||
success = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (sidebar->store), &iter, NULL, sidebar->new_bookmark_index);
|
||
g_assert (success);
|
||
gtk_list_store_remove (sidebar->store, &iter);
|
||
|
||
sidebar->drop_state = DROP_STATE_NORMAL;
|
||
}
|
||
}
|
||
|
||
static void
|
||
start_drop_feedback (GtkPlacesSidebar *sidebar,
|
||
GtkTreePath *path,
|
||
GtkTreeViewDropPosition pos,
|
||
gboolean drop_as_bookmarks)
|
||
{
|
||
if (drop_as_bookmarks)
|
||
{
|
||
gint new_bookmark_index;
|
||
GtkTreePath *new_path;
|
||
gboolean need_feedback_row;
|
||
|
||
new_bookmark_index = gtk_tree_path_get_indices (path)[0];
|
||
if (pos == GTK_TREE_VIEW_DROP_AFTER)
|
||
new_bookmark_index++;
|
||
|
||
if (sidebar->drop_state == DROP_STATE_NORMAL)
|
||
need_feedback_row = TRUE;
|
||
else
|
||
{
|
||
/* Feedback row already exists; remove it if its position needs to change */
|
||
if (sidebar->new_bookmark_index == new_bookmark_index)
|
||
need_feedback_row = FALSE;
|
||
else
|
||
{
|
||
if (sidebar->new_bookmark_index < new_bookmark_index)
|
||
new_bookmark_index--; /* since the removal of the old feedback row pushed items one position up */
|
||
|
||
remove_drop_bookmark_feedback_row (sidebar);
|
||
need_feedback_row = TRUE;
|
||
}
|
||
}
|
||
|
||
if (need_feedback_row)
|
||
{
|
||
GtkTreeIter iter;
|
||
|
||
sidebar->new_bookmark_index = new_bookmark_index;
|
||
gtk_list_store_insert_with_values (sidebar->store, &iter, sidebar->new_bookmark_index,
|
||
PLACES_SIDEBAR_COLUMN_ROW_TYPE, PLACES_DROP_FEEDBACK,
|
||
PLACES_SIDEBAR_COLUMN_SECTION_TYPE, SECTION_BOOKMARKS,
|
||
PLACES_SIDEBAR_COLUMN_NAME, _("New bookmark"),
|
||
PLACES_SIDEBAR_COLUMN_NO_EJECT, TRUE,
|
||
-1);
|
||
}
|
||
|
||
new_path = gtk_tree_path_new_from_indices (sidebar->new_bookmark_index, -1);
|
||
gtk_tree_view_set_drag_dest_row (sidebar->tree_view, new_path, GTK_TREE_VIEW_DROP_INTO_OR_BEFORE);
|
||
gtk_tree_path_free (new_path);
|
||
|
||
sidebar->drop_state = DROP_STATE_NEW_BOOKMARK_ARMED;
|
||
}
|
||
else
|
||
gtk_tree_view_set_drag_dest_row (sidebar->tree_view, path, pos);
|
||
}
|
||
|
||
static void
|
||
stop_drop_feedback (GtkPlacesSidebar *sidebar)
|
||
{
|
||
gtk_tree_view_set_drag_dest_row (sidebar->tree_view, NULL, 0);
|
||
}
|
||
|
||
static gboolean
|
||
drag_motion_callback (GtkTreeView *tree_view,
|
||
GdkDragContext *context,
|
||
gint x,
|
||
gint y,
|
||
guint time,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
GtkTreePath *path;
|
||
GtkTreeViewDropPosition pos;
|
||
gint action;
|
||
GtkTreeIter iter;
|
||
gboolean res;
|
||
gboolean drop_as_bookmarks;
|
||
gchar *drop_target_uri = NULL;
|
||
|
||
action = 0;
|
||
drop_as_bookmarks = FALSE;
|
||
path = NULL;
|
||
|
||
if (!sidebar->drag_data_received)
|
||
{
|
||
if (!get_drag_data (tree_view, context, time))
|
||
goto out;
|
||
}
|
||
|
||
res = compute_drop_position (tree_view, x, y, &path, &pos, sidebar);
|
||
if (!res)
|
||
goto out;
|
||
|
||
if (sidebar->drag_data_received &&
|
||
sidebar->drag_data_info == GTK_TREE_MODEL_ROW)
|
||
{
|
||
/* Dragging bookmarks always moves them to another position in the bookmarks list */
|
||
action = GDK_ACTION_MOVE;
|
||
}
|
||
else
|
||
{
|
||
/* URIs are being dragged. See if the caller wants to handle a
|
||
* file move/copy operation itself, or if we should only try to
|
||
* create bookmarks out of the dragged URIs.
|
||
*/
|
||
if (sidebar->drag_list != NULL)
|
||
{
|
||
SectionType section_type;
|
||
PlaceType place_type;
|
||
|
||
gtk_tree_model_get_iter (GTK_TREE_MODEL (sidebar->store), &iter, path);
|
||
gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store),
|
||
&iter,
|
||
PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type,
|
||
PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type,
|
||
-1);
|
||
|
||
if (place_type == PLACES_DROP_FEEDBACK ||
|
||
(section_type == SECTION_BOOKMARKS &&
|
||
(pos == GTK_TREE_VIEW_DROP_BEFORE || pos == GTK_TREE_VIEW_DROP_AFTER)))
|
||
{
|
||
action = GDK_ACTION_COPY;
|
||
drop_as_bookmarks = TRUE;
|
||
}
|
||
|
||
if (!drop_as_bookmarks)
|
||
{
|
||
gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store),
|
||
&iter,
|
||
PLACES_SIDEBAR_COLUMN_URI, &drop_target_uri,
|
||
-1);
|
||
|
||
if (drop_target_uri != NULL)
|
||
{
|
||
GFile *dest_file = g_file_new_for_uri (drop_target_uri);
|
||
|
||
action = emit_drag_action_requested (sidebar, context, dest_file, sidebar->drag_list);
|
||
|
||
g_object_unref (dest_file);
|
||
} /* uri may be NULL for unmounted volumes, for example, so we don't allow drops there */
|
||
}
|
||
}
|
||
}
|
||
|
||
out:
|
||
if (action != 0)
|
||
{
|
||
check_switch_location_timer (sidebar, drop_target_uri);
|
||
start_drop_feedback (sidebar, path, pos, drop_as_bookmarks);
|
||
}
|
||
else
|
||
{
|
||
remove_switch_location_timer (sidebar);
|
||
stop_drop_feedback (sidebar);
|
||
}
|
||
|
||
g_free (drop_target_uri);
|
||
|
||
if (path != NULL)
|
||
gtk_tree_path_free (path);
|
||
|
||
g_signal_stop_emission_by_name (tree_view, "drag-motion");
|
||
|
||
gdk_drag_status (context, action, time);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static gboolean
|
||
drag_leave_timeout_cb (gpointer data)
|
||
{
|
||
GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (data);
|
||
|
||
free_drag_data (sidebar);
|
||
stop_drop_feedback (sidebar);
|
||
remove_drop_bookmark_feedback_row (sidebar);
|
||
|
||
sidebar->drag_leave_timeout_id = 0;
|
||
return FALSE;
|
||
}
|
||
|
||
static void
|
||
drag_leave_callback (GtkTreeView *tree_view,
|
||
GdkDragContext *context,
|
||
guint time,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
if (sidebar->drag_leave_timeout_id)
|
||
g_source_remove (sidebar->drag_leave_timeout_id);
|
||
|
||
sidebar->drag_leave_timeout_id = gdk_threads_add_timeout (500, drag_leave_timeout_cb, sidebar);
|
||
g_source_set_name_by_id (sidebar->drag_leave_timeout_id, "[gtk+] drag_leave_timeout_cb");
|
||
|
||
remove_switch_location_timer (sidebar);
|
||
|
||
g_signal_stop_emission_by_name (tree_view, "drag-leave");
|
||
}
|
||
|
||
/* Takes an array of URIs and turns it into a list of GFile */
|
||
static GList *
|
||
build_file_list_from_uris (const gchar **uris)
|
||
{
|
||
GList *result;
|
||
gint i;
|
||
|
||
result = NULL;
|
||
for (i = 0; uris[i]; i++)
|
||
{
|
||
GFile *file;
|
||
|
||
file = g_file_new_for_uri (uris[i]);
|
||
result = g_list_prepend (result, file);
|
||
}
|
||
|
||
return g_list_reverse (result);
|
||
}
|
||
|
||
/* Reorders the selected bookmark to the specified position */
|
||
static void
|
||
reorder_bookmarks (GtkPlacesSidebar *sidebar,
|
||
gint new_position)
|
||
{
|
||
GtkTreeIter iter;
|
||
gchar *uri;
|
||
GFile *file;
|
||
|
||
if (!get_selected_iter (sidebar, &iter))
|
||
return;
|
||
|
||
gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter,
|
||
PLACES_SIDEBAR_COLUMN_URI, &uri,
|
||
-1);
|
||
|
||
file = g_file_new_for_uri (uri);
|
||
_gtk_bookmarks_manager_reorder_bookmark (sidebar->bookmarks_manager, file, new_position, NULL); /* NULL-GError */
|
||
|
||
g_object_unref (file);
|
||
g_free (uri);
|
||
}
|
||
|
||
/* Creates bookmarks for the specified files at the given position in the bookmarks list */
|
||
static void
|
||
drop_files_as_bookmarks (GtkPlacesSidebar *sidebar,
|
||
GList *files,
|
||
gint position)
|
||
{
|
||
GList *l;
|
||
|
||
for (l = files; l; l = l->next)
|
||
{
|
||
GFile *f = G_FILE (l->data);
|
||
GFileInfo *info = g_file_query_info (f,
|
||
G_FILE_ATTRIBUTE_STANDARD_TYPE,
|
||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||
NULL,
|
||
NULL);
|
||
|
||
if (info)
|
||
{
|
||
if (_gtk_file_info_consider_as_directory (info))
|
||
_gtk_bookmarks_manager_insert_bookmark (sidebar->bookmarks_manager, f, position++, NULL); /* NULL-GError */
|
||
|
||
g_object_unref (info);
|
||
}
|
||
}
|
||
}
|
||
|
||
static void
|
||
drag_data_received_callback (GtkWidget *widget,
|
||
GdkDragContext *context,
|
||
int x,
|
||
int y,
|
||
GtkSelectionData *selection_data,
|
||
guint info,
|
||
guint time,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
GtkTreeView *tree_view;
|
||
GtkTreePath *tree_path;
|
||
GtkTreeViewDropPosition tree_pos;
|
||
GtkTreeIter iter;
|
||
gint position;
|
||
GtkTreeModel *model;
|
||
PlaceType place_type;
|
||
SectionType section_type;
|
||
gboolean success;
|
||
|
||
tree_view = GTK_TREE_VIEW (widget);
|
||
|
||
if (!sidebar->drag_data_received)
|
||
{
|
||
if (gtk_selection_data_get_target (selection_data) != GDK_NONE &&
|
||
info == TEXT_URI_LIST)
|
||
{
|
||
gchar **uris;
|
||
|
||
uris = gtk_selection_data_get_uris (selection_data);
|
||
sidebar->drag_list = build_file_list_from_uris ((const char **) uris);
|
||
g_strfreev (uris);
|
||
}
|
||
else
|
||
{
|
||
sidebar->drag_list = NULL;
|
||
}
|
||
sidebar->drag_data_received = TRUE;
|
||
sidebar->drag_data_info = info;
|
||
}
|
||
|
||
g_signal_stop_emission_by_name (widget, "drag-data-received");
|
||
|
||
if (!sidebar->drop_occured)
|
||
return;
|
||
|
||
/* Compute position */
|
||
success = compute_drop_position (tree_view, x, y, &tree_path, &tree_pos, sidebar);
|
||
if (!success)
|
||
goto out;
|
||
|
||
success = FALSE;
|
||
|
||
if (sidebar->drag_data_info == GTK_TREE_MODEL_ROW)
|
||
{
|
||
/* A bookmark got reordered */
|
||
|
||
model = gtk_tree_view_get_model (tree_view);
|
||
|
||
if (!gtk_tree_model_get_iter (model, &iter, tree_path))
|
||
goto out;
|
||
|
||
gtk_tree_model_get (model, &iter,
|
||
PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type,
|
||
PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type,
|
||
PLACES_SIDEBAR_COLUMN_INDEX, &position,
|
||
-1);
|
||
|
||
if (section_type != SECTION_BOOKMARKS)
|
||
goto out;
|
||
|
||
if (place_type == PLACES_HEADING)
|
||
position = 0;
|
||
else if (tree_pos == GTK_TREE_VIEW_DROP_AFTER)
|
||
position++;
|
||
|
||
reorder_bookmarks (sidebar, position);
|
||
success = TRUE;
|
||
}
|
||
else
|
||
{
|
||
/* Dropping URIs! */
|
||
|
||
GdkDragAction real_action;
|
||
gchar **uris;
|
||
GList *source_file_list;
|
||
|
||
/* file transfer requested */
|
||
real_action = gdk_drag_context_get_selected_action (context);
|
||
|
||
if (real_action == GDK_ACTION_ASK)
|
||
real_action = emit_drag_action_ask (sidebar, gdk_drag_context_get_actions (context));
|
||
|
||
if (real_action > 0)
|
||
{
|
||
gchar *uri;
|
||
GFile *dest_file;
|
||
gboolean drop_as_bookmarks;
|
||
|
||
model = gtk_tree_view_get_model (tree_view);
|
||
|
||
gtk_tree_model_get_iter (model, &iter, tree_path);
|
||
gtk_tree_model_get (model, &iter,
|
||
PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type,
|
||
PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type,
|
||
PLACES_SIDEBAR_COLUMN_INDEX, &position,
|
||
-1);
|
||
|
||
drop_as_bookmarks = FALSE;
|
||
|
||
uris = gtk_selection_data_get_uris (selection_data);
|
||
source_file_list = build_file_list_from_uris ((const gchar **) uris);
|
||
|
||
if (section_type == SECTION_BOOKMARKS)
|
||
{
|
||
if (place_type == PLACES_HEADING)
|
||
{
|
||
position = 0;
|
||
tree_pos = GTK_TREE_VIEW_DROP_BEFORE;
|
||
}
|
||
|
||
if (tree_pos == GTK_TREE_VIEW_DROP_AFTER)
|
||
position++;
|
||
|
||
if (tree_pos == GTK_TREE_VIEW_DROP_BEFORE ||
|
||
tree_pos == GTK_TREE_VIEW_DROP_AFTER ||
|
||
place_type == PLACES_DROP_FEEDBACK)
|
||
{
|
||
remove_drop_bookmark_feedback_row (sidebar);
|
||
drop_files_as_bookmarks (sidebar, source_file_list, position);
|
||
success = TRUE;
|
||
drop_as_bookmarks = TRUE;
|
||
}
|
||
}
|
||
|
||
if (!drop_as_bookmarks)
|
||
{
|
||
gtk_tree_model_get_iter (model, &iter, tree_path);
|
||
gtk_tree_model_get (model, &iter,
|
||
PLACES_SIDEBAR_COLUMN_URI, &uri,
|
||
-1);
|
||
|
||
dest_file = g_file_new_for_uri (uri);
|
||
|
||
emit_drag_perform_drop (sidebar, dest_file, source_file_list, real_action);
|
||
success = TRUE;
|
||
|
||
g_object_unref (dest_file);
|
||
g_free (uri);
|
||
}
|
||
|
||
g_list_free_full (source_file_list, g_object_unref);
|
||
g_strfreev (uris);
|
||
}
|
||
}
|
||
|
||
out:
|
||
sidebar->drop_occured = FALSE;
|
||
free_drag_data (sidebar);
|
||
remove_drop_bookmark_feedback_row (sidebar);
|
||
gtk_drag_finish (context, success, FALSE, time);
|
||
|
||
gtk_tree_path_free (tree_path);
|
||
}
|
||
|
||
static gboolean
|
||
drag_drop_callback (GtkTreeView *tree_view,
|
||
GdkDragContext *context,
|
||
gint x,
|
||
gint y,
|
||
guint time,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
gboolean retval = FALSE;
|
||
|
||
sidebar->drop_occured = TRUE;
|
||
retval = get_drag_data (tree_view, context, time);
|
||
g_signal_stop_emission_by_name (tree_view, "drag-drop");
|
||
return retval;
|
||
}
|
||
|
||
/* Callback used when the file list's popup menu is detached */
|
||
static void
|
||
bookmarks_popup_menu_detach_cb (GtkWidget *attach_widget,
|
||
GtkMenu *menu)
|
||
{
|
||
GTK_PLACES_SIDEBAR (attach_widget)->popup_menu = NULL;
|
||
}
|
||
|
||
static void
|
||
check_unmount_and_eject (GMount *mount,
|
||
GVolume *volume,
|
||
GDrive *drive,
|
||
gboolean *show_unmount,
|
||
gboolean *show_eject)
|
||
{
|
||
*show_unmount = FALSE;
|
||
*show_eject = FALSE;
|
||
|
||
if (drive != NULL)
|
||
*show_eject = g_drive_can_eject (drive);
|
||
|
||
if (volume != NULL)
|
||
*show_eject |= g_volume_can_eject (volume);
|
||
|
||
if (mount != NULL)
|
||
{
|
||
*show_eject |= g_mount_can_eject (mount);
|
||
*show_unmount = g_mount_can_unmount (mount) && !*show_eject;
|
||
}
|
||
}
|
||
|
||
static void
|
||
check_visibility (GMount *mount,
|
||
GVolume *volume,
|
||
GDrive *drive,
|
||
gboolean *show_mount,
|
||
gboolean *show_unmount,
|
||
gboolean *show_eject,
|
||
gboolean *show_rescan,
|
||
gboolean *show_start,
|
||
gboolean *show_stop)
|
||
{
|
||
*show_mount = FALSE;
|
||
*show_rescan = FALSE;
|
||
*show_start = FALSE;
|
||
*show_stop = FALSE;
|
||
|
||
check_unmount_and_eject (mount, volume, drive, show_unmount, show_eject);
|
||
|
||
if (drive != NULL)
|
||
{
|
||
if (g_drive_is_media_removable (drive) &&
|
||
!g_drive_is_media_check_automatic (drive) &&
|
||
g_drive_can_poll_for_media (drive))
|
||
*show_rescan = TRUE;
|
||
|
||
*show_start = g_drive_can_start (drive) || g_drive_can_start_degraded (drive);
|
||
*show_stop = g_drive_can_stop (drive);
|
||
|
||
if (*show_stop)
|
||
*show_unmount = FALSE;
|
||
}
|
||
|
||
if (volume != NULL)
|
||
{
|
||
if (mount == NULL)
|
||
*show_mount = g_volume_can_mount (volume);
|
||
}
|
||
}
|
||
|
||
typedef struct {
|
||
PlaceType type;
|
||
GDrive *drive;
|
||
GVolume *volume;
|
||
GMount *mount;
|
||
gchar *uri;
|
||
} SelectionInfo;
|
||
|
||
static void
|
||
get_selection_info (GtkPlacesSidebar *sidebar,
|
||
SelectionInfo *info)
|
||
{
|
||
GtkTreeIter iter;
|
||
|
||
info->type = PLACES_BUILT_IN;
|
||
info->drive = NULL;
|
||
info->volume = NULL;
|
||
info->mount = NULL;
|
||
info->uri = NULL;
|
||
|
||
if (get_selected_iter (sidebar, &iter))
|
||
gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter,
|
||
PLACES_SIDEBAR_COLUMN_ROW_TYPE, &info->type,
|
||
PLACES_SIDEBAR_COLUMN_DRIVE, &info->drive,
|
||
PLACES_SIDEBAR_COLUMN_VOLUME, &info->volume,
|
||
PLACES_SIDEBAR_COLUMN_MOUNT, &info->mount,
|
||
PLACES_SIDEBAR_COLUMN_URI, &info->uri,
|
||
-1);
|
||
}
|
||
|
||
static void
|
||
free_selection_info (SelectionInfo *info)
|
||
{
|
||
g_clear_object (&info->drive);
|
||
g_clear_object (&info->volume);
|
||
g_clear_object (&info->mount);
|
||
g_clear_pointer (&info->uri, g_free);
|
||
}
|
||
|
||
typedef struct {
|
||
GtkWidget *add_shortcut_item;
|
||
GtkWidget *remove_item;
|
||
GtkWidget *rename_item;
|
||
GtkWidget *separator_item;
|
||
GtkWidget *mount_item;
|
||
GtkWidget *unmount_item;
|
||
GtkWidget *eject_item;
|
||
GtkWidget *rescan_item;
|
||
GtkWidget *start_item;
|
||
GtkWidget *stop_item;
|
||
} PopupMenuData;
|
||
|
||
static void
|
||
check_popup_sensitivity (GtkPlacesSidebar *sidebar,
|
||
PopupMenuData *data,
|
||
SelectionInfo *info)
|
||
{
|
||
gboolean show_mount;
|
||
gboolean show_unmount;
|
||
gboolean show_eject;
|
||
gboolean show_rescan;
|
||
gboolean show_start;
|
||
gboolean show_stop;
|
||
|
||
gtk_widget_set_visible (data->add_shortcut_item, (info->type == PLACES_MOUNTED_VOLUME));
|
||
|
||
gtk_widget_set_sensitive (data->remove_item, (info->type == PLACES_BOOKMARK));
|
||
gtk_widget_set_sensitive (data->rename_item, (info->type == PLACES_BOOKMARK || info->type == PLACES_XDG_DIR));
|
||
|
||
check_visibility (info->mount, info->volume, info->drive,
|
||
&show_mount, &show_unmount, &show_eject, &show_rescan, &show_start, &show_stop);
|
||
|
||
gtk_widget_set_visible (data->separator_item, show_mount || show_unmount || show_eject);
|
||
gtk_widget_set_visible (data->mount_item, show_mount);
|
||
gtk_widget_set_visible (data->unmount_item, show_unmount);
|
||
gtk_widget_set_visible (data->eject_item, show_eject);
|
||
gtk_widget_set_visible (data->rescan_item, show_rescan);
|
||
gtk_widget_set_visible (data->start_item, show_start);
|
||
gtk_widget_set_visible (data->stop_item, show_stop);
|
||
|
||
/* Adjust start/stop items to reflect the type of the drive */
|
||
gtk_menu_item_set_label (GTK_MENU_ITEM (data->start_item), _("_Start"));
|
||
gtk_menu_item_set_label (GTK_MENU_ITEM (data->stop_item), _("_Stop"));
|
||
if ((show_start || show_stop) && info->drive != NULL)
|
||
{
|
||
switch (g_drive_get_start_stop_type (info->drive))
|
||
{
|
||
case G_DRIVE_START_STOP_TYPE_SHUTDOWN:
|
||
/* start() for type G_DRIVE_START_STOP_TYPE_SHUTDOWN is normally not used */
|
||
gtk_menu_item_set_label (GTK_MENU_ITEM (data->start_item), _("_Power On"));
|
||
gtk_menu_item_set_label (GTK_MENU_ITEM (data->stop_item), _("_Safely Remove Drive"));
|
||
break;
|
||
|
||
case G_DRIVE_START_STOP_TYPE_NETWORK:
|
||
gtk_menu_item_set_label (GTK_MENU_ITEM (data->start_item), _("_Connect Drive"));
|
||
gtk_menu_item_set_label (GTK_MENU_ITEM (data->stop_item), _("_Disconnect Drive"));
|
||
break;
|
||
|
||
case G_DRIVE_START_STOP_TYPE_MULTIDISK:
|
||
gtk_menu_item_set_label (GTK_MENU_ITEM (data->start_item), _("_Start Multi-disk Device"));
|
||
gtk_menu_item_set_label (GTK_MENU_ITEM (data->stop_item), _("_Stop Multi-disk Device"));
|
||
break;
|
||
|
||
case G_DRIVE_START_STOP_TYPE_PASSWORD:
|
||
/* stop() for type G_DRIVE_START_STOP_TYPE_PASSWORD is normally not used */
|
||
gtk_menu_item_set_label (GTK_MENU_ITEM (data->start_item), _("_Unlock Drive"));
|
||
gtk_menu_item_set_label (GTK_MENU_ITEM (data->stop_item), _("_Lock Drive"));
|
||
break;
|
||
|
||
default:
|
||
case G_DRIVE_START_STOP_TYPE_UNKNOWN:
|
||
/* uses defaults set above */
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void
|
||
drive_start_from_bookmark_cb (GObject *source_object,
|
||
GAsyncResult *res,
|
||
gpointer user_data)
|
||
{
|
||
GtkPlacesSidebar *sidebar;
|
||
GError *error;
|
||
gchar *primary;
|
||
gchar *name;
|
||
|
||
sidebar = GTK_PLACES_SIDEBAR (user_data);
|
||
|
||
error = NULL;
|
||
if (!g_drive_poll_for_media_finish (G_DRIVE (source_object), res, &error))
|
||
{
|
||
if (error->code != G_IO_ERROR_FAILED_HANDLED)
|
||
{
|
||
name = g_drive_get_name (G_DRIVE (source_object));
|
||
primary = g_strdup_printf (_("Unable to start %s"), name);
|
||
g_free (name);
|
||
emit_show_error_message (sidebar, primary, error->message);
|
||
g_free (primary);
|
||
}
|
||
g_error_free (error);
|
||
}
|
||
}
|
||
|
||
static void
|
||
volume_mount_cb (GObject *source_object,
|
||
GAsyncResult *result,
|
||
gpointer user_data)
|
||
{
|
||
GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (user_data);
|
||
GVolume *volume;
|
||
GError *error;
|
||
gchar *primary;
|
||
gchar *name;
|
||
GMount *mount;
|
||
|
||
volume = G_VOLUME (source_object);
|
||
|
||
error = NULL;
|
||
if (!g_volume_mount_finish (volume, result, &error))
|
||
{
|
||
if (error->code != G_IO_ERROR_FAILED_HANDLED &&
|
||
error->code != G_IO_ERROR_ALREADY_MOUNTED)
|
||
{
|
||
name = g_volume_get_name (G_VOLUME (source_object));
|
||
primary = g_strdup_printf (_("Unable to access “%s”"), name);
|
||
g_free (name);
|
||
emit_show_error_message (sidebar, primary, error->message);
|
||
g_free (primary);
|
||
}
|
||
g_error_free (error);
|
||
}
|
||
|
||
sidebar->mounting = FALSE;
|
||
|
||
mount = g_volume_get_mount (volume);
|
||
if (mount != NULL)
|
||
{
|
||
GFile *location;
|
||
|
||
location = g_mount_get_default_location (mount);
|
||
emit_open_location (sidebar, location, sidebar->go_to_after_mount_open_flags);
|
||
|
||
g_object_unref (G_OBJECT (location));
|
||
g_object_unref (G_OBJECT (mount));
|
||
}
|
||
|
||
g_object_unref (sidebar);
|
||
}
|
||
|
||
static void
|
||
mount_volume (GtkPlacesSidebar *sidebar,
|
||
GVolume *volume)
|
||
{
|
||
GMountOperation *mount_op;
|
||
|
||
mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar))));
|
||
g_mount_operation_set_password_save (mount_op, G_PASSWORD_SAVE_FOR_SESSION);
|
||
|
||
g_object_ref (sidebar);
|
||
g_volume_mount (volume, 0, mount_op, NULL, volume_mount_cb, sidebar);
|
||
}
|
||
|
||
static void
|
||
open_selected_volume (GtkPlacesSidebar *sidebar,
|
||
GtkTreeModel *model,
|
||
GtkTreeIter *iter,
|
||
GtkPlacesOpenFlags open_flags)
|
||
{
|
||
GDrive *drive;
|
||
GVolume *volume;
|
||
|
||
gtk_tree_model_get (model, iter,
|
||
PLACES_SIDEBAR_COLUMN_DRIVE, &drive,
|
||
PLACES_SIDEBAR_COLUMN_VOLUME, &volume,
|
||
-1);
|
||
|
||
if (volume != NULL && !sidebar->mounting)
|
||
{
|
||
sidebar->mounting = TRUE;
|
||
sidebar->go_to_after_mount_open_flags = open_flags;
|
||
mount_volume (sidebar, volume);
|
||
}
|
||
else if (volume == NULL && drive != NULL &&
|
||
(g_drive_can_start (drive) || g_drive_can_start_degraded (drive)))
|
||
{
|
||
GMountOperation *mount_op;
|
||
|
||
mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar))));
|
||
g_drive_start (drive, G_DRIVE_START_NONE, mount_op, NULL, drive_start_from_bookmark_cb, NULL);
|
||
g_object_unref (mount_op);
|
||
}
|
||
|
||
if (drive != NULL)
|
||
g_object_unref (drive);
|
||
|
||
if (volume != NULL)
|
||
g_object_unref (volume);
|
||
}
|
||
|
||
static void
|
||
open_selected_uri (GtkPlacesSidebar *sidebar,
|
||
const gchar *uri,
|
||
GtkPlacesOpenFlags open_flags)
|
||
{
|
||
GFile *location;
|
||
|
||
location = g_file_new_for_uri (uri);
|
||
emit_open_location (sidebar, location, open_flags);
|
||
g_object_unref (location);
|
||
}
|
||
|
||
static void
|
||
open_selected_bookmark (GtkPlacesSidebar *sidebar,
|
||
GtkTreeModel *model,
|
||
GtkTreeIter *iter,
|
||
GtkPlacesOpenFlags open_flags)
|
||
{
|
||
gchar *uri;
|
||
PlaceType place_type;
|
||
|
||
if (!iter)
|
||
return;
|
||
|
||
gtk_tree_model_get (model, iter,
|
||
PLACES_SIDEBAR_COLUMN_URI, &uri,
|
||
PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type,
|
||
-1);
|
||
|
||
if (uri != NULL)
|
||
{
|
||
open_selected_uri (sidebar, uri, open_flags);
|
||
g_free (uri);
|
||
}
|
||
else if (place_type == PLACES_CONNECT_TO_SERVER)
|
||
{
|
||
emit_show_connect_to_server (sidebar);
|
||
}
|
||
else if (place_type == PLACES_ENTER_LOCATION)
|
||
{
|
||
emit_show_enter_location (sidebar);
|
||
}
|
||
else
|
||
{
|
||
open_selected_volume (sidebar, model, iter, open_flags);
|
||
}
|
||
}
|
||
|
||
static void
|
||
open_shortcut_from_menu (GtkPlacesSidebar *sidebar,
|
||
GtkPlacesOpenFlags open_flags)
|
||
{
|
||
GtkTreeModel *model;
|
||
GtkTreeIter iter;
|
||
GtkTreePath *path = NULL;
|
||
|
||
model = gtk_tree_view_get_model (sidebar->tree_view);
|
||
gtk_tree_view_get_cursor (sidebar->tree_view, &path, NULL);
|
||
|
||
if (path != NULL && gtk_tree_model_get_iter (model, &iter, path))
|
||
open_selected_bookmark (sidebar, model, &iter, open_flags);
|
||
|
||
gtk_tree_path_free (path);
|
||
}
|
||
|
||
/* Callback used for the "Open" menu item in the context menu */
|
||
static void
|
||
open_shortcut_cb (GtkMenuItem *item,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
open_shortcut_from_menu (sidebar, GTK_PLACES_OPEN_NORMAL);
|
||
}
|
||
|
||
/* Callback used for the "Open in new tab" menu item in the context menu */
|
||
static void
|
||
open_shortcut_in_new_tab_cb (GtkMenuItem *item,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
open_shortcut_from_menu (sidebar, GTK_PLACES_OPEN_NEW_TAB);
|
||
}
|
||
|
||
/* Callback used for the "Open in new window" menu item in the context menu */
|
||
static void
|
||
open_shortcut_in_new_window_cb (GtkMenuItem *item,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
open_shortcut_from_menu (sidebar, GTK_PLACES_OPEN_NEW_WINDOW);
|
||
}
|
||
|
||
/* Add bookmark for the selected item - just used from mount points */
|
||
static void
|
||
add_shortcut_cb (GtkMenuItem *item,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
GtkTreeModel *model;
|
||
GtkTreeIter iter;
|
||
gchar *uri;
|
||
gchar *name;
|
||
GFile *location;
|
||
|
||
model = gtk_tree_view_get_model (sidebar->tree_view);
|
||
|
||
if (get_selected_iter (sidebar, &iter))
|
||
{
|
||
gtk_tree_model_get (model, &iter,
|
||
PLACES_SIDEBAR_COLUMN_URI, &uri,
|
||
PLACES_SIDEBAR_COLUMN_NAME, &name,
|
||
-1);
|
||
|
||
if (uri == NULL)
|
||
return;
|
||
|
||
location = g_file_new_for_uri (uri);
|
||
if (_gtk_bookmarks_manager_insert_bookmark (sidebar->bookmarks_manager, location, -1, NULL))
|
||
_gtk_bookmarks_manager_set_bookmark_label (sidebar->bookmarks_manager, location, name, NULL);
|
||
|
||
g_object_unref (location);
|
||
g_free (uri);
|
||
g_free (name);
|
||
}
|
||
}
|
||
|
||
/* Rename the selected bookmark */
|
||
static void
|
||
rename_selected_bookmark (GtkPlacesSidebar *sidebar)
|
||
{
|
||
GtkTreeIter iter;
|
||
GtkTreePath *path;
|
||
GtkTreeViewColumn *column;
|
||
PlaceType type;
|
||
|
||
if (get_selected_iter (sidebar, &iter))
|
||
{
|
||
gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter,
|
||
PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type,
|
||
-1);
|
||
|
||
if (type != PLACES_BOOKMARK && type != PLACES_XDG_DIR)
|
||
return;
|
||
|
||
path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store), &iter);
|
||
column = gtk_tree_view_get_column (GTK_TREE_VIEW (sidebar->tree_view), 0);
|
||
g_object_set (sidebar->text_cell_renderer, "editable", TRUE, NULL);
|
||
gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (sidebar->tree_view),
|
||
path, column, sidebar->text_cell_renderer, TRUE);
|
||
gtk_tree_path_free (path);
|
||
}
|
||
}
|
||
|
||
static void
|
||
rename_shortcut_cb (GtkMenuItem *item,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
rename_selected_bookmark (sidebar);
|
||
}
|
||
|
||
/* Removes the selected bookmarks */
|
||
static void
|
||
remove_selected_bookmarks (GtkPlacesSidebar *sidebar)
|
||
{
|
||
GtkTreeIter iter;
|
||
PlaceType type;
|
||
gchar *uri;
|
||
GFile *file;
|
||
|
||
if (!get_selected_iter (sidebar, &iter))
|
||
return;
|
||
|
||
gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter,
|
||
PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type,
|
||
-1);
|
||
|
||
if (type != PLACES_BOOKMARK)
|
||
return;
|
||
|
||
gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter,
|
||
PLACES_SIDEBAR_COLUMN_URI, &uri,
|
||
-1);
|
||
|
||
file = g_file_new_for_uri (uri);
|
||
_gtk_bookmarks_manager_remove_bookmark (sidebar->bookmarks_manager, file, NULL);
|
||
|
||
g_object_unref (file);
|
||
g_free (uri);
|
||
}
|
||
|
||
static void
|
||
remove_shortcut_cb (GtkMenuItem *item,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
remove_selected_bookmarks (sidebar);
|
||
}
|
||
|
||
static void
|
||
mount_shortcut_cb (GtkMenuItem *item,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
GtkTreeIter iter;
|
||
GVolume *volume;
|
||
|
||
if (!get_selected_iter (sidebar, &iter))
|
||
return;
|
||
|
||
gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter,
|
||
PLACES_SIDEBAR_COLUMN_VOLUME, &volume,
|
||
-1);
|
||
|
||
if (volume != NULL)
|
||
{
|
||
mount_volume (sidebar, volume);
|
||
g_object_unref (volume);
|
||
}
|
||
}
|
||
|
||
/* Callback used from g_mount_unmount_with_operation() */
|
||
static void
|
||
unmount_mount_cb (GObject *source_object,
|
||
GAsyncResult *result,
|
||
gpointer user_data)
|
||
{
|
||
GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (user_data);
|
||
GMount *mount;
|
||
GError *error;
|
||
|
||
mount = G_MOUNT (source_object);
|
||
|
||
error = NULL;
|
||
if (!g_mount_unmount_with_operation_finish (mount, result, &error))
|
||
{
|
||
if (error->code != G_IO_ERROR_FAILED_HANDLED)
|
||
{
|
||
gchar *name;
|
||
gchar *primary;
|
||
|
||
name = g_mount_get_name (mount);
|
||
primary = g_strdup_printf (_("Unable to unmount %s"), name);
|
||
g_free (name);
|
||
emit_show_error_message (sidebar, primary, error->message);
|
||
g_free (primary);
|
||
}
|
||
|
||
g_error_free (error);
|
||
}
|
||
|
||
/* FIXME: we need to switch to a path that is available now - $HOME? */
|
||
|
||
g_object_unref (sidebar);
|
||
}
|
||
|
||
static void
|
||
show_unmount_progress_cb (GMountOperation *op,
|
||
const gchar *message,
|
||
gint64 time_left,
|
||
gint64 bytes_left,
|
||
gpointer user_data)
|
||
{
|
||
/* FIXME: These are just libnotify notifications, but GTK+ doesn't do notifications right now.
|
||
* Should we just call D-Bus directly?
|
||
*/
|
||
#if 0
|
||
NautilusApplication *app = NAUTILUS_APPLICATION (g_application_get_default ());
|
||
|
||
if (bytes_left == 0) {
|
||
nautilus_application_notify_unmount_done (app, message);
|
||
} else {
|
||
nautilus_application_notify_unmount_show (app, message);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
static void
|
||
show_unmount_progress_aborted_cb (GMountOperation *op,
|
||
gpointer user_data)
|
||
{
|
||
/* FIXME: These are just libnotify notifications, but GTK+ doesn't do notifications right now.
|
||
* Should we just call D-Bus directly?
|
||
*/
|
||
#if 0
|
||
NautilusApplication *app = NAUTILUS_APPLICATION (g_application_get_default ());
|
||
nautilus_application_notify_unmount_done (app, NULL);
|
||
#endif
|
||
}
|
||
|
||
static GMountOperation *
|
||
get_unmount_operation (GtkPlacesSidebar *sidebar)
|
||
{
|
||
GMountOperation *mount_op;
|
||
|
||
mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar))));
|
||
g_signal_connect (mount_op, "show-unmount-progress",
|
||
G_CALLBACK (show_unmount_progress_cb), sidebar);
|
||
g_signal_connect (mount_op, "aborted",
|
||
G_CALLBACK (show_unmount_progress_aborted_cb), sidebar);
|
||
|
||
return mount_op;
|
||
}
|
||
|
||
static void
|
||
do_unmount (GMount *mount,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
if (mount != NULL)
|
||
{
|
||
GMountOperation *mount_op;
|
||
|
||
mount_op = get_unmount_operation (sidebar);
|
||
g_mount_unmount_with_operation (mount,
|
||
0,
|
||
mount_op,
|
||
NULL,
|
||
unmount_mount_cb,
|
||
g_object_ref (sidebar));
|
||
g_object_unref (mount_op);
|
||
}
|
||
}
|
||
|
||
static void
|
||
do_unmount_selection (GtkPlacesSidebar *sidebar)
|
||
{
|
||
GtkTreeIter iter;
|
||
GMount *mount;
|
||
|
||
if (!get_selected_iter (sidebar, &iter))
|
||
return;
|
||
|
||
gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter,
|
||
PLACES_SIDEBAR_COLUMN_MOUNT, &mount,
|
||
-1);
|
||
|
||
if (mount != NULL)
|
||
{
|
||
do_unmount (mount, sidebar);
|
||
g_object_unref (mount);
|
||
}
|
||
}
|
||
|
||
static void
|
||
unmount_shortcut_cb (GtkMenuItem *item,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
do_unmount_selection (sidebar);
|
||
}
|
||
|
||
static void
|
||
drive_stop_cb (GObject *source_object,
|
||
GAsyncResult *res,
|
||
gpointer user_data)
|
||
{
|
||
GtkPlacesSidebar *sidebar;
|
||
GError *error;
|
||
gchar *primary;
|
||
gchar *name;
|
||
|
||
sidebar = user_data;
|
||
|
||
error = NULL;
|
||
if (!g_drive_stop_finish (G_DRIVE (source_object), res, &error))
|
||
{
|
||
if (error->code != G_IO_ERROR_FAILED_HANDLED)
|
||
{
|
||
name = g_drive_get_name (G_DRIVE (source_object));
|
||
primary = g_strdup_printf (_("Unable to stop %s"), name);
|
||
g_free (name);
|
||
emit_show_error_message (sidebar, primary, error->message);
|
||
g_free (primary);
|
||
}
|
||
g_error_free (error);
|
||
}
|
||
|
||
g_object_unref (sidebar);
|
||
}
|
||
|
||
static void
|
||
drive_eject_cb (GObject *source_object,
|
||
GAsyncResult *res,
|
||
gpointer user_data)
|
||
{
|
||
GtkPlacesSidebar *sidebar;
|
||
GError *error;
|
||
gchar *primary;
|
||
gchar *name;
|
||
|
||
sidebar = user_data;
|
||
|
||
error = NULL;
|
||
if (!g_drive_eject_with_operation_finish (G_DRIVE (source_object), res, &error))
|
||
{
|
||
if (error->code != G_IO_ERROR_FAILED_HANDLED)
|
||
{
|
||
name = g_drive_get_name (G_DRIVE (source_object));
|
||
primary = g_strdup_printf (_("Unable to eject %s"), name);
|
||
g_free (name);
|
||
emit_show_error_message (sidebar, primary, error->message);
|
||
g_free (primary);
|
||
}
|
||
g_error_free (error);
|
||
}
|
||
|
||
g_object_unref (sidebar);
|
||
}
|
||
|
||
static void
|
||
volume_eject_cb (GObject *source_object,
|
||
GAsyncResult *res,
|
||
gpointer user_data)
|
||
{
|
||
GtkPlacesSidebar *sidebar;
|
||
GError *error;
|
||
gchar *primary;
|
||
gchar *name;
|
||
|
||
sidebar = user_data;
|
||
|
||
error = NULL;
|
||
if (!g_volume_eject_with_operation_finish (G_VOLUME (source_object), res, &error))
|
||
{
|
||
if (error->code != G_IO_ERROR_FAILED_HANDLED)
|
||
{
|
||
name = g_volume_get_name (G_VOLUME (source_object));
|
||
primary = g_strdup_printf (_("Unable to eject %s"), name);
|
||
g_free (name);
|
||
emit_show_error_message (sidebar, primary, error->message);
|
||
g_free (primary);
|
||
}
|
||
g_error_free (error);
|
||
}
|
||
|
||
g_object_unref (sidebar);
|
||
}
|
||
|
||
static void
|
||
mount_eject_cb (GObject *source_object,
|
||
GAsyncResult *res,
|
||
gpointer user_data)
|
||
{
|
||
GtkPlacesSidebar *sidebar;
|
||
GError *error;
|
||
gchar *primary;
|
||
gchar *name;
|
||
|
||
sidebar = user_data;
|
||
|
||
error = NULL;
|
||
if (!g_mount_eject_with_operation_finish (G_MOUNT (source_object), res, &error))
|
||
{
|
||
if (error->code != G_IO_ERROR_FAILED_HANDLED)
|
||
{
|
||
name = g_mount_get_name (G_MOUNT (source_object));
|
||
primary = g_strdup_printf (_("Unable to eject %s"), name);
|
||
g_free (name);
|
||
emit_show_error_message (sidebar, primary, error->message);
|
||
g_free (primary);
|
||
}
|
||
g_error_free (error);
|
||
}
|
||
|
||
g_object_unref (sidebar);
|
||
}
|
||
|
||
static void
|
||
do_eject (GMount *mount,
|
||
GVolume *volume,
|
||
GDrive *drive,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
GMountOperation *mount_op;
|
||
|
||
mount_op = get_unmount_operation (sidebar);
|
||
if (mount != NULL)
|
||
g_mount_eject_with_operation (mount, 0, mount_op, NULL, mount_eject_cb,
|
||
g_object_ref (sidebar));
|
||
else if (volume != NULL)
|
||
g_volume_eject_with_operation (volume, 0, mount_op, NULL, volume_eject_cb,
|
||
g_object_ref (sidebar));
|
||
else if (drive != NULL)
|
||
{
|
||
if (g_drive_can_stop (drive))
|
||
g_drive_stop (drive, 0, mount_op, NULL, drive_stop_cb,
|
||
g_object_ref (sidebar));
|
||
else
|
||
g_drive_eject_with_operation (drive, 0, mount_op, NULL, drive_eject_cb,
|
||
g_object_ref (sidebar));
|
||
}
|
||
g_object_unref (mount_op);
|
||
}
|
||
|
||
static void
|
||
eject_shortcut_cb (GtkMenuItem *item,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
GtkTreeIter iter;
|
||
GMount *mount;
|
||
GVolume *volume;
|
||
GDrive *drive;
|
||
|
||
if (!get_selected_iter (sidebar, &iter))
|
||
return;
|
||
|
||
gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter,
|
||
PLACES_SIDEBAR_COLUMN_MOUNT, &mount,
|
||
PLACES_SIDEBAR_COLUMN_VOLUME, &volume,
|
||
PLACES_SIDEBAR_COLUMN_DRIVE, &drive,
|
||
-1);
|
||
|
||
do_eject (mount, volume, drive, sidebar);
|
||
}
|
||
|
||
static gboolean
|
||
eject_or_unmount_bookmark (GtkPlacesSidebar *sidebar,
|
||
GtkTreePath *path)
|
||
{
|
||
GtkTreeModel *model;
|
||
GtkTreeIter iter;
|
||
gboolean can_unmount, can_eject;
|
||
GMount *mount;
|
||
GVolume *volume;
|
||
GDrive *drive;
|
||
gboolean ret;
|
||
|
||
model = GTK_TREE_MODEL (sidebar->store);
|
||
|
||
if (!path)
|
||
return FALSE;
|
||
|
||
if (!gtk_tree_model_get_iter (model, &iter, path))
|
||
return FALSE;
|
||
|
||
gtk_tree_model_get (model, &iter,
|
||
PLACES_SIDEBAR_COLUMN_MOUNT, &mount,
|
||
PLACES_SIDEBAR_COLUMN_VOLUME, &volume,
|
||
PLACES_SIDEBAR_COLUMN_DRIVE, &drive,
|
||
-1);
|
||
|
||
ret = FALSE;
|
||
|
||
check_unmount_and_eject (mount, volume, drive, &can_unmount, &can_eject);
|
||
/* if we can eject, it has priority over unmount */
|
||
if (can_eject)
|
||
{
|
||
do_eject (mount, volume, drive, sidebar);
|
||
ret = TRUE;
|
||
}
|
||
else if (can_unmount)
|
||
{
|
||
do_unmount (mount, sidebar);
|
||
ret = TRUE;
|
||
}
|
||
|
||
g_clear_object (&mount);
|
||
g_clear_object (&volume);
|
||
g_clear_object (&drive);
|
||
|
||
return ret;
|
||
}
|
||
|
||
static gboolean
|
||
eject_or_unmount_selection (GtkPlacesSidebar *sidebar)
|
||
{
|
||
GtkTreeIter iter;
|
||
GtkTreePath *path;
|
||
gboolean ret;
|
||
|
||
if (!get_selected_iter (sidebar, &iter))
|
||
return FALSE;
|
||
|
||
path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store), &iter);
|
||
if (path == NULL)
|
||
return FALSE;
|
||
|
||
ret = eject_or_unmount_bookmark (sidebar, path);
|
||
|
||
gtk_tree_path_free (path);
|
||
|
||
return ret;
|
||
}
|
||
|
||
static void
|
||
drive_poll_for_media_cb (GObject *source_object,
|
||
GAsyncResult *res,
|
||
gpointer user_data)
|
||
{
|
||
GtkPlacesSidebar *sidebar;
|
||
GError *error;
|
||
gchar *primary;
|
||
gchar *name;
|
||
|
||
sidebar = GTK_PLACES_SIDEBAR (user_data);
|
||
|
||
error = NULL;
|
||
if (!g_drive_poll_for_media_finish (G_DRIVE (source_object), res, &error))
|
||
{
|
||
if (error->code != G_IO_ERROR_FAILED_HANDLED)
|
||
{
|
||
name = g_drive_get_name (G_DRIVE (source_object));
|
||
primary = g_strdup_printf (_("Unable to poll %s for media changes"), name);
|
||
g_free (name);
|
||
emit_show_error_message (sidebar, primary, error->message);
|
||
g_free (primary);
|
||
}
|
||
g_error_free (error);
|
||
}
|
||
|
||
g_object_unref (sidebar);
|
||
}
|
||
|
||
static void
|
||
rescan_shortcut_cb (GtkMenuItem *item,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
GtkTreeIter iter;
|
||
GDrive *drive;
|
||
|
||
if (!get_selected_iter (sidebar, &iter))
|
||
return;
|
||
|
||
gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter,
|
||
PLACES_SIDEBAR_COLUMN_DRIVE, &drive,
|
||
-1);
|
||
|
||
if (drive != NULL)
|
||
{
|
||
g_drive_poll_for_media (drive, NULL, drive_poll_for_media_cb, g_object_ref (sidebar));
|
||
g_object_unref (drive);
|
||
}
|
||
}
|
||
|
||
static void
|
||
drive_start_cb (GObject *source_object,
|
||
GAsyncResult *res,
|
||
gpointer user_data)
|
||
{
|
||
GtkPlacesSidebar *sidebar;
|
||
GError *error;
|
||
gchar *primary;
|
||
gchar *name;
|
||
|
||
sidebar = GTK_PLACES_SIDEBAR (user_data);
|
||
|
||
error = NULL;
|
||
if (!g_drive_start_finish (G_DRIVE (source_object), res, &error))
|
||
{
|
||
if (error->code != G_IO_ERROR_FAILED_HANDLED)
|
||
{
|
||
name = g_drive_get_name (G_DRIVE (source_object));
|
||
primary = g_strdup_printf (_("Unable to start %s"), name);
|
||
g_free (name);
|
||
emit_show_error_message (sidebar, primary, error->message);
|
||
g_free (primary);
|
||
}
|
||
g_error_free (error);
|
||
}
|
||
|
||
g_object_unref (sidebar);
|
||
}
|
||
|
||
static void
|
||
start_shortcut_cb (GtkMenuItem *item,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
GtkTreeIter iter;
|
||
GDrive *drive;
|
||
|
||
if (!get_selected_iter (sidebar, &iter))
|
||
return;
|
||
|
||
gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter,
|
||
PLACES_SIDEBAR_COLUMN_DRIVE, &drive,
|
||
-1);
|
||
|
||
if (drive != NULL)
|
||
{
|
||
GMountOperation *mount_op;
|
||
|
||
mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar))));
|
||
|
||
g_drive_start (drive, G_DRIVE_START_NONE, mount_op, NULL, drive_start_cb, g_object_ref (sidebar));
|
||
|
||
g_object_unref (mount_op);
|
||
g_object_unref (drive);
|
||
}
|
||
}
|
||
|
||
static void
|
||
stop_shortcut_cb (GtkMenuItem *item,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
GtkTreeIter iter;
|
||
GDrive *drive;
|
||
|
||
if (!get_selected_iter (sidebar, &iter))
|
||
return;
|
||
|
||
gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter,
|
||
PLACES_SIDEBAR_COLUMN_DRIVE, &drive,
|
||
-1);
|
||
|
||
if (drive != NULL)
|
||
{
|
||
GMountOperation *mount_op;
|
||
|
||
mount_op = get_unmount_operation (sidebar);
|
||
g_drive_stop (drive, G_MOUNT_UNMOUNT_NONE, mount_op, NULL, drive_stop_cb,
|
||
g_object_ref (sidebar));
|
||
|
||
g_object_unref (mount_op);
|
||
g_object_unref (drive);
|
||
}
|
||
}
|
||
|
||
static gboolean
|
||
find_prev_or_next_row (GtkPlacesSidebar *sidebar,
|
||
GtkTreeIter *iter,
|
||
gboolean go_up)
|
||
{
|
||
GtkTreeModel *model = GTK_TREE_MODEL (sidebar->store);
|
||
gboolean res;
|
||
gint place_type;
|
||
|
||
if (go_up)
|
||
res = gtk_tree_model_iter_previous (model, iter);
|
||
else
|
||
res = gtk_tree_model_iter_next (model, iter);
|
||
|
||
if (res)
|
||
{
|
||
gtk_tree_model_get (model, iter,
|
||
PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type,
|
||
-1);
|
||
if (place_type == PLACES_HEADING)
|
||
{
|
||
if (go_up)
|
||
res = gtk_tree_model_iter_previous (model, iter);
|
||
else
|
||
res = gtk_tree_model_iter_next (model, iter);
|
||
}
|
||
}
|
||
|
||
return res;
|
||
}
|
||
|
||
static gboolean
|
||
find_prev_row (GtkPlacesSidebar *sidebar,
|
||
GtkTreeIter *iter)
|
||
{
|
||
return find_prev_or_next_row (sidebar, iter, TRUE);
|
||
}
|
||
|
||
static gboolean
|
||
find_next_row (GtkPlacesSidebar *sidebar,
|
||
GtkTreeIter *iter)
|
||
{
|
||
return find_prev_or_next_row (sidebar, iter, FALSE);
|
||
}
|
||
|
||
static gboolean
|
||
gtk_places_sidebar_focus (GtkWidget *widget,
|
||
GtkDirectionType direction)
|
||
{
|
||
GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (widget);
|
||
GtkTreePath *path;
|
||
GtkTreeIter iter;
|
||
gboolean res;
|
||
|
||
if (!get_selected_iter (sidebar, &iter))
|
||
{
|
||
gtk_tree_model_get_iter_first (GTK_TREE_MODEL (sidebar->store), &iter);
|
||
res = find_next_row (sidebar, &iter);
|
||
if (res)
|
||
{
|
||
path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store), &iter);
|
||
gtk_tree_view_set_cursor (sidebar->tree_view, path, NULL, FALSE);
|
||
gtk_tree_path_free (path);
|
||
}
|
||
}
|
||
|
||
return GTK_WIDGET_CLASS (gtk_places_sidebar_parent_class)->focus (widget, direction);
|
||
}
|
||
|
||
/* Handler for GtkWidget::key-press-event on the shortcuts list */
|
||
static gboolean
|
||
bookmarks_key_press_event_cb (GtkWidget *widget,
|
||
GdkEventKey *event,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
guint modifiers;
|
||
GtkTreeIter selected_iter;
|
||
GtkTreePath *path;
|
||
|
||
if (!get_selected_iter (sidebar, &selected_iter))
|
||
return FALSE;
|
||
|
||
modifiers = gtk_accelerator_get_default_mod_mask ();
|
||
|
||
if (event->keyval == GDK_KEY_Return ||
|
||
event->keyval == GDK_KEY_KP_Enter ||
|
||
event->keyval == GDK_KEY_ISO_Enter ||
|
||
event->keyval == GDK_KEY_space)
|
||
{
|
||
GtkPlacesOpenFlags open_flags = GTK_PLACES_OPEN_NORMAL;
|
||
|
||
if ((event->state & modifiers) == GDK_SHIFT_MASK)
|
||
open_flags = GTK_PLACES_OPEN_NEW_TAB;
|
||
else if ((event->state & modifiers) == GDK_CONTROL_MASK)
|
||
open_flags = GTK_PLACES_OPEN_NEW_WINDOW;
|
||
|
||
open_selected_bookmark (sidebar, GTK_TREE_MODEL (sidebar->store),
|
||
&selected_iter, open_flags);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
if (event->keyval == GDK_KEY_Down &&
|
||
(event->state & modifiers) == GDK_MOD1_MASK)
|
||
return eject_or_unmount_selection (sidebar);
|
||
|
||
if (event->keyval == GDK_KEY_Up)
|
||
{
|
||
if (find_prev_row (sidebar, &selected_iter))
|
||
{
|
||
path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store), &selected_iter);
|
||
gtk_tree_view_set_cursor (sidebar->tree_view, path, NULL, FALSE);
|
||
gtk_tree_path_free (path);
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
if (event->keyval == GDK_KEY_Down)
|
||
{
|
||
if (find_next_row (sidebar, &selected_iter))
|
||
{
|
||
path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store), &selected_iter);
|
||
gtk_tree_view_set_cursor (sidebar->tree_view, path, NULL, FALSE);
|
||
gtk_tree_path_free (path);
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
if ((event->keyval == GDK_KEY_Delete ||
|
||
event->keyval == GDK_KEY_KP_Delete) &&
|
||
(event->state & modifiers) == 0)
|
||
{
|
||
remove_selected_bookmarks (sidebar);
|
||
return TRUE;
|
||
}
|
||
|
||
if ((event->keyval == GDK_KEY_F2) &&
|
||
(event->state & modifiers) == 0)
|
||
{
|
||
rename_selected_bookmark (sidebar);
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static GtkMenuItem *
|
||
append_menu_separator (GtkMenu *menu)
|
||
{
|
||
GtkWidget *menu_item;
|
||
|
||
menu_item = gtk_separator_menu_item_new ();
|
||
gtk_widget_show (menu_item);
|
||
gtk_menu_shell_insert (GTK_MENU_SHELL (menu), menu_item, -1);
|
||
|
||
return GTK_MENU_ITEM (menu_item);
|
||
}
|
||
|
||
/* Constructs the popup menu for the file list if needed */
|
||
static void
|
||
bookmarks_build_popup_menu (GtkPlacesSidebar *sidebar)
|
||
{
|
||
PopupMenuData menu_data;
|
||
GtkWidget *item;
|
||
SelectionInfo sel_info;
|
||
GFile *file;
|
||
|
||
sidebar->popup_menu = gtk_menu_new ();
|
||
gtk_style_context_add_class (gtk_widget_get_style_context (sidebar->popup_menu),
|
||
GTK_STYLE_CLASS_CONTEXT_MENU);
|
||
|
||
gtk_menu_attach_to_widget (GTK_MENU (sidebar->popup_menu),
|
||
GTK_WIDGET (sidebar),
|
||
bookmarks_popup_menu_detach_cb);
|
||
|
||
item = gtk_menu_item_new_with_mnemonic (_("_Open"));
|
||
g_signal_connect (item, "activate",
|
||
G_CALLBACK (open_shortcut_cb), sidebar);
|
||
gtk_widget_show (item);
|
||
gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
|
||
|
||
if (sidebar->open_flags & GTK_PLACES_OPEN_NEW_TAB)
|
||
{
|
||
item = gtk_menu_item_new_with_mnemonic (_("Open in New _Tab"));
|
||
g_signal_connect (item, "activate",
|
||
G_CALLBACK (open_shortcut_in_new_tab_cb), sidebar);
|
||
gtk_widget_show (item);
|
||
gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
|
||
}
|
||
|
||
if (sidebar->open_flags & GTK_PLACES_OPEN_NEW_WINDOW)
|
||
{
|
||
item = gtk_menu_item_new_with_mnemonic (_("Open in New _Window"));
|
||
g_signal_connect (item, "activate",
|
||
G_CALLBACK (open_shortcut_in_new_window_cb), sidebar);
|
||
gtk_widget_show (item);
|
||
gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
|
||
}
|
||
|
||
append_menu_separator (GTK_MENU (sidebar->popup_menu));
|
||
|
||
item = gtk_menu_item_new_with_mnemonic (_("_Add Bookmark"));
|
||
menu_data.add_shortcut_item = item;
|
||
g_signal_connect (item, "activate",
|
||
G_CALLBACK (add_shortcut_cb), sidebar);
|
||
gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
|
||
|
||
item = gtk_menu_item_new_with_label (_("Remove"));
|
||
menu_data.remove_item = item;
|
||
g_signal_connect (item, "activate",
|
||
G_CALLBACK (remove_shortcut_cb), sidebar);
|
||
gtk_widget_show (item);
|
||
gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
|
||
|
||
item = gtk_menu_item_new_with_label (_("Rename…"));
|
||
menu_data.rename_item = item;
|
||
g_signal_connect (item, "activate",
|
||
G_CALLBACK (rename_shortcut_cb), sidebar);
|
||
gtk_widget_show (item);
|
||
gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
|
||
|
||
/* Mount/Unmount/Eject menu items */
|
||
|
||
menu_data.separator_item = GTK_WIDGET (append_menu_separator (GTK_MENU (sidebar->popup_menu)));
|
||
|
||
item = gtk_menu_item_new_with_mnemonic (_("_Mount"));
|
||
menu_data.mount_item = item;
|
||
g_signal_connect (item, "activate",
|
||
G_CALLBACK (mount_shortcut_cb), sidebar);
|
||
gtk_widget_show (item);
|
||
gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
|
||
|
||
item = gtk_menu_item_new_with_mnemonic (_("_Unmount"));
|
||
menu_data.unmount_item = item;
|
||
g_signal_connect (item, "activate",
|
||
G_CALLBACK (unmount_shortcut_cb), sidebar);
|
||
gtk_widget_show (item);
|
||
gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
|
||
|
||
item = gtk_menu_item_new_with_mnemonic (_("_Eject"));
|
||
menu_data.eject_item = item;
|
||
g_signal_connect (item, "activate",
|
||
G_CALLBACK (eject_shortcut_cb), sidebar);
|
||
gtk_widget_show (item);
|
||
gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
|
||
|
||
item = gtk_menu_item_new_with_mnemonic (_("_Detect Media"));
|
||
menu_data.rescan_item = item;
|
||
g_signal_connect (item, "activate",
|
||
G_CALLBACK (rescan_shortcut_cb), sidebar);
|
||
gtk_widget_show (item);
|
||
gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
|
||
|
||
item = gtk_menu_item_new_with_mnemonic (_("_Start"));
|
||
menu_data.start_item = item;
|
||
g_signal_connect (item, "activate",
|
||
G_CALLBACK (start_shortcut_cb), sidebar);
|
||
gtk_widget_show (item);
|
||
gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
|
||
|
||
item = gtk_menu_item_new_with_mnemonic (_("_Stop"));
|
||
menu_data.stop_item = item;
|
||
g_signal_connect (item, "activate",
|
||
G_CALLBACK (stop_shortcut_cb), sidebar);
|
||
gtk_widget_show (item);
|
||
gtk_menu_shell_append (GTK_MENU_SHELL (sidebar->popup_menu), item);
|
||
|
||
/* Update everything! */
|
||
|
||
get_selection_info (sidebar, &sel_info);
|
||
|
||
check_popup_sensitivity (sidebar, &menu_data, &sel_info);
|
||
|
||
/* And let the caller spice things up */
|
||
|
||
if (sel_info.uri)
|
||
file = g_file_new_for_uri (sel_info.uri);
|
||
else
|
||
file = NULL;
|
||
|
||
emit_populate_popup (sidebar, GTK_MENU (sidebar->popup_menu), file, sel_info.volume);
|
||
|
||
if (file)
|
||
g_object_unref (file);
|
||
|
||
free_selection_info (&sel_info);
|
||
}
|
||
|
||
static void
|
||
bookmarks_popup_menu (GtkPlacesSidebar *sidebar,
|
||
GdkEventButton *event)
|
||
{
|
||
gint button;
|
||
|
||
if (sidebar->popup_menu)
|
||
gtk_widget_destroy (sidebar->popup_menu);
|
||
|
||
bookmarks_build_popup_menu (sidebar);
|
||
|
||
/* The event button needs to be 0 if we're popping up this menu from
|
||
* a button release, else a 2nd click outside the menu with any button
|
||
* other than the one that invoked the menu will be ignored (instead
|
||
* of dismissing the menu). This is a subtle fragility of the GTK menu code.
|
||
*/
|
||
if (event)
|
||
{
|
||
if (event->type == GDK_BUTTON_RELEASE)
|
||
button = 0;
|
||
else
|
||
button = event->button;
|
||
}
|
||
else
|
||
{
|
||
button = 0;
|
||
}
|
||
|
||
gtk_menu_popup (GTK_MENU (sidebar->popup_menu),
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
button,
|
||
event ? event->time : gtk_get_current_event_time ());
|
||
}
|
||
|
||
/* Callback used for the GtkWidget::popup-menu signal of the shortcuts list */
|
||
static gboolean
|
||
bookmarks_popup_menu_cb (GtkWidget *widget,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
bookmarks_popup_menu (sidebar, NULL);
|
||
return TRUE;
|
||
}
|
||
|
||
static void
|
||
bookmarks_row_activated_cb (GtkWidget *widget,
|
||
GtkTreePath *path,
|
||
GtkTreeViewColumn *column,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
GtkTreeIter iter;
|
||
GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
|
||
|
||
if (!gtk_tree_model_get_iter (model, &iter, path))
|
||
return;
|
||
|
||
open_selected_bookmark (sidebar, model, &iter, 0);
|
||
}
|
||
|
||
static gboolean
|
||
bookmarks_button_release_event_cb (GtkWidget *widget,
|
||
GdkEventButton *event,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
GtkTreePath *path;
|
||
GtkTreeIter iter;
|
||
GtkTreeModel *model;
|
||
GtkTreeView *tree_view;
|
||
gboolean ret = FALSE;
|
||
gboolean res;
|
||
|
||
path = NULL;
|
||
|
||
if (event->type != GDK_BUTTON_RELEASE)
|
||
return TRUE;
|
||
|
||
if (clicked_eject_button (sidebar, &path))
|
||
{
|
||
eject_or_unmount_bookmark (sidebar, path);
|
||
gtk_tree_path_free (path);
|
||
return TRUE;
|
||
}
|
||
|
||
if (event->button == 1)
|
||
return FALSE;
|
||
|
||
tree_view = GTK_TREE_VIEW (widget);
|
||
model = gtk_tree_view_get_model (tree_view);
|
||
|
||
if (event->window != gtk_tree_view_get_bin_window (tree_view))
|
||
return FALSE;
|
||
|
||
res = gtk_tree_view_get_path_at_pos (tree_view, (int) event->x, (int) event->y,
|
||
&path, NULL, NULL, NULL);
|
||
|
||
if (!res || path == NULL)
|
||
return FALSE;
|
||
|
||
res = gtk_tree_model_get_iter (model, &iter, path);
|
||
if (!res)
|
||
{
|
||
gtk_tree_path_free (path);
|
||
return FALSE;
|
||
}
|
||
|
||
if (event->button == 2)
|
||
{
|
||
GtkPlacesOpenFlags open_flags = GTK_PLACES_OPEN_NORMAL;
|
||
|
||
open_flags = (event->state & GDK_CONTROL_MASK) ?
|
||
GTK_PLACES_OPEN_NEW_WINDOW :
|
||
GTK_PLACES_OPEN_NEW_TAB;
|
||
|
||
open_selected_bookmark (sidebar, model, &iter, open_flags);
|
||
ret = TRUE;
|
||
}
|
||
else if (event->button == 3)
|
||
{
|
||
PlaceType row_type;
|
||
|
||
gtk_tree_model_get (model, &iter,
|
||
PLACES_SIDEBAR_COLUMN_ROW_TYPE, &row_type,
|
||
-1);
|
||
|
||
if (row_type != PLACES_HEADING && row_type != PLACES_CONNECT_TO_SERVER)
|
||
bookmarks_popup_menu (sidebar, event);
|
||
}
|
||
|
||
gtk_tree_path_free (path);
|
||
|
||
return ret;
|
||
}
|
||
|
||
static void
|
||
bookmarks_edited (GtkCellRenderer *cell,
|
||
gchar *path_string,
|
||
gchar *new_text,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
GtkTreePath *path;
|
||
GtkTreeIter iter;
|
||
char *uri;
|
||
GFile *file;
|
||
|
||
g_object_set (cell, "editable", FALSE, NULL);
|
||
|
||
path = gtk_tree_path_new_from_string (path_string);
|
||
gtk_tree_model_get_iter (GTK_TREE_MODEL (sidebar->store), &iter, path);
|
||
gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter,
|
||
PLACES_SIDEBAR_COLUMN_URI, &uri,
|
||
-1);
|
||
gtk_tree_path_free (path);
|
||
|
||
file = g_file_new_for_uri (uri);
|
||
if (!_gtk_bookmarks_manager_has_bookmark (sidebar->bookmarks_manager, file))
|
||
_gtk_bookmarks_manager_insert_bookmark (sidebar->bookmarks_manager, file, -1, NULL);
|
||
|
||
_gtk_bookmarks_manager_set_bookmark_label (sidebar->bookmarks_manager, file, new_text, NULL); /* NULL-GError */
|
||
|
||
g_object_unref (file);
|
||
g_free (uri);
|
||
}
|
||
|
||
static void
|
||
bookmarks_editing_canceled (GtkCellRenderer *cell,
|
||
GtkPlacesSidebar *sidebar)
|
||
{
|
||
g_object_set (cell, "editable", FALSE, NULL);
|
||
}
|
||
|
||
static gboolean
|
||
tree_selection_func (GtkTreeSelection *selection,
|
||
GtkTreeModel *model,
|
||
GtkTreePath *path,
|
||
gboolean path_currently_selected,
|
||
gpointer user_data)
|
||
{
|
||
GtkTreeIter iter;
|
||
PlaceType row_type;
|
||
|
||
gtk_tree_model_get_iter (model, &iter, path);
|
||
gtk_tree_model_get (model, &iter,
|
||
PLACES_SIDEBAR_COLUMN_ROW_TYPE, &row_type,
|
||
-1);
|
||
|
||
if (row_type == PLACES_HEADING)
|
||
return FALSE;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static void
|
||
icon_cell_renderer_func (GtkTreeViewColumn *column,
|
||
GtkCellRenderer *cell,
|
||
GtkTreeModel *model,
|
||
GtkTreeIter *iter,
|
||
gpointer user_data)
|
||
{
|
||
PlaceType type;
|
||
|
||
gtk_tree_model_get (model, iter,
|
||
PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type,
|
||
-1);
|
||
|
||
g_object_set (cell, "visible", type != PLACES_HEADING, NULL);
|
||
}
|
||
|
||
static gint
|
||
places_sidebar_sort_func (GtkTreeModel *model,
|
||
GtkTreeIter *iter_a,
|
||
GtkTreeIter *iter_b,
|
||
gpointer user_data)
|
||
{
|
||
SectionType section_type_a, section_type_b;
|
||
PlaceType place_type_a, place_type_b;
|
||
gint retval = 0;
|
||
|
||
gtk_tree_model_get (model, iter_a,
|
||
PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type_a,
|
||
PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type_a,
|
||
-1);
|
||
gtk_tree_model_get (model, iter_b,
|
||
PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type_b,
|
||
PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type_b,
|
||
-1);
|
||
|
||
/* fall back to the default order if we're not in the
|
||
* XDG part of the computer section.
|
||
*/
|
||
if ((section_type_a == section_type_b) &&
|
||
(section_type_a == SECTION_COMPUTER) &&
|
||
(place_type_a == place_type_b) &&
|
||
(place_type_a == PLACES_XDG_DIR))
|
||
{
|
||
gchar *name_a, *name_b;
|
||
|
||
gtk_tree_model_get (model, iter_a,
|
||
PLACES_SIDEBAR_COLUMN_NAME, &name_a,
|
||
-1);
|
||
gtk_tree_model_get (model, iter_b,
|
||
PLACES_SIDEBAR_COLUMN_NAME, &name_b,
|
||
-1);
|
||
|
||
retval = g_utf8_collate (name_a, name_b);
|
||
|
||
g_free (name_a);
|
||
g_free (name_b);
|
||
}
|
||
else if (place_type_a == PLACES_CONNECT_TO_SERVER)
|
||
{
|
||
retval = 1;
|
||
}
|
||
|
||
return retval;
|
||
}
|
||
|
||
static void
|
||
update_hostname (GtkPlacesSidebar *sidebar)
|
||
{
|
||
GVariant *variant;
|
||
gsize len;
|
||
const gchar *hostname;
|
||
|
||
if (sidebar->hostnamed_proxy == NULL)
|
||
return;
|
||
|
||
variant = g_dbus_proxy_get_cached_property (sidebar->hostnamed_proxy,
|
||
"PrettyHostname");
|
||
if (variant == NULL)
|
||
return;
|
||
|
||
hostname = g_variant_get_string (variant, &len);
|
||
if (len > 0 &&
|
||
g_strcmp0 (sidebar->hostname, hostname) != 0)
|
||
{
|
||
g_free (sidebar->hostname);
|
||
sidebar->hostname = g_strdup (hostname);
|
||
update_places (sidebar);
|
||
}
|
||
|
||
g_variant_unref (variant);
|
||
}
|
||
|
||
static void
|
||
hostname_proxy_new_cb (GObject *source_object,
|
||
GAsyncResult *res,
|
||
gpointer user_data)
|
||
{
|
||
GtkPlacesSidebar *sidebar = user_data;
|
||
GError *error = NULL;
|
||
GDBusProxy *proxy;
|
||
|
||
proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
|
||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||
return;
|
||
|
||
sidebar->hostnamed_proxy = proxy;
|
||
g_clear_object (&sidebar->hostnamed_cancellable);
|
||
|
||
if (error != NULL)
|
||
{
|
||
g_debug ("Failed to create D-Bus proxy: %s", error->message);
|
||
g_error_free (error);
|
||
return;
|
||
}
|
||
|
||
g_signal_connect_swapped (sidebar->hostnamed_proxy,
|
||
"g-properties-changed",
|
||
G_CALLBACK (update_hostname),
|
||
sidebar);
|
||
update_hostname (sidebar);
|
||
}
|
||
|
||
static void
|
||
create_volume_monitor (GtkPlacesSidebar *sidebar)
|
||
{
|
||
g_assert (sidebar->volume_monitor == NULL);
|
||
|
||
sidebar->volume_monitor = g_volume_monitor_get ();
|
||
|
||
g_signal_connect_object (sidebar->volume_monitor, "volume_added",
|
||
G_CALLBACK (update_places), sidebar, G_CONNECT_SWAPPED);
|
||
g_signal_connect_object (sidebar->volume_monitor, "volume_removed",
|
||
G_CALLBACK (update_places), sidebar, G_CONNECT_SWAPPED);
|
||
g_signal_connect_object (sidebar->volume_monitor, "volume_changed",
|
||
G_CALLBACK (update_places), sidebar, G_CONNECT_SWAPPED);
|
||
g_signal_connect_object (sidebar->volume_monitor, "mount_added",
|
||
G_CALLBACK (update_places), sidebar, G_CONNECT_SWAPPED);
|
||
g_signal_connect_object (sidebar->volume_monitor, "mount_removed",
|
||
G_CALLBACK (update_places), sidebar, G_CONNECT_SWAPPED);
|
||
g_signal_connect_object (sidebar->volume_monitor, "mount_changed",
|
||
G_CALLBACK (update_places), sidebar, G_CONNECT_SWAPPED);
|
||
g_signal_connect_object (sidebar->volume_monitor, "drive_disconnected",
|
||
G_CALLBACK (update_places), sidebar, G_CONNECT_SWAPPED);
|
||
g_signal_connect_object (sidebar->volume_monitor, "drive_connected",
|
||
G_CALLBACK (update_places), sidebar, G_CONNECT_SWAPPED);
|
||
g_signal_connect_object (sidebar->volume_monitor, "drive_changed",
|
||
G_CALLBACK (update_places), sidebar, G_CONNECT_SWAPPED);
|
||
}
|
||
|
||
static void
|
||
shell_shows_desktop_changed (GtkSettings *settings,
|
||
GParamSpec *pspec,
|
||
gpointer user_data)
|
||
{
|
||
GtkPlacesSidebar *sidebar = user_data;
|
||
gboolean b;
|
||
|
||
g_assert (settings == sidebar->gtk_settings);
|
||
|
||
/* Check if the user explicitly set this and, if so, don't change it. */
|
||
if (sidebar->show_desktop_set)
|
||
return;
|
||
|
||
g_object_get (settings, "gtk-shell-shows-desktop", &b, NULL);
|
||
|
||
if (b != sidebar->show_desktop)
|
||
{
|
||
sidebar->show_desktop = b;
|
||
update_places (sidebar);
|
||
g_object_notify_by_pspec (G_OBJECT (sidebar), properties[PROP_SHOW_DESKTOP]);
|
||
}
|
||
}
|
||
|
||
static gboolean
|
||
row_separator_func (GtkTreeModel *model,
|
||
GtkTreeIter *iter,
|
||
gpointer data)
|
||
{
|
||
PlaceType type;
|
||
|
||
gtk_tree_model_get (model, iter,
|
||
PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type,
|
||
-1);
|
||
|
||
return type == PLACES_HEADING;
|
||
}
|
||
|
||
static void
|
||
gtk_places_sidebar_init (GtkPlacesSidebar *sidebar)
|
||
{
|
||
GtkTreeView *tree_view;
|
||
GtkTreeViewColumn *col;
|
||
GtkCellRenderer *cell;
|
||
GtkTreeSelection *selection;
|
||
GIcon *eject;
|
||
GtkTargetList *target_list;
|
||
gboolean b;
|
||
|
||
gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (sidebar)), GTK_STYLE_CLASS_SIDEBAR);
|
||
|
||
create_volume_monitor (sidebar);
|
||
|
||
sidebar->open_flags = GTK_PLACES_OPEN_NORMAL;
|
||
|
||
sidebar->bookmarks_manager = _gtk_bookmarks_manager_new ((GtkBookmarksChangedFunc)update_places, sidebar);
|
||
|
||
sidebar->trash_monitor = _gtk_trash_monitor_get ();
|
||
sidebar->trash_monitor_changed_id = g_signal_connect_swapped (sidebar->trash_monitor, "trash-state-changed",
|
||
G_CALLBACK (update_places), sidebar);
|
||
|
||
sidebar->shortcuts = NULL;
|
||
|
||
gtk_widget_set_size_request (GTK_WIDGET (sidebar), 140, 280);
|
||
|
||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sidebar),
|
||
GTK_POLICY_NEVER,
|
||
GTK_POLICY_AUTOMATIC);
|
||
gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (sidebar), NULL);
|
||
gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (sidebar), NULL);
|
||
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sidebar), GTK_SHADOW_IN);
|
||
|
||
gtk_style_context_set_junction_sides (gtk_widget_get_style_context (GTK_WIDGET (sidebar)),
|
||
GTK_JUNCTION_RIGHT | GTK_JUNCTION_LEFT);
|
||
|
||
/* tree view */
|
||
tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
|
||
gtk_tree_view_set_headers_visible (tree_view, FALSE);
|
||
|
||
gtk_tree_view_set_row_separator_func (tree_view,
|
||
row_separator_func,
|
||
sidebar,
|
||
NULL);
|
||
|
||
col = gtk_tree_view_column_new ();
|
||
|
||
/* icon renderer */
|
||
cell = gtk_cell_renderer_pixbuf_new ();
|
||
g_object_set (cell,
|
||
"xpad", 10,
|
||
"ypad", 6,
|
||
"follow-state", TRUE,
|
||
NULL);
|
||
gtk_tree_view_column_pack_start (col, cell, FALSE);
|
||
gtk_tree_view_column_set_attributes (col, cell,
|
||
"gicon", PLACES_SIDEBAR_COLUMN_GICON,
|
||
NULL);
|
||
gtk_tree_view_column_set_cell_data_func (col, cell,
|
||
icon_cell_renderer_func,
|
||
sidebar, NULL);
|
||
|
||
/* eject text renderer */
|
||
cell = gtk_cell_renderer_text_new ();
|
||
gtk_tree_view_column_pack_start (col, cell, TRUE);
|
||
gtk_tree_view_column_set_attributes (col, cell,
|
||
"text", PLACES_SIDEBAR_COLUMN_NAME,
|
||
"visible", PLACES_SIDEBAR_COLUMN_EJECT,
|
||
NULL);
|
||
g_object_set (cell,
|
||
"ellipsize", PANGO_ELLIPSIZE_END,
|
||
"ellipsize-set", TRUE,
|
||
NULL);
|
||
|
||
/* eject icon renderer */
|
||
cell = gtk_cell_renderer_pixbuf_new ();
|
||
sidebar->eject_icon_cell_renderer = cell;
|
||
eject = g_themed_icon_new_with_default_fallbacks ("media-eject-symbolic");
|
||
g_object_set (cell,
|
||
"mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE,
|
||
"stock-size", GTK_ICON_SIZE_MENU,
|
||
"xpad", EJECT_BUTTON_XPAD,
|
||
/* align right, because for some reason gtk+ expands
|
||
this even though we tell it not to. */
|
||
"xalign", 1.0,
|
||
"follow-state", TRUE,
|
||
"gicon", eject,
|
||
NULL);
|
||
gtk_tree_view_column_pack_start (col, cell, FALSE);
|
||
gtk_tree_view_column_set_attributes (col, cell,
|
||
"visible", PLACES_SIDEBAR_COLUMN_EJECT,
|
||
NULL);
|
||
g_object_unref (eject);
|
||
|
||
/* normal text renderer */
|
||
cell = gtk_cell_renderer_text_new ();
|
||
sidebar->text_cell_renderer = cell;
|
||
gtk_tree_view_column_pack_start (col, cell, TRUE);
|
||
g_object_set (G_OBJECT (cell), "editable", FALSE, NULL);
|
||
gtk_tree_view_column_set_attributes (col, cell,
|
||
"text", PLACES_SIDEBAR_COLUMN_NAME,
|
||
"visible", PLACES_SIDEBAR_COLUMN_NO_EJECT,
|
||
"editable-set", PLACES_SIDEBAR_COLUMN_BOOKMARK,
|
||
NULL);
|
||
g_object_set (cell,
|
||
"ellipsize", PANGO_ELLIPSIZE_END,
|
||
"ellipsize-set", TRUE,
|
||
NULL);
|
||
|
||
g_signal_connect (cell, "edited",
|
||
G_CALLBACK (bookmarks_edited), sidebar);
|
||
g_signal_connect (cell, "editing-canceled",
|
||
G_CALLBACK (bookmarks_editing_canceled), sidebar);
|
||
|
||
/* this is required to align the eject buttons to the right */
|
||
gtk_tree_view_column_set_max_width (GTK_TREE_VIEW_COLUMN (col), 24);
|
||
gtk_tree_view_append_column (tree_view, col);
|
||
|
||
sidebar->store = shortcuts_model_new (sidebar);
|
||
gtk_tree_view_set_tooltip_column (tree_view, PLACES_SIDEBAR_COLUMN_TOOLTIP);
|
||
|
||
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sidebar->store),
|
||
PLACES_SIDEBAR_COLUMN_NAME,
|
||
GTK_SORT_ASCENDING);
|
||
gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sidebar->store),
|
||
PLACES_SIDEBAR_COLUMN_NAME,
|
||
places_sidebar_sort_func,
|
||
sidebar, NULL);
|
||
|
||
gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (sidebar->store));
|
||
gtk_container_add (GTK_CONTAINER (sidebar), GTK_WIDGET (tree_view));
|
||
gtk_widget_show (GTK_WIDGET (tree_view));
|
||
gtk_tree_view_set_enable_search (tree_view, FALSE);
|
||
|
||
gtk_widget_show (GTK_WIDGET (sidebar));
|
||
sidebar->tree_view = tree_view;
|
||
|
||
gtk_tree_view_set_search_column (tree_view, PLACES_SIDEBAR_COLUMN_NAME);
|
||
selection = gtk_tree_view_get_selection (tree_view);
|
||
gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
|
||
|
||
gtk_tree_selection_set_select_function (selection,
|
||
tree_selection_func,
|
||
sidebar,
|
||
NULL);
|
||
|
||
gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (tree_view),
|
||
GDK_BUTTON1_MASK,
|
||
dnd_source_targets, G_N_ELEMENTS (dnd_source_targets),
|
||
GDK_ACTION_MOVE);
|
||
gtk_drag_dest_set (GTK_WIDGET (tree_view),
|
||
0,
|
||
NULL, 0,
|
||
GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
|
||
target_list = gtk_target_list_new (dnd_drop_targets, G_N_ELEMENTS (dnd_drop_targets));
|
||
gtk_target_list_add_uri_targets (target_list, TEXT_URI_LIST);
|
||
gtk_drag_dest_set_target_list (GTK_WIDGET (tree_view), target_list);
|
||
gtk_target_list_unref (target_list);
|
||
|
||
g_signal_connect (tree_view, "key-press-event",
|
||
G_CALLBACK (bookmarks_key_press_event_cb), sidebar);
|
||
|
||
g_signal_connect (tree_view, "drag-motion",
|
||
G_CALLBACK (drag_motion_callback), sidebar);
|
||
g_signal_connect (tree_view, "drag-leave",
|
||
G_CALLBACK (drag_leave_callback), sidebar);
|
||
g_signal_connect (tree_view, "drag-data-received",
|
||
G_CALLBACK (drag_data_received_callback), sidebar);
|
||
g_signal_connect (tree_view, "drag-drop",
|
||
G_CALLBACK (drag_drop_callback), sidebar);
|
||
|
||
g_signal_connect (tree_view, "popup-menu",
|
||
G_CALLBACK (bookmarks_popup_menu_cb), sidebar);
|
||
g_signal_connect (tree_view, "button-release-event",
|
||
G_CALLBACK (bookmarks_button_release_event_cb), sidebar);
|
||
g_signal_connect (tree_view, "row-activated",
|
||
G_CALLBACK (bookmarks_row_activated_cb), sidebar);
|
||
|
||
gtk_tree_view_set_activate_on_single_click (sidebar->tree_view, TRUE);
|
||
|
||
sidebar->hostname = g_strdup (_("Computer"));
|
||
sidebar->hostnamed_cancellable = g_cancellable_new ();
|
||
g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
|
||
G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
|
||
NULL,
|
||
"org.freedesktop.hostname1",
|
||
"/org/freedesktop/hostname1",
|
||
"org.freedesktop.hostname1",
|
||
sidebar->hostnamed_cancellable,
|
||
hostname_proxy_new_cb,
|
||
sidebar);
|
||
|
||
sidebar->drop_state = DROP_STATE_NORMAL;
|
||
sidebar->new_bookmark_index = -1;
|
||
|
||
/* Don't bother trying to trace this across hierarchy changes... */
|
||
sidebar->gtk_settings = gtk_settings_get_default ();
|
||
g_signal_connect (sidebar->gtk_settings, "notify::gtk-shell-shows-desktop",
|
||
G_CALLBACK (shell_shows_desktop_changed), sidebar);
|
||
g_object_get (sidebar->gtk_settings, "gtk-shell-shows-desktop", &b, NULL);
|
||
sidebar->show_desktop = b;
|
||
|
||
/* populate the sidebar */
|
||
update_places (sidebar);
|
||
}
|
||
|
||
static void
|
||
gtk_places_sidebar_set_property (GObject *obj,
|
||
guint property_id,
|
||
const GValue *value,
|
||
GParamSpec *pspec)
|
||
{
|
||
GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (obj);
|
||
|
||
switch (property_id)
|
||
{
|
||
case PROP_LOCATION:
|
||
gtk_places_sidebar_set_location (sidebar, g_value_get_object (value));
|
||
break;
|
||
|
||
case PROP_OPEN_FLAGS:
|
||
gtk_places_sidebar_set_open_flags (sidebar, g_value_get_flags (value));
|
||
break;
|
||
|
||
case PROP_SHOW_DESKTOP:
|
||
gtk_places_sidebar_set_show_desktop (sidebar, g_value_get_boolean (value));
|
||
break;
|
||
|
||
case PROP_SHOW_CONNECT_TO_SERVER:
|
||
gtk_places_sidebar_set_show_connect_to_server (sidebar, g_value_get_boolean (value));
|
||
break;
|
||
|
||
case PROP_SHOW_ENTER_LOCATION:
|
||
gtk_places_sidebar_set_show_enter_location (sidebar, g_value_get_boolean (value));
|
||
break;
|
||
|
||
case PROP_LOCAL_ONLY:
|
||
gtk_places_sidebar_set_local_only (sidebar, g_value_get_boolean (value));
|
||
break;
|
||
|
||
default:
|
||
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void
|
||
gtk_places_sidebar_get_property (GObject *obj,
|
||
guint property_id,
|
||
GValue *value,
|
||
GParamSpec *pspec)
|
||
{
|
||
GtkPlacesSidebar *sidebar = GTK_PLACES_SIDEBAR (obj);
|
||
|
||
switch (property_id)
|
||
{
|
||
case PROP_LOCATION:
|
||
g_value_take_object (value, gtk_places_sidebar_get_location (sidebar));
|
||
break;
|
||
|
||
case PROP_OPEN_FLAGS:
|
||
g_value_set_flags (value, gtk_places_sidebar_get_open_flags (sidebar));
|
||
break;
|
||
|
||
case PROP_SHOW_DESKTOP:
|
||
g_value_set_boolean (value, gtk_places_sidebar_get_show_desktop (sidebar));
|
||
break;
|
||
|
||
case PROP_SHOW_CONNECT_TO_SERVER:
|
||
g_value_set_boolean (value, gtk_places_sidebar_get_show_connect_to_server (sidebar));
|
||
break;
|
||
|
||
case PROP_SHOW_ENTER_LOCATION:
|
||
g_value_set_boolean (value, gtk_places_sidebar_get_show_enter_location (sidebar));
|
||
break;
|
||
|
||
case PROP_LOCAL_ONLY:
|
||
g_value_set_boolean (value, gtk_places_sidebar_get_local_only (sidebar));
|
||
break;
|
||
|
||
default:
|
||
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void
|
||
gtk_places_sidebar_dispose (GObject *object)
|
||
{
|
||
GtkPlacesSidebar *sidebar;
|
||
|
||
sidebar = GTK_PLACES_SIDEBAR (object);
|
||
|
||
sidebar->tree_view = NULL;
|
||
|
||
if (sidebar->drag_leave_timeout_id)
|
||
{
|
||
g_source_remove (sidebar->drag_leave_timeout_id);
|
||
sidebar->drag_leave_timeout_id = 0;
|
||
}
|
||
|
||
free_drag_data (sidebar);
|
||
|
||
if (sidebar->bookmarks_manager != NULL)
|
||
{
|
||
_gtk_bookmarks_manager_free (sidebar->bookmarks_manager);
|
||
sidebar->bookmarks_manager = NULL;
|
||
}
|
||
|
||
if (sidebar->popup_menu)
|
||
{
|
||
gtk_widget_destroy (sidebar->popup_menu);
|
||
sidebar->popup_menu = NULL;
|
||
}
|
||
|
||
if (sidebar->trash_monitor)
|
||
{
|
||
g_signal_handler_disconnect (sidebar->trash_monitor, sidebar->trash_monitor_changed_id);
|
||
sidebar->trash_monitor_changed_id = 0;
|
||
g_clear_object (&sidebar->trash_monitor);
|
||
}
|
||
|
||
g_clear_object (&sidebar->store);
|
||
|
||
g_slist_free_full (sidebar->shortcuts, g_object_unref);
|
||
sidebar->shortcuts = NULL;
|
||
|
||
if (sidebar->volume_monitor != NULL)
|
||
{
|
||
g_signal_handlers_disconnect_by_func (sidebar->volume_monitor,
|
||
update_places, sidebar);
|
||
g_clear_object (&sidebar->volume_monitor);
|
||
}
|
||
|
||
if (sidebar->hostnamed_cancellable != NULL)
|
||
{
|
||
g_cancellable_cancel (sidebar->hostnamed_cancellable);
|
||
g_clear_object (&sidebar->hostnamed_cancellable);
|
||
}
|
||
|
||
g_clear_object (&sidebar->hostnamed_proxy);
|
||
g_free (sidebar->hostname);
|
||
sidebar->hostname = NULL;
|
||
|
||
if (sidebar->gtk_settings)
|
||
{
|
||
g_signal_handlers_disconnect_by_func (sidebar->gtk_settings, shell_shows_desktop_changed, sidebar);
|
||
sidebar->gtk_settings = NULL;
|
||
}
|
||
|
||
G_OBJECT_CLASS (gtk_places_sidebar_parent_class)->dispose (object);
|
||
}
|
||
|
||
static void
|
||
gtk_places_sidebar_class_init (GtkPlacesSidebarClass *class)
|
||
{
|
||
GObjectClass *gobject_class;
|
||
|
||
gobject_class = (GObjectClass *) class;
|
||
|
||
gobject_class->dispose = gtk_places_sidebar_dispose;
|
||
gobject_class->set_property = gtk_places_sidebar_set_property;
|
||
gobject_class->get_property = gtk_places_sidebar_get_property;
|
||
|
||
GTK_WIDGET_CLASS (class)->focus = gtk_places_sidebar_focus;
|
||
|
||
/**
|
||
* GtkPlacesSidebar::open-location:
|
||
* @sidebar: the object which received the signal.
|
||
* @location: (type Gio.File): #GFile to which the caller should switch.
|
||
* @open_flags: a single value from #GtkPlacesOpenFlags specifying how the @location should be opened.
|
||
*
|
||
* The places sidebar emits this signal when the user selects a location
|
||
* in it. The calling application should display the contents of that
|
||
* location; for example, a file manager should show a list of files in
|
||
* the specified location.
|
||
*
|
||
* Since: 3.10
|
||
*/
|
||
places_sidebar_signals [OPEN_LOCATION] =
|
||
g_signal_new (I_("open-location"),
|
||
G_OBJECT_CLASS_TYPE (gobject_class),
|
||
G_SIGNAL_RUN_FIRST,
|
||
G_STRUCT_OFFSET (GtkPlacesSidebarClass, open_location),
|
||
NULL, NULL,
|
||
_gtk_marshal_VOID__OBJECT_FLAGS,
|
||
G_TYPE_NONE, 2,
|
||
G_TYPE_OBJECT,
|
||
GTK_TYPE_PLACES_OPEN_FLAGS);
|
||
|
||
/**
|
||
* GtkPlacesSidebar::populate-popup:
|
||
* @sidebar: the object which received the signal.
|
||
* @menu: (type Gtk.Menu): a #GtkMenu.
|
||
* @selected_item: (type Gio.File) (nullable): #GFile with the item to which the menu should refer, or #NULL in the case of a @selected_volume.
|
||
* @selected_volume: (type Gio.Volume) (nullable): #GVolume if the selected item is a volume, or #NULL if it is a file.
|
||
*
|
||
* The places sidebar emits this signal when the user invokes a contextual
|
||
* menu on one of its items. In the signal handler, the application may
|
||
* add extra items to the menu as appropriate. For example, a file manager
|
||
* may want to add a "Properties" command to the menu.
|
||
*
|
||
* It is not necessary to store the @selected_item for each menu item;
|
||
* during their GtkMenuItem::activate callbacks, the application can use
|
||
* gtk_places_sidebar_get_location() to get the file to which the item
|
||
* refers.
|
||
*
|
||
* The @selected_item argument may be #NULL in case the selection refers to
|
||
* a volume. In this case, @selected_volume will be non-NULL. In this case,
|
||
* the calling application will have to g_object_ref() the @selected_volume and
|
||
* keep it around for the purposes of its menu item's "activate" callback.
|
||
*
|
||
* The @menu and all its menu items are destroyed after the user
|
||
* dismisses the menu. The menu is re-created (and thus, this signal is
|
||
* emitted) every time the user activates the contextual menu.
|
||
*
|
||
* Since: 3.10
|
||
*/
|
||
places_sidebar_signals [POPULATE_POPUP] =
|
||
g_signal_new (I_("populate-popup"),
|
||
G_OBJECT_CLASS_TYPE (gobject_class),
|
||
G_SIGNAL_RUN_FIRST,
|
||
G_STRUCT_OFFSET (GtkPlacesSidebarClass, populate_popup),
|
||
NULL, NULL,
|
||
_gtk_marshal_VOID__OBJECT_OBJECT_OBJECT,
|
||
G_TYPE_NONE, 3,
|
||
G_TYPE_OBJECT,
|
||
G_TYPE_OBJECT,
|
||
G_TYPE_OBJECT);
|
||
|
||
/**
|
||
* GtkPlacesSidebar::show-error-message:
|
||
* @sidebar: the object which received the signal.
|
||
* @primary: primary message with a summary of the error to show.
|
||
* @secondary: secondary message with details of the error to show.
|
||
*
|
||
* The places sidebar emits this signal when it needs the calling
|
||
* application to present an error message. Most of these messages
|
||
* refer to mounting or unmounting media, for example, when a drive
|
||
* cannot be started for some reason.
|
||
*
|
||
* Since: 3.10
|
||
*/
|
||
places_sidebar_signals [SHOW_ERROR_MESSAGE] =
|
||
g_signal_new (I_("show-error-message"),
|
||
G_OBJECT_CLASS_TYPE (gobject_class),
|
||
G_SIGNAL_RUN_FIRST,
|
||
G_STRUCT_OFFSET (GtkPlacesSidebarClass, show_error_message),
|
||
NULL, NULL,
|
||
_gtk_marshal_VOID__STRING_STRING,
|
||
G_TYPE_NONE, 2,
|
||
G_TYPE_STRING,
|
||
G_TYPE_STRING);
|
||
|
||
/**
|
||
* GtkPlacesSidebar::show-connect-to-server:
|
||
* @sidebar: the object which received the signal.
|
||
*
|
||
* The places sidebar emits this signal when it needs the calling
|
||
* application to present an way to connect directly to a network server.
|
||
* For example, the application may bring up a dialog box asking for
|
||
* a URL like "sftp://ftp.example.com". It is up to the application to create
|
||
* the corresponding mount by using, for example, g_file_mount_enclosing_volume().
|
||
*
|
||
* Since: 3.10
|
||
*/
|
||
places_sidebar_signals [SHOW_CONNECT_TO_SERVER] =
|
||
g_signal_new (I_("show-connect-to-server"),
|
||
G_OBJECT_CLASS_TYPE (gobject_class),
|
||
G_SIGNAL_RUN_FIRST,
|
||
G_STRUCT_OFFSET (GtkPlacesSidebarClass, show_connect_to_server),
|
||
NULL, NULL,
|
||
_gtk_marshal_VOID__VOID,
|
||
G_TYPE_NONE, 0);
|
||
|
||
/**
|
||
* GtkPlacesSidebar::show-enter-location:
|
||
* @sidebar: the object which received the signal.
|
||
*
|
||
* The places sidebar emits this signal when it needs the calling
|
||
* application to present an way to directly enter a location.
|
||
* For example, the application may bring up a dialog box asking for
|
||
* a URL like "http://http.example.com".
|
||
*
|
||
* Since: 3.14
|
||
*/
|
||
places_sidebar_signals [SHOW_ENTER_LOCATION] =
|
||
g_signal_new (I_("show-enter-location"),
|
||
G_OBJECT_CLASS_TYPE (gobject_class),
|
||
G_SIGNAL_RUN_FIRST,
|
||
G_STRUCT_OFFSET (GtkPlacesSidebarClass, show_enter_location),
|
||
NULL, NULL,
|
||
_gtk_marshal_VOID__VOID,
|
||
G_TYPE_NONE, 0);
|
||
|
||
/**
|
||
* GtkPlacesSidebar::drag-action-requested:
|
||
* @sidebar: the object which received the signal.
|
||
* @context: (type Gdk.DragContext): #GdkDragContext with information about the drag operation
|
||
* @dest_file: (type Gio.File): #GFile with the tentative location that is being hovered for a drop
|
||
* @source_file_list: (type GLib.List) (element-type GFile) (transfer none):
|
||
* List of #GFile that are being dragged
|
||
*
|
||
* When the user starts a drag-and-drop operation and the sidebar needs
|
||
* to ask the application for which drag action to perform, then the
|
||
* sidebar will emit this signal.
|
||
*
|
||
* The application can evaluate the @context for customary actions, or
|
||
* it can check the type of the files indicated by @source_file_list against the
|
||
* possible actions for the destination @dest_file.
|
||
*
|
||
* The drag action to use must be the return value of the signal handler.
|
||
*
|
||
* Returns: The drag action to use, for example, #GDK_ACTION_COPY
|
||
* or #GDK_ACTION_MOVE, or 0 if no action is allowed here (i.e. drops
|
||
* are not allowed in the specified @dest_file).
|
||
*
|
||
* Since: 3.10
|
||
*/
|
||
places_sidebar_signals [DRAG_ACTION_REQUESTED] =
|
||
g_signal_new (I_("drag-action-requested"),
|
||
G_OBJECT_CLASS_TYPE (gobject_class),
|
||
G_SIGNAL_RUN_LAST,
|
||
G_STRUCT_OFFSET (GtkPlacesSidebarClass, drag_action_requested),
|
||
NULL, NULL,
|
||
_gtk_marshal_INT__OBJECT_OBJECT_POINTER,
|
||
G_TYPE_INT, 3,
|
||
GDK_TYPE_DRAG_CONTEXT,
|
||
G_TYPE_OBJECT,
|
||
G_TYPE_POINTER /* GList of GFile */ );
|
||
|
||
/**
|
||
* GtkPlacesSidebar::drag-action-ask:
|
||
* @sidebar: the object which received the signal.
|
||
* @actions: Possible drag actions that need to be asked for.
|
||
*
|
||
* The places sidebar emits this signal when it needs to ask the application
|
||
* to pop up a menu to ask the user for which drag action to perform.
|
||
*
|
||
* Returns: the final drag action that the sidebar should pass to the drag side
|
||
* of the drag-and-drop operation.
|
||
*
|
||
* Since: 3.10
|
||
*/
|
||
places_sidebar_signals [DRAG_ACTION_ASK] =
|
||
g_signal_new (I_("drag-action-ask"),
|
||
G_OBJECT_CLASS_TYPE (gobject_class),
|
||
G_SIGNAL_RUN_LAST,
|
||
G_STRUCT_OFFSET (GtkPlacesSidebarClass, drag_action_ask),
|
||
NULL, NULL,
|
||
_gtk_marshal_INT__INT,
|
||
G_TYPE_INT, 1,
|
||
G_TYPE_INT);
|
||
|
||
/**
|
||
* GtkPlacesSidebar::drag-perform-drop:
|
||
* @sidebar: the object which received the signal.
|
||
* @dest_file: (type Gio.File): Destination #GFile.
|
||
* @source_file_list: (type GLib.List) (element-type GFile) (transfer none):
|
||
* #GList of #GFile that got dropped.
|
||
* @action: Drop action to perform.
|
||
*
|
||
* The places sidebar emits this signal when the user completes a
|
||
* drag-and-drop operation and one of the sidebar's items is the
|
||
* destination. This item is in the @dest_file, and the
|
||
* @source_file_list has the list of files that are dropped into it and
|
||
* which should be copied/moved/etc. based on the specified @action.
|
||
*
|
||
* Since: 3.10
|
||
*/
|
||
places_sidebar_signals [DRAG_PERFORM_DROP] =
|
||
g_signal_new (I_("drag-perform-drop"),
|
||
G_OBJECT_CLASS_TYPE (gobject_class),
|
||
G_SIGNAL_RUN_FIRST,
|
||
G_STRUCT_OFFSET (GtkPlacesSidebarClass, drag_perform_drop),
|
||
NULL, NULL,
|
||
_gtk_marshal_VOID__OBJECT_POINTER_INT,
|
||
G_TYPE_NONE, 3,
|
||
G_TYPE_OBJECT,
|
||
G_TYPE_POINTER, /* GList of GFile */
|
||
G_TYPE_INT);
|
||
|
||
properties[PROP_LOCATION] =
|
||
g_param_spec_object ("location",
|
||
P_("Location to Select"),
|
||
P_("The location to highlight in the sidebar"),
|
||
G_TYPE_FILE,
|
||
G_PARAM_READWRITE);
|
||
properties[PROP_OPEN_FLAGS] =
|
||
g_param_spec_flags ("open-flags",
|
||
P_("Open Flags"),
|
||
P_("Modes in which the calling application can open locations selected in the sidebar"),
|
||
GTK_TYPE_PLACES_OPEN_FLAGS,
|
||
GTK_PLACES_OPEN_NORMAL,
|
||
G_PARAM_READWRITE);
|
||
properties[PROP_SHOW_DESKTOP] =
|
||
g_param_spec_boolean ("show-desktop",
|
||
P_("Show 'Desktop'"),
|
||
P_("Whether the sidebar includes a builtin shortcut to the Desktop folder"),
|
||
TRUE,
|
||
G_PARAM_READWRITE);
|
||
properties[PROP_SHOW_CONNECT_TO_SERVER] =
|
||
g_param_spec_boolean ("show-connect-to-server",
|
||
P_("Show 'Connect to Server'"),
|
||
P_("Whether the sidebar includes a builtin shortcut to a 'Connect to server' dialog"),
|
||
FALSE,
|
||
G_PARAM_READWRITE);
|
||
properties[PROP_SHOW_ENTER_LOCATION] =
|
||
g_param_spec_boolean ("show-enter-location",
|
||
P_("Show 'Enter Location'"),
|
||
P_("Whether the sidebar includes a builtin shortcut to manually enter a location"),
|
||
FALSE,
|
||
G_PARAM_READWRITE);
|
||
properties[PROP_LOCAL_ONLY] =
|
||
g_param_spec_boolean ("local-only",
|
||
P_("Local Only"),
|
||
P_("Whether the sidebar only includes local files"),
|
||
FALSE,
|
||
G_PARAM_READWRITE);
|
||
|
||
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
|
||
}
|
||
|
||
/**
|
||
* gtk_places_sidebar_new:
|
||
*
|
||
* Creates a new #GtkPlacesSidebar widget.
|
||
*
|
||
* The application should connect to at least the
|
||
* #GtkPlacesSidebar::open-location signal to be notified
|
||
* when the user makes a selection in the sidebar.
|
||
*
|
||
* Returns: a newly created #GtkPlacesSidebar
|
||
*
|
||
* Since: 3.10
|
||
*/
|
||
GtkWidget *
|
||
gtk_places_sidebar_new (void)
|
||
{
|
||
return GTK_WIDGET (g_object_new (gtk_places_sidebar_get_type (), NULL));
|
||
}
|
||
|
||
|
||
|
||
/* Drag and drop interfaces */
|
||
|
||
/* GtkTreeDragSource::row_draggable implementation for the shortcuts model */
|
||
static gboolean
|
||
shortcuts_model_row_draggable (GtkTreeDragSource *drag_source,
|
||
GtkTreePath *path)
|
||
{
|
||
GtkTreeModel *model;
|
||
GtkTreeIter iter;
|
||
PlaceType place_type;
|
||
SectionType section_type;
|
||
|
||
model = GTK_TREE_MODEL (drag_source);
|
||
|
||
gtk_tree_model_get_iter (model, &iter, path);
|
||
gtk_tree_model_get (model, &iter,
|
||
PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type,
|
||
PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type,
|
||
-1);
|
||
|
||
if (place_type != PLACES_HEADING && section_type == SECTION_BOOKMARKS)
|
||
return TRUE;
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
/* Fill the GtkTreeDragSourceIface vtable */
|
||
static void
|
||
shortcuts_model_class_init (ShortcutsModelClass *klass)
|
||
{
|
||
}
|
||
|
||
static void
|
||
shortcuts_model_init (ShortcutsModel *model)
|
||
{
|
||
model->sidebar = NULL;
|
||
}
|
||
|
||
static void
|
||
shortcuts_model_drag_source_iface_init (GtkTreeDragSourceIface *iface)
|
||
{
|
||
iface->row_draggable = shortcuts_model_row_draggable;
|
||
}
|
||
|
||
static GtkListStore *
|
||
shortcuts_model_new (GtkPlacesSidebar *sidebar)
|
||
{
|
||
ShortcutsModel *model;
|
||
GType model_types[PLACES_SIDEBAR_COLUMN_COUNT] = {
|
||
G_TYPE_INT,
|
||
G_TYPE_STRING,
|
||
G_TYPE_DRIVE,
|
||
G_TYPE_VOLUME,
|
||
G_TYPE_MOUNT,
|
||
G_TYPE_STRING,
|
||
G_TYPE_ICON,
|
||
G_TYPE_INT,
|
||
G_TYPE_BOOLEAN,
|
||
G_TYPE_BOOLEAN,
|
||
G_TYPE_BOOLEAN,
|
||
G_TYPE_STRING,
|
||
G_TYPE_INT,
|
||
G_TYPE_STRING
|
||
};
|
||
|
||
model = g_object_new (shortcuts_model_get_type (), NULL);
|
||
model->sidebar = sidebar;
|
||
|
||
gtk_list_store_set_column_types (GTK_LIST_STORE (model),
|
||
PLACES_SIDEBAR_COLUMN_COUNT,
|
||
model_types);
|
||
|
||
return GTK_LIST_STORE (model);
|
||
}
|
||
|
||
|
||
|
||
/* Public methods for GtkPlacesSidebar */
|
||
|
||
/**
|
||
* gtk_places_sidebar_set_open_flags:
|
||
* @sidebar: a places sidebar
|
||
* @flags: Bitmask of modes in which the calling application can open locations
|
||
*
|
||
* Sets the way in which the calling application can open new locations from
|
||
* the places sidebar. For example, some applications only open locations
|
||
* “directly” into their main view, while others may support opening locations
|
||
* in a new notebook tab or a new window.
|
||
*
|
||
* This function is used to tell the places @sidebar about the ways in which the
|
||
* application can open new locations, so that the sidebar can display (or not)
|
||
* the “Open in new tab” and “Open in new window” menu items as appropriate.
|
||
*
|
||
* When the #GtkPlacesSidebar::open-location signal is emitted, its flags
|
||
* argument will be set to one of the @flags that was passed in
|
||
* gtk_places_sidebar_set_open_flags().
|
||
*
|
||
* Passing 0 for @flags will cause #GTK_PLACES_OPEN_NORMAL to always be sent
|
||
* to callbacks for the “open-location” signal.
|
||
*
|
||
* Since: 3.10
|
||
*/
|
||
void
|
||
gtk_places_sidebar_set_open_flags (GtkPlacesSidebar *sidebar,
|
||
GtkPlacesOpenFlags flags)
|
||
{
|
||
g_return_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar));
|
||
|
||
if (sidebar->open_flags != flags)
|
||
{
|
||
sidebar->open_flags = flags;
|
||
g_object_notify_by_pspec (G_OBJECT (sidebar), properties[PROP_OPEN_FLAGS]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* gtk_places_sidebar_get_open_flags:
|
||
* @sidebar: a #GtkPlacesSidebar
|
||
*
|
||
* Gets the open flags.
|
||
*
|
||
* Returns: the #GtkPlacesOpenFlags of @sidebar
|
||
*
|
||
* Since: 3.10
|
||
*/
|
||
GtkPlacesOpenFlags
|
||
gtk_places_sidebar_get_open_flags (GtkPlacesSidebar *sidebar)
|
||
{
|
||
g_return_val_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar), 0);
|
||
|
||
return sidebar->open_flags;
|
||
}
|
||
|
||
/**
|
||
* gtk_places_sidebar_set_location:
|
||
* @sidebar: a places sidebar
|
||
* @location: (allow-none): location to select, or #NULL for no current path
|
||
*
|
||
* Sets the location that is being shown in the widgets surrounding the
|
||
* @sidebar, for example, in a folder view in a file manager. In turn, the
|
||
* @sidebar will highlight that location if it is being shown in the list of
|
||
* places, or it will unhighlight everything if the @location is not among the
|
||
* places in the list.
|
||
*
|
||
* Since: 3.10
|
||
*/
|
||
void
|
||
gtk_places_sidebar_set_location (GtkPlacesSidebar *sidebar,
|
||
GFile *location)
|
||
{
|
||
GtkTreeSelection *selection;
|
||
GtkTreeIter iter;
|
||
gboolean valid;
|
||
gchar *iter_uri;
|
||
gchar *uri;
|
||
|
||
g_return_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar));
|
||
|
||
selection = gtk_tree_view_get_selection (sidebar->tree_view);
|
||
gtk_tree_selection_unselect_all (selection);
|
||
|
||
if (location == NULL)
|
||
goto out;
|
||
|
||
uri = g_file_get_uri (location);
|
||
|
||
valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (sidebar->store), &iter);
|
||
while (valid)
|
||
{
|
||
gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter,
|
||
PLACES_SIDEBAR_COLUMN_URI, &iter_uri,
|
||
-1);
|
||
if (iter_uri != NULL)
|
||
{
|
||
if (strcmp (iter_uri, uri) == 0)
|
||
{
|
||
g_free (iter_uri);
|
||
gtk_tree_selection_select_iter (selection, &iter);
|
||
break;
|
||
}
|
||
g_free (iter_uri);
|
||
}
|
||
valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (sidebar->store), &iter);
|
||
}
|
||
|
||
g_free (uri);
|
||
|
||
out:
|
||
g_object_notify_by_pspec (G_OBJECT (sidebar), properties[PROP_LOCATION]);
|
||
}
|
||
|
||
/**
|
||
* gtk_places_sidebar_get_location:
|
||
* @sidebar: a places sidebar
|
||
*
|
||
* Gets the currently-selected location in the @sidebar. This can be #NULL when
|
||
* nothing is selected, for example, when gtk_places_sidebar_set_location() has
|
||
* been called with a location that is not among the sidebar’s list of places to
|
||
* show.
|
||
*
|
||
* You can use this function to get the selection in the @sidebar. Also, if you
|
||
* connect to the #GtkPlacesSidebar::populate-popup signal, you can use this
|
||
* function to get the location that is being referred to during the callbacks
|
||
* for your menu items.
|
||
*
|
||
* Returns: (transfer full): a GFile with the selected location, or #NULL if nothing is visually
|
||
* selected.
|
||
*
|
||
* Since: 3.10
|
||
*/
|
||
GFile *
|
||
gtk_places_sidebar_get_location (GtkPlacesSidebar *sidebar)
|
||
{
|
||
GtkTreeIter iter;
|
||
GFile *file;
|
||
|
||
g_return_val_if_fail (sidebar != NULL, NULL);
|
||
|
||
file = NULL;
|
||
|
||
if (get_selected_iter (sidebar, &iter))
|
||
{
|
||
gchar *uri;
|
||
|
||
gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter,
|
||
PLACES_SIDEBAR_COLUMN_URI, &uri,
|
||
-1);
|
||
|
||
file = g_file_new_for_uri (uri);
|
||
g_free (uri);
|
||
}
|
||
|
||
return file;
|
||
}
|
||
|
||
/**
|
||
* gtk_places_sidebar_set_show_desktop:
|
||
* @sidebar: a places sidebar
|
||
* @show_desktop: whether to show an item for the Desktop folder
|
||
*
|
||
* Sets whether the @sidebar should show an item for the Desktop folder.
|
||
* The default value for this option is determined by the desktop
|
||
* environment and the user’s configuration, but this function can be
|
||
* used to override it on a per-application basis.
|
||
*
|
||
* Since: 3.10
|
||
*/
|
||
void
|
||
gtk_places_sidebar_set_show_desktop (GtkPlacesSidebar *sidebar,
|
||
gboolean show_desktop)
|
||
{
|
||
g_return_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar));
|
||
|
||
/* Don't bother disconnecting from the GtkSettings -- it will just
|
||
* complicate things. Besides, it's highly unlikely that this will
|
||
* change while we're running, but we can ignore it if it does.
|
||
*/
|
||
sidebar->show_desktop_set = TRUE;
|
||
|
||
show_desktop = !!show_desktop;
|
||
if (sidebar->show_desktop != show_desktop)
|
||
{
|
||
sidebar->show_desktop = show_desktop;
|
||
update_places (sidebar);
|
||
g_object_notify_by_pspec (G_OBJECT (sidebar), properties[PROP_SHOW_DESKTOP]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* gtk_places_sidebar_get_show_desktop:
|
||
* @sidebar: a places sidebar
|
||
*
|
||
* Returns the value previously set with gtk_places_sidebar_set_show_desktop()
|
||
*
|
||
* Returns: %TRUE if the sidebar will display a builtin shortcut to the desktop folder.
|
||
*
|
||
* Since: 3.10
|
||
*/
|
||
gboolean
|
||
gtk_places_sidebar_get_show_desktop (GtkPlacesSidebar *sidebar)
|
||
{
|
||
g_return_val_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar), FALSE);
|
||
|
||
return sidebar->show_desktop;
|
||
}
|
||
|
||
/**
|
||
* gtk_places_sidebar_set_show_connect_to_server:
|
||
* @sidebar: a places sidebar
|
||
* @show_connect_to_server: whether to show an item for the Connect to Server command
|
||
*
|
||
* Sets whether the @sidebar should show an item for connecting to a network server; this is off by default.
|
||
* An application may want to turn this on if it implements a way for the user to connect
|
||
* to network servers directly.
|
||
*
|
||
* Since: 3.10
|
||
*/
|
||
void
|
||
gtk_places_sidebar_set_show_connect_to_server (GtkPlacesSidebar *sidebar,
|
||
gboolean show_connect_to_server)
|
||
{
|
||
g_return_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar));
|
||
|
||
show_connect_to_server = !!show_connect_to_server;
|
||
if (sidebar->show_connect_to_server != show_connect_to_server)
|
||
{
|
||
sidebar->show_connect_to_server = show_connect_to_server;
|
||
update_places (sidebar);
|
||
g_object_notify_by_pspec (G_OBJECT (sidebar), properties[PROP_SHOW_CONNECT_TO_SERVER]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* gtk_places_sidebar_get_show_connect_to_server:
|
||
* @sidebar: a places sidebar
|
||
*
|
||
* Returns the value previously set with gtk_places_sidebar_set_show_connect_to_server()
|
||
*
|
||
* Returns: %TRUE if the sidebar will display a “Connect to Server” item.
|
||
*
|
||
* Since: 3.10
|
||
*/
|
||
gboolean
|
||
gtk_places_sidebar_get_show_connect_to_server (GtkPlacesSidebar *sidebar)
|
||
{
|
||
g_return_val_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar), FALSE);
|
||
|
||
return sidebar->show_connect_to_server;
|
||
}
|
||
|
||
/**
|
||
* gtk_places_sidebar_set_show_enter_location:
|
||
* @sidebar: a places sidebar
|
||
* @show_enter_location: whether to show an item for the Connect to Server command
|
||
*
|
||
* Sets whether the @sidebar should show an item for connecting to a network server; this is off by default.
|
||
* An application may want to turn this on if it implements a way for the user to connect
|
||
* to network servers directly.
|
||
*
|
||
* Since: 3.14
|
||
*/
|
||
void
|
||
gtk_places_sidebar_set_show_enter_location (GtkPlacesSidebar *sidebar,
|
||
gboolean show_enter_location)
|
||
{
|
||
g_return_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar));
|
||
|
||
show_enter_location = !!show_enter_location;
|
||
if (sidebar->show_enter_location != show_enter_location)
|
||
{
|
||
sidebar->show_enter_location = show_enter_location;
|
||
update_places (sidebar);
|
||
g_object_notify_by_pspec (G_OBJECT (sidebar), properties[PROP_SHOW_ENTER_LOCATION]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* gtk_places_sidebar_get_show_enter_location:
|
||
* @sidebar: a places sidebar
|
||
*
|
||
* Returns the value previously set with gtk_places_sidebar_set_show_enter_location()
|
||
*
|
||
* Returns: %TRUE if the sidebar will display an “Enter Location” item.
|
||
*
|
||
* Since: 3.14
|
||
*/
|
||
gboolean
|
||
gtk_places_sidebar_get_show_enter_location (GtkPlacesSidebar *sidebar)
|
||
{
|
||
g_return_val_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar), FALSE);
|
||
|
||
return sidebar->show_enter_location;
|
||
}
|
||
|
||
/**
|
||
* gtk_places_sidebar_set_local_only:
|
||
* @sidebar: a places sidebar
|
||
* @local_only: whether to show only local files
|
||
*
|
||
* Sets whether the @sidebar should only show local files.
|
||
*
|
||
* Since: 3.12
|
||
*/
|
||
void
|
||
gtk_places_sidebar_set_local_only (GtkPlacesSidebar *sidebar,
|
||
gboolean local_only)
|
||
{
|
||
g_return_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar));
|
||
|
||
local_only = !!local_only;
|
||
if (sidebar->local_only != local_only)
|
||
{
|
||
sidebar->local_only = local_only;
|
||
update_places (sidebar);
|
||
g_object_notify_by_pspec (G_OBJECT (sidebar), properties[PROP_LOCAL_ONLY]);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* gtk_places_sidebar_get_local_only:
|
||
* @sidebar: a places sidebar
|
||
*
|
||
* Returns the value previously set with gtk_places_sidebar_set_local_only().
|
||
*
|
||
* Returns: %TRUE if the sidebar will only show local files.
|
||
*
|
||
* Since: 3.12
|
||
*/
|
||
gboolean
|
||
gtk_places_sidebar_get_local_only (GtkPlacesSidebar *sidebar)
|
||
{
|
||
g_return_val_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar), FALSE);
|
||
|
||
return sidebar->local_only;
|
||
}
|
||
|
||
static GSList *
|
||
find_shortcut_link (GtkPlacesSidebar *sidebar,
|
||
GFile *location)
|
||
{
|
||
GSList *l;
|
||
|
||
for (l = sidebar->shortcuts; l; l = l->next)
|
||
{
|
||
GFile *shortcut;
|
||
|
||
shortcut = G_FILE (l->data);
|
||
if (g_file_equal (shortcut, location))
|
||
return l;
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
/**
|
||
* gtk_places_sidebar_add_shortcut:
|
||
* @sidebar: a places sidebar
|
||
* @location: location to add as an application-specific shortcut
|
||
*
|
||
* Applications may want to present some folders in the places sidebar if
|
||
* they could be immediately useful to users. For example, a drawing
|
||
* program could add a “/usr/share/clipart” location when the sidebar is
|
||
* being used in an “Insert Clipart” dialog box.
|
||
*
|
||
* This function adds the specified @location to a special place for immutable
|
||
* shortcuts. The shortcuts are application-specific; they are not shared
|
||
* across applications, and they are not persistent. If this function
|
||
* is called multiple times with different locations, then they are added
|
||
* to the sidebar’s list in the same order as the function is called.
|
||
*
|
||
* Since: 3.10
|
||
*/
|
||
void
|
||
gtk_places_sidebar_add_shortcut (GtkPlacesSidebar *sidebar,
|
||
GFile *location)
|
||
{
|
||
g_return_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar));
|
||
g_return_if_fail (G_IS_FILE (location));
|
||
|
||
g_object_ref (location);
|
||
sidebar->shortcuts = g_slist_append (sidebar->shortcuts, location);
|
||
|
||
update_places (sidebar);
|
||
}
|
||
|
||
/**
|
||
* gtk_places_sidebar_remove_shortcut:
|
||
* @sidebar: a places sidebar
|
||
* @location: location to remove
|
||
*
|
||
* Removes an application-specific shortcut that has been previously been
|
||
* inserted with gtk_places_sidebar_add_shortcut(). If the @location is not a
|
||
* shortcut in the sidebar, then nothing is done.
|
||
*
|
||
* Since: 3.10
|
||
*/
|
||
void
|
||
gtk_places_sidebar_remove_shortcut (GtkPlacesSidebar *sidebar,
|
||
GFile *location)
|
||
{
|
||
GSList *link;
|
||
GFile *shortcut;
|
||
|
||
g_return_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar));
|
||
g_return_if_fail (G_IS_FILE (location));
|
||
|
||
link = find_shortcut_link (sidebar, location);
|
||
if (!link)
|
||
return;
|
||
|
||
shortcut = G_FILE (link->data);
|
||
g_object_unref (shortcut);
|
||
|
||
sidebar->shortcuts = g_slist_delete_link (sidebar->shortcuts, link);
|
||
update_places (sidebar);
|
||
}
|
||
|
||
/**
|
||
* gtk_places_sidebar_list_shortcuts:
|
||
* @sidebar: a places sidebar
|
||
*
|
||
* Gets the list of shortcuts.
|
||
*
|
||
* Returns: (element-type GFile) (transfer full):
|
||
* A #GSList of #GFile of the locations that have been added as
|
||
* application-specific shortcuts with gtk_places_sidebar_add_shortcut().
|
||
* To free this list, you can use
|
||
* |[<!-- language="C" -->
|
||
* g_slist_free_full (list, (GDestroyNotify) g_object_unref);
|
||
* ]|
|
||
*
|
||
* Since: 3.10
|
||
*/
|
||
GSList *
|
||
gtk_places_sidebar_list_shortcuts (GtkPlacesSidebar *sidebar)
|
||
{
|
||
g_return_val_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar), FALSE);
|
||
|
||
return g_slist_copy_deep (sidebar->shortcuts, (GCopyFunc) g_object_ref, NULL);
|
||
}
|
||
|
||
/**
|
||
* gtk_places_sidebar_get_nth_bookmark:
|
||
* @sidebar: a places sidebar
|
||
* @n: index of the bookmark to query
|
||
*
|
||
* This function queries the bookmarks added by the user to the places sidebar,
|
||
* and returns one of them. This function is used by #GtkFileChooser to implement
|
||
* the “Alt-1”, “Alt-2”, etc. shortcuts, which activate the cooresponding bookmark.
|
||
*
|
||
* Returns: (transfer full): The bookmark specified by the index @n, or
|
||
* #NULL if no such index exist. Note that the indices start at 0, even though
|
||
* the file chooser starts them with the keyboard shortcut “Alt-1”.
|
||
*
|
||
* Since: 3.10
|
||
*/
|
||
GFile *
|
||
gtk_places_sidebar_get_nth_bookmark (GtkPlacesSidebar *sidebar,
|
||
gint n)
|
||
{
|
||
GtkTreeIter iter;
|
||
int k;
|
||
GFile *file;
|
||
|
||
g_return_val_if_fail (GTK_IS_PLACES_SIDEBAR (sidebar), NULL);
|
||
|
||
file = NULL;
|
||
|
||
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (sidebar->store), &iter))
|
||
{
|
||
k = 0;
|
||
|
||
do
|
||
{
|
||
PlaceType place_type;
|
||
gchar *uri;
|
||
|
||
gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter,
|
||
PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type,
|
||
PLACES_SIDEBAR_COLUMN_URI, &uri,
|
||
-1);
|
||
|
||
if (place_type == PLACES_BOOKMARK)
|
||
{
|
||
if (k == n)
|
||
{
|
||
file = g_file_new_for_uri (uri);
|
||
g_free (uri);
|
||
break;
|
||
}
|
||
g_free (uri);
|
||
k++;
|
||
}
|
||
}
|
||
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (sidebar->store), &iter));
|
||
}
|
||
|
||
return file;
|
||
}
|