2009-08-06 13:06:33 +00:00
|
|
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
|
|
|
|
|
|
|
const Big = imports.gi.Big;
|
|
|
|
const Clutter = imports.gi.Clutter;
|
|
|
|
const Pango = imports.gi.Pango;
|
|
|
|
const GLib = imports.gi.GLib;
|
|
|
|
const Gio = imports.gi.Gio;
|
|
|
|
const Shell = imports.gi.Shell;
|
|
|
|
const Lang = imports.lang;
|
|
|
|
const Mainloop = imports.mainloop;
|
|
|
|
const Signals = imports.signals;
|
|
|
|
|
2009-08-19 20:55:39 +00:00
|
|
|
const DND = imports.ui.dnd;
|
2009-08-06 13:06:33 +00:00
|
|
|
const Main = imports.ui.main;
|
|
|
|
const GenericDisplay = imports.ui.genericDisplay;
|
|
|
|
|
2009-10-01 10:18:13 +00:00
|
|
|
const NAUTILUS_PREFS_DIR = '/apps/nautilus/preferences';
|
|
|
|
const DESKTOP_IS_HOME_KEY = NAUTILUS_PREFS_DIR + '/desktop_is_home_dir';
|
|
|
|
|
2009-08-06 13:06:33 +00:00
|
|
|
const PLACES_VSPACING = 8;
|
|
|
|
const PLACES_ICON_SIZE = 16;
|
|
|
|
|
2009-08-19 20:55:39 +00:00
|
|
|
/**
|
|
|
|
* An entry in the places menu.
|
|
|
|
* @name: String title
|
|
|
|
* @iconFactory: A JavaScript callback which will create an icon texture
|
|
|
|
* @onActivate: A JavaScript callback to launch the entry
|
|
|
|
*/
|
|
|
|
function PlaceDisplay(name, iconFactory, onActivate) {
|
|
|
|
this._init(name, iconFactory, onActivate);
|
2009-08-06 13:06:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PlaceDisplay.prototype = {
|
2009-08-19 20:55:39 +00:00
|
|
|
_init : function(name, iconFactory, onActivate) {
|
2009-08-06 13:06:33 +00:00
|
|
|
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
|
|
|
reactive: true,
|
|
|
|
spacing: 4 });
|
2009-08-19 20:55:39 +00:00
|
|
|
this.actor.connect('button-release-event', Lang.bind(this, function (b, e) {
|
2009-08-06 13:06:33 +00:00
|
|
|
onActivate(this);
|
2009-08-19 20:55:39 +00:00
|
|
|
Main.overview.hide();
|
2009-08-06 13:06:33 +00:00
|
|
|
}));
|
|
|
|
let text = new Clutter.Text({ font_name: "Sans 14px",
|
|
|
|
ellipsize: Pango.EllipsizeMode.END,
|
|
|
|
color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
|
|
|
|
text: name });
|
2009-08-13 18:59:16 +00:00
|
|
|
let iconBox = new Big.Box({ y_align: Big.BoxAlignment.CENTER });
|
2009-08-19 20:55:39 +00:00
|
|
|
this._icon = iconFactory();
|
|
|
|
iconBox.append(this._icon, Big.BoxPackFlags.NONE);
|
2009-08-13 18:59:16 +00:00
|
|
|
this.actor.append(iconBox, Big.BoxPackFlags.NONE);
|
2009-08-06 13:06:33 +00:00
|
|
|
this.actor.append(text, Big.BoxPackFlags.EXPAND);
|
2009-08-19 20:55:39 +00:00
|
|
|
|
|
|
|
this._iconFactory = iconFactory;
|
|
|
|
this._onActivate = onActivate;
|
|
|
|
|
|
|
|
this.actor._delegate = this;
|
|
|
|
let draggable = DND.makeDraggable(this.actor);
|
|
|
|
},
|
|
|
|
|
|
|
|
getDragActorSource: function() {
|
|
|
|
return this._icon;
|
|
|
|
},
|
|
|
|
|
|
|
|
getDragActor: function(stageX, stageY) {
|
|
|
|
return this._iconFactory();
|
|
|
|
},
|
|
|
|
|
|
|
|
//// Drag and drop methods ////
|
|
|
|
|
|
|
|
shellWorkspaceLaunch : function() {
|
|
|
|
this._onActivate();
|
2009-08-06 13:06:33 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
Signals.addSignalMethods(PlaceDisplay.prototype);
|
|
|
|
|
|
|
|
function Places() {
|
|
|
|
this._init();
|
|
|
|
}
|
|
|
|
|
|
|
|
Places.prototype = {
|
|
|
|
_init : function() {
|
2009-10-01 12:56:59 +00:00
|
|
|
// Places is divided semi-arbitrarily into left and right; a grid would
|
|
|
|
// look better in that there would be an even number of items left+right,
|
|
|
|
// but it seems like we want some sort of differentiation between actions
|
|
|
|
// like "Connect to server..." and regular folders
|
2009-08-06 13:06:33 +00:00
|
|
|
this.actor = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL,
|
|
|
|
spacing: 4 });
|
2009-10-01 12:56:59 +00:00
|
|
|
this._leftBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL });
|
|
|
|
this.actor.append(this._leftBox, Big.BoxPackFlags.EXPAND);
|
|
|
|
this._rightBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL });
|
|
|
|
this.actor.append(this._rightBox, Big.BoxPackFlags.EXPAND);
|
|
|
|
|
|
|
|
// Subdivide left into actions and devices
|
|
|
|
this._actionsBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
|
|
|
spacing: PLACES_VSPACING });
|
|
|
|
this._leftBox.append(this._actionsBox, Big.BoxPackFlags.NONE);
|
2009-09-02 18:41:32 +00:00
|
|
|
this._devBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
|
|
|
spacing: PLACES_VSPACING });
|
2009-10-01 12:56:59 +00:00
|
|
|
this._leftBox.append(this._devBox, Big.BoxPackFlags.NONE);
|
2009-09-02 18:41:32 +00:00
|
|
|
|
2009-10-01 12:56:59 +00:00
|
|
|
// Right is bookmarks
|
2009-08-06 13:06:33 +00:00
|
|
|
this._dirsBox = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
|
|
|
|
spacing: PLACES_VSPACING });
|
2009-10-01 12:56:59 +00:00
|
|
|
this._rightBox.append(this._dirsBox, Big.BoxPackFlags.NONE);
|
2009-08-06 13:06:33 +00:00
|
|
|
|
2009-10-01 10:18:13 +00:00
|
|
|
let gconf = Shell.GConf.get_default();
|
|
|
|
gconf.watch_directory(NAUTILUS_PREFS_DIR);
|
|
|
|
|
2009-08-06 13:06:33 +00:00
|
|
|
let homeFile = Gio.file_new_for_path (GLib.get_home_dir());
|
|
|
|
let homeUri = homeFile.get_uri();
|
|
|
|
let homeLabel = Shell.util_get_label_for_uri (homeUri);
|
|
|
|
let homeIcon = Shell.util_get_icon_for_uri (homeUri);
|
2009-08-19 20:55:39 +00:00
|
|
|
let home = new PlaceDisplay(homeLabel,
|
|
|
|
function() {
|
|
|
|
return Shell.TextureCache.get_default().load_gicon(homeIcon, PLACES_ICON_SIZE);
|
|
|
|
},
|
|
|
|
function() {
|
|
|
|
Gio.app_info_launch_default_for_uri(homeUri, Main.createAppLaunchContext());
|
|
|
|
});
|
2009-08-06 13:06:33 +00:00
|
|
|
|
2009-10-01 12:56:59 +00:00
|
|
|
this._actionsBox.append(home.actor, Big.BoxPackFlags.NONE);
|
2009-08-06 13:06:33 +00:00
|
|
|
|
2009-10-01 10:18:13 +00:00
|
|
|
let desktopPath = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
|
|
|
|
let desktopFile = Gio.file_new_for_path (desktopPath);
|
|
|
|
let desktopUri = desktopFile.get_uri();
|
|
|
|
let desktopLabel = Shell.util_get_label_for_uri (desktopUri);
|
|
|
|
let desktopIcon = Shell.util_get_icon_for_uri (desktopUri);
|
|
|
|
this._desktopMenu = new PlaceDisplay(desktopLabel,
|
|
|
|
function() {
|
|
|
|
return Shell.TextureCache.get_default().load_gicon(desktopIcon, PLACES_ICON_SIZE);
|
|
|
|
},
|
|
|
|
function() {
|
|
|
|
Gio.app_info_launch_default_for_uri(desktopUri, Main.createAppLaunchContext());
|
|
|
|
});
|
|
|
|
this._actionsBox.append(this._desktopMenu.actor, Big.BoxPackFlags.NONE);
|
|
|
|
this._updateDesktopMenuVisibility();
|
|
|
|
gconf.connect('changed::' + DESKTOP_IS_HOME_KEY, Lang.bind(this, this._updateDesktopMenuVisibility));
|
|
|
|
|
2009-09-02 18:41:32 +00:00
|
|
|
/*
|
|
|
|
* Show devices, code more or less ported from nautilus-places-sidebar.c
|
|
|
|
*/
|
|
|
|
this._volumeMonitor = Gio.VolumeMonitor.get();
|
|
|
|
this._volumeMonitor.connect('volume-added', Lang.bind(this, this._updateDevices));
|
|
|
|
this._volumeMonitor.connect('volume-removed',Lang.bind(this, this._updateDevices));
|
|
|
|
this._volumeMonitor.connect('volume-changed', Lang.bind(this, this._updateDevices));
|
|
|
|
this._volumeMonitor.connect('mount-added', Lang.bind(this, this._updateDevices));
|
|
|
|
this._volumeMonitor.connect('mount-removed', Lang.bind(this, this._updateDevices));
|
|
|
|
this._volumeMonitor.connect('mount-changed', Lang.bind(this, this._updateDevices));
|
|
|
|
this._volumeMonitor.connect('drive-connected', Lang.bind(this, this._updateDevices));
|
|
|
|
this._volumeMonitor.connect('drive-disconnected', Lang.bind(this, this._updateDevices));
|
|
|
|
this._volumeMonitor.connect('drive-changed', Lang.bind(this, this._updateDevices));
|
|
|
|
this._updateDevices();
|
|
|
|
|
2009-08-09 16:32:22 +00:00
|
|
|
let networkApp = null;
|
|
|
|
try {
|
|
|
|
networkApp = Shell.AppSystem.get_default().load_from_desktop_file('gnome-network-scheme.desktop');
|
|
|
|
} catch(e) {
|
2009-08-09 17:00:00 +00:00
|
|
|
try {
|
|
|
|
networkApp = Shell.AppSystem.get_default().load_from_desktop_file('network-scheme.desktop');
|
|
|
|
} catch(e) {
|
|
|
|
log("Cannot create \"Network\" item, .desktop file not found or corrupt.");
|
|
|
|
}
|
2009-08-09 16:32:22 +00:00
|
|
|
}
|
|
|
|
|
2009-08-09 15:39:17 +00:00
|
|
|
if (networkApp != null) {
|
2009-08-19 20:55:39 +00:00
|
|
|
let network = new PlaceDisplay(networkApp.get_name(),
|
|
|
|
function() {
|
|
|
|
return networkApp.create_icon_texture(PLACES_ICON_SIZE);
|
|
|
|
},
|
|
|
|
function () {
|
|
|
|
networkApp.launch();
|
|
|
|
});
|
2009-10-01 12:56:59 +00:00
|
|
|
this._actionsBox.append(network.actor, Big.BoxPackFlags.NONE);
|
2009-08-09 15:39:17 +00:00
|
|
|
}
|
2009-08-06 13:06:33 +00:00
|
|
|
|
2009-08-19 20:55:39 +00:00
|
|
|
let connect = new PlaceDisplay('Connect to...',
|
|
|
|
function () {
|
|
|
|
return Shell.TextureCache.get_default().load_icon_name("applications-internet", PLACES_ICON_SIZE);
|
|
|
|
},
|
|
|
|
function () {
|
|
|
|
new Shell.Process({ args: ['nautilus-connect-server'] }).run();
|
|
|
|
});
|
2009-10-01 12:56:59 +00:00
|
|
|
this._actionsBox.append(connect.actor, Big.BoxPackFlags.NONE);
|
2009-08-06 13:06:33 +00:00
|
|
|
|
|
|
|
this._bookmarksPath = GLib.build_filenamev([GLib.get_home_dir(), ".gtk-bookmarks"]);
|
|
|
|
this._bookmarksFile = Gio.file_new_for_path(this._bookmarksPath);
|
|
|
|
let monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null);
|
2009-08-12 16:29:33 +00:00
|
|
|
this._bookmarkTimeoutId = 0;
|
2009-08-06 13:06:33 +00:00
|
|
|
monitor.connect('changed', Lang.bind(this, function () {
|
2009-08-12 16:29:33 +00:00
|
|
|
if (this._bookmarkTimeoutId > 0)
|
2009-08-06 13:06:33 +00:00
|
|
|
return;
|
|
|
|
/* Defensive event compression */
|
2009-08-12 16:29:33 +00:00
|
|
|
this._bookmarkTimeoutId = Mainloop.timeout_add(100, Lang.bind(this, function () {
|
|
|
|
this._bookmarkTimeoutId = 0;
|
2009-08-06 13:06:33 +00:00
|
|
|
this._reloadBookmarks();
|
|
|
|
return false;
|
|
|
|
}));
|
|
|
|
}));
|
|
|
|
|
|
|
|
this._reloadBookmarks();
|
|
|
|
},
|
|
|
|
|
|
|
|
_reloadBookmarks: function() {
|
|
|
|
|
|
|
|
this._dirsBox.remove_all();
|
|
|
|
|
2009-08-20 20:39:04 +00:00
|
|
|
if (!GLib.file_test(this._bookmarksPath, GLib.FileTest.EXISTS))
|
|
|
|
return;
|
|
|
|
|
2009-08-06 13:06:33 +00:00
|
|
|
let [success, bookmarksContent, len] = GLib.file_get_contents(this._bookmarksPath);
|
|
|
|
|
|
|
|
if (!success)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let bookmarks = bookmarksContent.split('\n');
|
|
|
|
|
|
|
|
let bookmarksToLabel = {};
|
|
|
|
let bookmarksOrder = [];
|
|
|
|
for (let i = 0; i < bookmarks.length; i++) {
|
|
|
|
let bookmarkLine = bookmarks[i];
|
|
|
|
let components = bookmarkLine.split(' ');
|
|
|
|
let bookmark = components[0];
|
|
|
|
if (bookmark in bookmarksToLabel)
|
|
|
|
continue;
|
|
|
|
let label = null;
|
|
|
|
if (components.length > 1)
|
2009-08-09 17:19:07 +00:00
|
|
|
label = components.slice(1).join(' ');
|
2009-08-06 13:06:33 +00:00
|
|
|
bookmarksToLabel[bookmark] = label;
|
|
|
|
bookmarksOrder.push(bookmark);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let i = 0; i < bookmarksOrder.length; i++) {
|
|
|
|
let bookmark = bookmarksOrder[i];
|
|
|
|
let label = bookmarksToLabel[bookmark];
|
|
|
|
let file = Gio.file_new_for_uri(bookmark);
|
|
|
|
if (!file.query_exists(null))
|
|
|
|
continue;
|
|
|
|
if (label == null)
|
|
|
|
label = Shell.util_get_label_for_uri(bookmark);
|
|
|
|
if (label == null)
|
|
|
|
continue;
|
|
|
|
let icon = Shell.util_get_icon_for_uri(bookmark);
|
2009-08-19 20:55:39 +00:00
|
|
|
|
|
|
|
let item = new PlaceDisplay(label,
|
|
|
|
function() {
|
|
|
|
return Shell.TextureCache.get_default().load_gicon(icon, PLACES_ICON_SIZE);
|
|
|
|
},
|
|
|
|
function() {
|
|
|
|
Gio.app_info_launch_default_for_uri(bookmark, Main.createAppLaunchContext());
|
|
|
|
});
|
2009-08-06 13:06:33 +00:00
|
|
|
this._dirsBox.append(item.actor, Big.BoxPackFlags.NONE);
|
|
|
|
}
|
2009-09-02 18:41:32 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
_updateDevices: function() {
|
|
|
|
this._devBox.remove_all();
|
|
|
|
|
|
|
|
/* first go through all connected drives */
|
|
|
|
let drives = this._volumeMonitor.get_connected_drives();
|
|
|
|
for (let i = 0; i < drives.length; i++) {
|
|
|
|
let volumes = drives[i].get_volumes();
|
|
|
|
for(let j = 0; j < volumes.length; j++) {
|
|
|
|
let mount = volumes[j].get_mount();
|
|
|
|
if(mount != null) {
|
|
|
|
this._addMount(mount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add all volumes that is not associated with a drive */
|
|
|
|
let volumes = this._volumeMonitor.get_volumes();
|
|
|
|
for(let i = 0; i < volumes.length; i++) {
|
|
|
|
if(volumes[i].get_drive() != null)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
let mount = volumes[i].get_mount();
|
|
|
|
if(mount != null) {
|
|
|
|
this._addMount(mount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add mounts that have no volume (/etc/mtab mounts, ftp, sftp,...) */
|
|
|
|
let mounts = this._volumeMonitor.get_mounts();
|
|
|
|
for(let i = 0; i < mounts.length; i++) {
|
|
|
|
if(mounts[i].is_shadowed())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if(mounts[i].get_volume())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
this._addMount(mounts[i]);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_addMount: function(mount) {
|
|
|
|
let mountLabel = mount.get_name();
|
|
|
|
let mountIcon = mount.get_icon();
|
|
|
|
let root = mount.get_root();
|
|
|
|
let mountUri = root.get_uri();
|
|
|
|
let devItem = new PlaceDisplay(mountLabel,
|
|
|
|
function() {
|
|
|
|
return Shell.TextureCache.get_default().load_gicon(mountIcon, PLACES_ICON_SIZE);
|
|
|
|
},
|
|
|
|
function() {
|
|
|
|
Gio.app_info_launch_default_for_uri(mountUri, Main.createAppLaunchContext());
|
|
|
|
});
|
|
|
|
this._devBox.append(devItem.actor, Big.BoxPackFlags.NONE);
|
2009-10-01 10:18:13 +00:00
|
|
|
},
|
2009-09-02 18:41:32 +00:00
|
|
|
|
2009-10-01 10:18:13 +00:00
|
|
|
_updateDesktopMenuVisibility: function() {
|
|
|
|
let gconf = Shell.GConf.get_default();
|
|
|
|
let desktopIsHome = gconf.get_boolean(DESKTOP_IS_HOME_KEY);
|
|
|
|
if (desktopIsHome)
|
|
|
|
this._desktopMenu.actor.hide();
|
|
|
|
else
|
|
|
|
this._desktopMenu.actor.show();
|
|
|
|
}
|
2009-08-06 13:06:33 +00:00
|
|
|
};
|
|
|
|
Signals.addSignalMethods(Places.prototype);
|