提交 74b8ed77 编写于 作者: B Benjamin Pasero

Add files to Recent Files list when brought in via DND (fixes #7935)

上级 c4c07c55
......@@ -42,8 +42,13 @@ export class DelayedDragHandler {
}
}
export function extractResources(e: DragEvent, externalOnly?: boolean): URI[] {
const resources: URI[] = [];
export interface IDraggedResource {
resource: URI;
isExternal: boolean;
}
export function extractResources(e: DragEvent, externalOnly?: boolean): IDraggedResource[] {
const resources: IDraggedResource[] = [];
if (e.dataTransfer.types.length > 0) {
// Check for in-app DND
......@@ -51,7 +56,7 @@ export function extractResources(e: DragEvent, externalOnly?: boolean): URI[] {
const rawData = e.dataTransfer.getData(e.dataTransfer.types[0]);
if (rawData) {
try {
resources.push(URI.parse(rawData));
resources.push({ resource: URI.parse(rawData), isExternal: false });
} catch (error) {
// Invalid URI
}
......@@ -63,7 +68,7 @@ export function extractResources(e: DragEvent, externalOnly?: boolean): URI[] {
for (let i = 0; i < e.dataTransfer.files.length; i++) {
if (e.dataTransfer.files[i] && e.dataTransfer.files[i].path) {
try {
resources.push(URI.file(e.dataTransfer.files[i].path));
resources.push({ resource: URI.file(e.dataTransfer.files[i].path), isExternal: true });
} catch (error) {
// Invalid URI
}
......
......@@ -14,6 +14,7 @@ import { IURLService } from 'vs/platform/url/common/url';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { ParsedArgs } from 'vs/platform/environment/node/argv';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { once } from 'vs/base/common/event';
export const ID = 'launchService';
export const ILaunchService = createDecorator<ILaunchService>(ID);
......@@ -111,10 +112,9 @@ export class LaunchService implements ILaunchService {
const windowId = usedWindows[0].id;
return new TPromise<void>((c, e) => {
const unbind = this.windowsService.onClose(id => {
const onceWindowClose = once(this.windowsService.onWindowClose);
onceWindowClose(id => {
if (id === windowId) {
unbind();
c(null);
}
});
......
......@@ -5,10 +5,8 @@
'use strict';
import * as nls from 'vs/nls';
import { app, ipcMain as ipc } from 'electron';
import { assign } from 'vs/base/common/objects';
import { trim } from 'vs/base/common/strings';
import * as platform from 'vs/base/common/platform';
import { parseMainProcessArgv, ParsedArgs } from 'vs/platform/environment/node/argv';
import { mkdirp } from 'vs/base/node/pfs';
......@@ -44,7 +42,6 @@ import { ConfigurationService } from 'vs/platform/configuration/node/configurati
import { IRequestService } from 'vs/platform/request/common/request';
import { RequestService } from 'vs/platform/request/node/requestService';
import { generateUuid } from 'vs/base/common/uuid';
import { getPathLabel } from 'vs/base/common/labels';
import { IURLService } from 'vs/platform/url/common/url';
import { URLChannel } from 'vs/platform/url/common/urlIpc';
import { URLService } from 'vs/platform/url/electron-main/urlService';
......@@ -57,7 +54,6 @@ import product from 'vs/platform/product';
import pkg from 'vs/platform/package';
import * as fs from 'original-fs';
import * as cp from 'child_process';
import * as path from 'path';
function quit(accessor: ServicesAccessor, error?: Error);
function quit(accessor: ServicesAccessor, message?: string);
......@@ -245,12 +241,6 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo
const menu = instantiationService2.createInstance(VSCodeMenu);
menu.ready();
// Install JumpList on Windows (keep updated when windows open)
if (platform.isWindows) {
updateJumpList(windowsMainService, logService);
windowsMainService.onOpen(() => updateJumpList(windowsMainService, logService));
}
// Open our first window
if (environmentService.args['new-window'] && environmentService.args._.length === 0) {
windowsMainService.open({ cli: environmentService.args, forceNewWindow: true, forceEmpty: true }); // new window if "-n" was used without paths
......@@ -262,65 +252,6 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo
});
}
// TODO@Joao TODO@Ben shouldn't this be inside windows service instead?
function updateJumpList(windowsMainService: IWindowsMainService, logService: ILogService): void {
const jumpList: Electron.JumpListCategory[] = [];
// Tasks
jumpList.push({
type: 'tasks',
items: [
{
type: 'task',
title: nls.localize('newWindow', "New Window"),
description: nls.localize('newWindowDesc', "Opens a new window"),
program: process.execPath,
args: '-n', // force new window
iconPath: process.execPath,
iconIndex: 0
}
]
});
// Recent Folders
if (windowsMainService.getRecentPathsList().folders.length > 0) {
// The user might have meanwhile removed items from the jump list and we have to respect that
// so we need to update our list of recent paths with the choice of the user to not add them again
// Also: Windows will not show our custom category at all if there is any entry which was removed
// by the user! See https://github.com/Microsoft/vscode/issues/15052
windowsMainService.removeFromRecentPathsList(app.getJumpListSettings().removedItems.map(r => trim(r.args, '"')));
// Add entries
jumpList.push({
type: 'custom',
name: nls.localize('recentFolders', "Recent Folders"),
items: windowsMainService.getRecentPathsList().folders.slice(0, 7 /* limit number of entries here */).map(folder => {
return <Electron.JumpListItem>{
type: 'task',
title: path.basename(folder) || folder, // use the base name to show shorter entries in the list
description: nls.localize('folderDesc', "{0} {1}", path.basename(folder), getPathLabel(path.dirname(folder))),
program: process.execPath,
args: `"${folder}"`, // open folder (use quotes to support paths with whitespaces)
iconPath: 'explorer.exe', // simulate folder icon
iconIndex: 0
};
}).filter(i => !!i)
});
}
// Recent
jumpList.push({
type: 'recent' // this enables to show files in the "recent" category
});
try {
app.setJumpList(jumpList);
} catch (error) {
logService.log('#setJumpList', error); // since setJumpList is relatively new API, make sure to guard for errors
}
}
function setupIPC(accessor: ServicesAccessor): TPromise<Server> {
const logService = accessor.get(ILogService);
const environmentService = accessor.get(IEnvironmentService);
......
......@@ -11,7 +11,7 @@ import * as arrays from 'vs/base/common/arrays';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ipcMain as ipc, app, shell, dialog, Menu, MenuItem } from 'electron';
import { IWindowsMainService } from 'vs/code/electron-main/windows';
import { IPath, VSCodeWindow } from 'vs/code/electron-main/window';
import { VSCodeWindow } from 'vs/code/electron-main/window';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IStorageService } from 'vs/code/electron-main/storage';
import { IFilesConfiguration, AutoSaveConfiguration } from 'vs/platform/files/common/files';
......@@ -19,6 +19,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IUpdateService, State as UpdateState } from 'vs/platform/update/common/update';
import { Keybinding } from 'vs/base/common/keybinding';
import product from 'vs/platform/product';
import { RunOnceScheduler } from 'vs/base/common/async';
interface IResolvedKeybinding {
id: string;
......@@ -49,6 +50,8 @@ export class VSCodeMenu {
private isQuitting: boolean;
private appMenuInstalled: boolean;
private menuUpdater: RunOnceScheduler;
private actionIdKeybindingRequests: string[];
private mapLastKnownKeybindingToActionId: { [id: string]: string; };
private mapResolvedKeybindingToActionId: { [id: string]: string; };
......@@ -67,6 +70,8 @@ export class VSCodeMenu {
this.mapResolvedKeybindingToActionId = Object.create(null);
this.mapLastKnownKeybindingToActionId = this.storageService.getItem<{ [id: string]: string; }>(VSCodeMenu.lastKnownKeybindingsMapStorageKey) || Object.create(null);
this.menuUpdater = new RunOnceScheduler(() => this.doUpdateMenu(), 0);
this.onConfigurationUpdated(this.configurationService.getConfiguration<IConfiguration>());
}
......@@ -82,12 +87,12 @@ export class VSCodeMenu {
this.isQuitting = true;
});
// Listen to "open" & "close" event from window service
this.windowsService.onOpen(paths => this.onOpen(paths));
this.windowsService.onClose(_ => this.onClose(this.windowsService.getWindowCount()));
// Listen to some events from window service
this.windowsService.onRecentPathsChange(paths => this.updateMenu());
this.windowsService.onWindowClose(_ => this.onClose(this.windowsService.getWindowCount()));
// Resolve keybindings when any first workbench is loaded
this.windowsService.onReady(win => this.resolveKeybindings(win));
this.windowsService.onWindowReady(win => this.resolveKeybindings(win));
// Listen to resolved keybindings
ipc.on('vscode:keybindingsResolved', (event, rawKeybindings) => {
......@@ -172,6 +177,10 @@ export class VSCodeMenu {
}
private updateMenu(): void {
this.menuUpdater.schedule(); // buffer multiple attempts to update the menu
}
private doUpdateMenu(): void {
// Due to limitations in Electron, it is not possible to update menu items dynamically. The suggested
// workaround from Electron is to set the application menu again.
......@@ -187,10 +196,6 @@ export class VSCodeMenu {
}
}
private onOpen(path: IPath): void {
this.updateMenu();
}
private onClose(remainingWindowCount: number): void {
if (remainingWindowCount === 0 && platform.isMacintosh) {
this.updateMenu();
......@@ -431,7 +436,7 @@ export class VSCodeMenu {
if (folders.length || files.length) {
openRecentMenu.append(__separator__());
openRecentMenu.append(new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miClearItems', comment: ['&& denotes a mnemonic'] }, "&&Clear Items")), click: () => { this.windowsService.clearRecentPathsList(); this.updateMenu(); } }));
openRecentMenu.append(new MenuItem({ label: mnemonicLabel(nls.localize({ key: 'miClearItems', comment: ['&& denotes a mnemonic'] }, "&&Clear Items")), click: () => this.windowsService.clearRecentPathsList() }));
}
}
......@@ -442,7 +447,6 @@ export class VSCodeMenu {
const success = !!this.windowsService.open({ cli: this.environmentService.args, pathsToOpen: [path], forceNewWindow: openInNewWindow });
if (!success) {
this.windowsService.removeFromRecentPathsList(path);
this.updateMenu();
}
}
});
......
......@@ -14,7 +14,7 @@ import * as paths from 'vs/base/common/paths';
import * as types from 'vs/base/common/types';
import * as arrays from 'vs/base/common/arrays';
import { assign, mixin } from 'vs/base/common/objects';
import { EventEmitter } from 'events';
import { trim } from 'vs/base/common/strings';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IStorageService } from 'vs/code/electron-main/storage';
import { IPath, VSCodeWindow, ReadyState, IWindowConfiguration, IWindowState as ISingleWindowState, defaultWindowState, IWindowSettings } from 'vs/code/electron-main/window';
......@@ -23,19 +23,14 @@ import { IPathWithLineAndColumn, parseLineAndColumnAware } from 'vs/code/electro
import { ILifecycleService } from 'vs/code/electron-main/lifecycle';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILogService } from 'vs/code/electron-main/log';
import { getPathLabel } from 'vs/base/common/labels';
import { IWindowEventService } from 'vs/code/common/windows';
import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import CommonEvent, { Emitter } from 'vs/base/common/event';
import CommonEvent, { Emitter, once } from 'vs/base/common/event';
import product from 'vs/platform/product';
import { ParsedArgs } from 'vs/platform/environment/node/argv';
const EventTypes = {
OPEN: 'open',
CLOSE: 'close',
READY: 'ready'
};
enum WindowError {
UNRESPONSIVE,
CRASHED
......@@ -91,13 +86,12 @@ export const IWindowsMainService = createDecorator<IWindowsMainService>('windows
export interface IWindowsMainService {
_serviceBrand: any;
// TODO make proper events
// events
onOpen(clb: (path: IPath) => void): () => void;
onReady(clb: (win: VSCodeWindow) => void): () => void;
onClose(clb: (id: number) => void): () => void;
onWindowReady: CommonEvent<VSCodeWindow>;
onWindowClose: CommonEvent<number>;
onNewWindowOpen: CommonEvent<number>;
onWindowFocus: CommonEvent<number>;
onRecentPathsChange: CommonEvent<void>;
// methods
ready(initialUserEnv: platform.IProcessEnvironment): void;
......@@ -118,11 +112,13 @@ export interface IWindowsMainService {
getWindowById(windowId: number): VSCodeWindow;
getWindows(): VSCodeWindow[];
getWindowCount(): number;
addToRecentPathsList(paths: { path: string; isFile?: boolean; }[]): void;
getRecentPathsList(workspacePath?: string, filesToOpen?: IPath[]): IRecentPathsList;
removeFromRecentPathsList(path: string);
removeFromRecentPathsList(path: string[]);
clearRecentPathsList(): void;
toggleMenuBar(windowId: number): void;
updateWindowsJumpList(): void;
}
export class WindowsManager implements IWindowsMainService, IWindowEventService {
......@@ -137,7 +133,6 @@ export class WindowsManager implements IWindowsMainService, IWindowEventService
private static WINDOWS: VSCodeWindow[] = [];
private eventEmitter = new EventEmitter();
private initialUserEnv: platform.IProcessEnvironment;
private windowsState: IWindowsState;
......@@ -147,6 +142,15 @@ export class WindowsManager implements IWindowsMainService, IWindowEventService
private _onNewWindow = new Emitter<number>();
onNewWindowOpen: CommonEvent<number> = this._onNewWindow.event;
private _onRecentPathsChange = new Emitter<void>();
onRecentPathsChange: CommonEvent<void> = this._onRecentPathsChange.event;
private _onWindowReady = new Emitter<VSCodeWindow>();
onWindowReady: CommonEvent<VSCodeWindow> = this._onWindowReady.event;
private _onWindowClose = new Emitter<number>();
onWindowClose: CommonEvent<number> = this._onWindowClose.event;
constructor(
@IInstantiationService private instantiationService: IInstantiationService,
@ILogService private logService: ILogService,
......@@ -157,29 +161,13 @@ export class WindowsManager implements IWindowsMainService, IWindowEventService
@ITelemetryService private telemetryService: ITelemetryService
) { }
onOpen(clb: (path: IPath) => void): () => void {
this.eventEmitter.addListener(EventTypes.OPEN, clb);
return () => this.eventEmitter.removeListener(EventTypes.OPEN, clb);
}
onReady(clb: (win: VSCodeWindow) => void): () => void {
this.eventEmitter.addListener(EventTypes.READY, clb);
return () => this.eventEmitter.removeListener(EventTypes.READY, clb);
}
onClose(clb: (id: number) => void): () => void {
this.eventEmitter.addListener(EventTypes.CLOSE, clb);
return () => this.eventEmitter.removeListener(EventTypes.CLOSE, clb);
}
public ready(initialUserEnv: platform.IProcessEnvironment): void {
this.registerListeners();
this.initialUserEnv = initialUserEnv;
this.windowsState = this.storageService.getItem<IWindowsState>(WindowsManager.windowsStateStorageKey) || { openedFolders: [] };
this.updateWindowsJumpList();
}
private registerListeners(): void {
......@@ -223,7 +211,7 @@ export class WindowsManager implements IWindowsMainService, IWindowEventService
win.setReady();
// Event
this.eventEmitter.emit(EventTypes.READY, win);
this._onWindowReady.fire(win);
}
});
......@@ -272,7 +260,8 @@ export class WindowsManager implements IWindowsMainService, IWindowEventService
});
let loggedStartupTimes = false;
this.onReady(window => {
const onceWindowReady = once(this.onWindowReady);
onceWindowReady(window => {
if (loggedStartupTimes) {
return; // only for the first window
}
......@@ -281,6 +270,9 @@ export class WindowsManager implements IWindowsMainService, IWindowEventService
this.logStartupTimes(window);
});
// Update jump list when recent paths change
this.onRecentPathsChange(() => this.updateWindowsJumpList());
}
private logStartupTimes(window: VSCodeWindow): void {
......@@ -486,39 +478,47 @@ export class WindowsManager implements IWindowsMainService, IWindowEventService
// Remember in recent document list (unless this opens for extension development)
// Also do not add paths when files are opened for diffing, only if opened individually
if (!usedWindows.some(w => w.isPluginDevelopmentHost) && !openConfig.cli.diff) {
const recentPaths: { path: string; isFile?: boolean; }[] = [];
iPathsToOpen.forEach(iPath => {
if (iPath.filePath || iPath.workspacePath) {
app.addRecentDocument(iPath.filePath || iPath.workspacePath);
this.addToRecentPathsList(iPath.filePath || iPath.workspacePath, !!iPath.filePath);
recentPaths.push({ path: iPath.filePath || iPath.workspacePath, isFile: !!iPath.filePath });
}
});
}
// Emit events
iPathsToOpen.forEach(iPath => this.eventEmitter.emit(EventTypes.OPEN, iPath));
if (recentPaths.length) {
this.addToRecentPathsList(recentPaths);
}
}
return arrays.distinct(usedWindows);
}
private addToRecentPathsList(path?: string, isFile?: boolean): void {
if (!path) {
public addToRecentPathsList(paths: { path: string; isFile?: boolean; }[]): void {
if (!paths || !paths.length) {
return;
}
const mru = this.getRecentPathsList();
if (isFile) {
mru.files.unshift(path);
mru.files = arrays.distinct(mru.files, (f) => platform.isLinux ? f : f.toLowerCase());
} else {
mru.folders.unshift(path);
mru.folders = arrays.distinct(mru.folders, (f) => platform.isLinux ? f : f.toLowerCase());
}
paths.forEach(p => {
const {path, isFile} = p;
// Make sure its bounded
mru.folders = mru.folders.slice(0, WindowsManager.MAX_TOTAL_RECENT_ENTRIES);
mru.files = mru.files.slice(0, WindowsManager.MAX_TOTAL_RECENT_ENTRIES);
if (isFile) {
mru.files.unshift(path);
mru.files = arrays.distinct(mru.files, (f) => platform.isLinux ? f : f.toLowerCase());
} else {
mru.folders.unshift(path);
mru.folders = arrays.distinct(mru.folders, (f) => platform.isLinux ? f : f.toLowerCase());
}
// Make sure its bounded
mru.folders = mru.folders.slice(0, WindowsManager.MAX_TOTAL_RECENT_ENTRIES);
mru.files = mru.files.slice(0, WindowsManager.MAX_TOTAL_RECENT_ENTRIES);
});
this.storageService.setItem(WindowsManager.recentPathsListStorageKey, mru);
this._onRecentPathsChange.fire();
}
public removeFromRecentPathsList(path: string): void;
......@@ -550,12 +550,16 @@ export class WindowsManager implements IWindowsMainService, IWindowEventService
if (update) {
this.storageService.setItem(WindowsManager.recentPathsListStorageKey, mru);
this._onRecentPathsChange.fire();
}
}
public clearRecentPathsList(): void {
this.storageService.setItem(WindowsManager.recentPathsListStorageKey, { folders: [], files: [] });
app.clearRecentDocuments();
// Event
this._onRecentPathsChange.fire();
}
public getRecentPathsList(workspacePath?: string, filesToOpen?: IPath[]): IRecentPathsList {
......@@ -1117,7 +1121,7 @@ export class WindowsManager implements IWindowsMainService, IWindowEventService
WindowsManager.WINDOWS.splice(index, 1);
// Emit
this.eventEmitter.emit(EventTypes.CLOSE, win.id);
this._onWindowClose.fire(win.id);
}
private isPathEqual(pathA: string, pathB: string): boolean {
......@@ -1144,7 +1148,7 @@ export class WindowsManager implements IWindowsMainService, IWindowEventService
return pathA === pathB;
}
toggleMenuBar(windowId: number): void {
public toggleMenuBar(windowId: number): void {
// Update in settings
const menuBarHidden = this.storageService.getItem(VSCodeWindow.menuBarHiddenKey, false);
const newMenuBarHidden = !menuBarHidden;
......@@ -1161,4 +1165,66 @@ export class WindowsManager implements IWindowsMainService, IWindowEventService
}
}
}
}
public updateWindowsJumpList(): void {
if (!platform.isWindows) {
return; // only on windows
}
const jumpList: Electron.JumpListCategory[] = [];
// Tasks
jumpList.push({
type: 'tasks',
items: [
{
type: 'task',
title: nls.localize('newWindow', "New Window"),
description: nls.localize('newWindowDesc', "Opens a new window"),
program: process.execPath,
args: '-n', // force new window
iconPath: process.execPath,
iconIndex: 0
}
]
});
// Recent Folders
if (this.getRecentPathsList().folders.length > 0) {
// The user might have meanwhile removed items from the jump list and we have to respect that
// so we need to update our list of recent paths with the choice of the user to not add them again
// Also: Windows will not show our custom category at all if there is any entry which was removed
// by the user! See https://github.com/Microsoft/vscode/issues/15052
this.removeFromRecentPathsList(app.getJumpListSettings().removedItems.map(r => trim(r.args, '"')));
// Add entries
jumpList.push({
type: 'custom',
name: nls.localize('recentFolders', "Recent Folders"),
items: this.getRecentPathsList().folders.slice(0, 7 /* limit number of entries here */).map(folder => {
return <Electron.JumpListItem>{
type: 'task',
title: path.basename(folder) || folder, // use the base name to show shorter entries in the list
description: nls.localize('folderDesc', "{0} {1}", path.basename(folder), getPathLabel(path.dirname(folder))),
program: process.execPath,
args: `"${folder}"`, // open folder (use quotes to support paths with whitespaces)
iconPath: 'explorer.exe', // simulate folder icon
iconIndex: 0
};
}).filter(i => !!i)
});
}
// Recent
jumpList.push({
type: 'recent' // this enables to show files in the "recent" category
});
try {
app.setJumpList(jumpList);
} catch (error) {
this.logService.log('#setJumpList', error); // since setJumpList is relatively new API, make sure to guard for errors
}
}
}
\ No newline at end of file
......@@ -24,6 +24,7 @@ export interface IWindowsService {
closeFolder(windowId: number): TPromise<void>;
toggleFullScreen(windowId: number): TPromise<void>;
setRepresentedFilename(windowId: number, fileName: string): TPromise<void>;
addToRecentlyOpen(paths: { path: string, isFile?: boolean }[]): TPromise<void>;
getRecentlyOpen(windowId: number): TPromise<{ files: string[]; folders: string[]; }>;
focusWindow(windowId: number): TPromise<void>;
isMaximized(windowId: number): TPromise<boolean>;
......@@ -67,6 +68,7 @@ export interface IWindowService {
closeFolder(): TPromise<void>;
toggleFullScreen(): TPromise<void>;
setRepresentedFilename(fileName: string): TPromise<void>;
addToRecentlyOpen(paths: { path: string, isFile?: boolean }[]): TPromise<void>;
getRecentlyOpen(): TPromise<{ files: string[]; folders: string[]; }>;
focusWindow(): TPromise<void>;
setDocumentEdited(flag: boolean): TPromise<void>;
......
......@@ -18,6 +18,7 @@ export interface IWindowsChannel extends IChannel {
call(command: 'closeFolder', arg: number): TPromise<void>;
call(command: 'toggleFullScreen', arg: number): TPromise<void>;
call(command: 'setRepresentedFilename', arg: [number, string]): TPromise<void>;
call(command: 'addToRecentlyOpen', arg: { path: string, isFile?: boolean }[]): TPromise<void>;
call(command: 'getRecentlyOpen', arg: number): TPromise<{ files: string[]; folders: string[]; }>;
call(command: 'focusWindow', arg: number): TPromise<void>;
call(command: 'isMaximized', arg: number): TPromise<boolean>;
......@@ -52,6 +53,7 @@ export class WindowsChannel implements IWindowsChannel {
case 'closeFolder': return this.service.closeFolder(arg);
case 'toggleFullScreen': return this.service.toggleFullScreen(arg);
case 'setRepresentedFilename': return this.service.setRepresentedFilename(arg[0], arg[1]);
case 'addToRecentlyOpen': return this.service.addToRecentlyOpen(arg);
case 'getRecentlyOpen': return this.service.getRecentlyOpen(arg);
case 'focusWindow': return this.service.focusWindow(arg);
case 'isMaximized': return this.service.isMaximized(arg);
......@@ -114,6 +116,10 @@ export class WindowsChannelClient implements IWindowsService {
return this.channel.call('setRepresentedFilename', [windowId, fileName]);
}
addToRecentlyOpen(paths: { path: string, isFile?: boolean }[]): TPromise<void> {
return this.channel.call('addToRecentlyOpen', paths);
}
getRecentlyOpen(windowId: number): TPromise<{ files: string[]; folders: string[]; }> {
return this.channel.call('getRecentlyOpen', windowId);
}
......
......@@ -57,6 +57,10 @@ export class WindowService implements IWindowService {
return this.windowsService.setRepresentedFilename(this.windowId, fileName);
}
addToRecentlyOpen(paths: { path: string, isFile?: boolean }[]): TPromise<void> {
return this.windowsService.addToRecentlyOpen(paths);
}
getRecentlyOpen(): TPromise<{ files: string[]; folders: string[]; }> {
return this.windowsService.getRecentlyOpen(this.windowId);
}
......
......@@ -102,6 +102,12 @@ export class WindowsService implements IWindowsService {
return TPromise.as(null);
}
addToRecentlyOpen(paths: { path: string, isFile?: boolean }[]): TPromise<void> {
this.windowsMainService.addToRecentPathsList(paths);
return TPromise.as(null);
}
getRecentlyOpen(windowId: number): TPromise<{ files: string[]; folders: string[]; }> {
const vscodeWindow = this.windowsMainService.getWindowById(windowId);
......
......@@ -15,7 +15,6 @@ import { Sash, ISashEvent, IVerticalSashLayoutProvider, IHorizontalSashLayoutPro
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import DOM = require('vs/base/browser/dom');
import URI from 'vs/base/common/uri';
import errors = require('vs/base/common/errors');
import { RunOnceScheduler } from 'vs/base/common/async';
import { isMacintosh } from 'vs/base/common/platform';
......@@ -35,7 +34,7 @@ import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl';
import { NoTabsTitleControl } from 'vs/workbench/browser/parts/editor/noTabsTitleControl';
import { IEditorStacksModel, IStacksModelChangeEvent, IWorkbenchEditorConfiguration, IEditorGroup, EditorOptions, TextEditorOptions, IEditorIdentifier } from 'vs/workbench/common/editor';
import { ITitleAreaControl } from 'vs/workbench/browser/parts/editor/titleControl';
import { extractResources } from 'vs/base/browser/dnd';
import { extractResources, IDraggedResource } from 'vs/base/browser/dnd';
import { IWindowService } from 'vs/platform/windows/common/windows';
export enum Rochade {
......@@ -923,7 +922,7 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti
const stacks = this.editorGroupService.getStacksModel();
let overlay: Builder;
let draggedResources: URI[];
let draggedResources: IDraggedResource[];
function cleanUp(): void {
draggedResources = void 0;
......@@ -1005,8 +1004,21 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti
// Check for URI transfer
else {
if (droppedResources.length) {
// Add external ones to recently open list
const externalResources = droppedResources.filter(d => d.isExternal).map(d => d.resource);
if (externalResources.length) {
$this.windowService.addToRecentlyOpen(externalResources.map(resource => {
return {
path: resource.fsPath,
isFile: true
};
}));
}
// Open in Editor
$this.windowService.focusWindow()
.then(() => editorService.openEditors(droppedResources.map(resource => { return { input: { resource, options: { pinned: true } }, position: splitEditor ? freeGroup : position }; })))
.then(() => editorService.openEditors(droppedResources.map(d => { return { input: { resource: d.resource, options: { pinned: true } }, position: splitEditor ? freeGroup : position }; })))
.then(() => {
if (splitEditor && splitTo !== freeGroup) {
groupService.moveGroup(freeGroup, splitTo);
......@@ -1158,7 +1170,7 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti
// Upon first drag, detect the dragged resources and only take valid ones
if (!draggedResources) {
draggedResources = extractResources(e).filter(r => r.scheme === 'file' || r.scheme === 'untitled');
draggedResources = extractResources(e).filter(r => r.resource.scheme === 'file' || r.resource.scheme === 'untitled');
}
if (!draggedResources.length && !TitleControl.getDraggedEditor()) {
......
......@@ -573,15 +573,27 @@ export class TabsTitleControl extends TitleControl {
}
private handleExternalDrop(e: DragEvent, targetPosition: Position, targetIndex: number): void {
const resources = extractResources(e).filter(r => r.scheme === 'file' || r.scheme === 'untitled');
const resources = extractResources(e).filter(d => d.resource.scheme === 'file' || d.resource.scheme === 'untitled');
// Open resources if found
// Handle resources
if (resources.length) {
DOM.EventHelper.stop(e, true);
this.editorService.openEditors(resources.map(resource => {
// Add external ones to recently open list
const externalResources = resources.filter(d => d.isExternal).map(d => d.resource);
if (externalResources.length) {
this.windowService.addToRecentlyOpen(externalResources.map(resource => {
return {
path: resource.fsPath,
isFile: true
};
}));
}
// Open in Editor
this.editorService.openEditors(resources.map(d => {
return {
input: { resource, options: { pinned: true, index: targetIndex } },
input: { resource: d.resource, options: { pinned: true, index: targetIndex } },
position: targetPosition
};
})).then(() => {
......
......@@ -10,7 +10,7 @@ import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { stat } from 'vs/base/node/pfs';
import DOM = require('vs/base/browser/dom');
import DND = require('vs/base/browser/dnd');
import { extractResources } from 'vs/base/browser/dnd';
import { Builder, $ } from 'vs/base/browser/builder';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { asFileEditorInput } from 'vs/workbench/common/editor';
......@@ -72,7 +72,7 @@ export class ElectronWindow {
DOM.EventHelper.stop(e);
if (!draggedExternalResources) {
draggedExternalResources = DND.extractResources(e, true /* external only */);
draggedExternalResources = extractResources(e, true /* external only */).map(d => d.resource);
// Find out if folders are dragged and show the appropiate feedback then
this.includesFolder(draggedExternalResources).done(includesFolder => {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册