// ***************************************************************************** // Copyright (C) 2018 Red Hat, Inc. and others. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0. // // This Source Code may also be made available under the following Secondary // Licenses when the conditions for such availability set forth in the Eclipse // Public License v. 2.0 are satisfied: GNU General Public License, version 2 // with the GNU Classpath Exception which is available at // https://www.gnu.org/software/classpath/license.html. // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** import { interfaces } from '@theia/core/shared/inversify'; import { RPCProtocol } from '../../common/rpc-protocol'; import { OpenDialogOptionsMain, SaveDialogOptionsMain, DialogsMain, UploadDialogOptionsMain } from '../../common/plugin-api-rpc'; import { OpenFileDialogProps, SaveFileDialogProps, FileDialogService } from '@theia/filesystem/lib/browser'; import { WorkspaceService } from '@theia/workspace/lib/browser'; import URI from '@theia/core/lib/common/uri'; import { FileUploadService } from '@theia/filesystem/lib/common/upload/file-upload'; import { FileService } from '@theia/filesystem/lib/browser/file-service'; import { FileStat } from '@theia/filesystem/lib/common/files'; import { EnvVariablesServer } from '@theia/core/lib/common/env-variables'; import { nls } from '@theia/core'; export class DialogsMainImpl implements DialogsMain { private workspaceService: WorkspaceService; private fileService: FileService; private environments: EnvVariablesServer; private fileDialogService: FileDialogService; private uploadService: FileUploadService; constructor(rpc: RPCProtocol, container: interfaces.Container) { this.workspaceService = container.get(WorkspaceService); this.fileService = container.get(FileService); this.environments = container.get(EnvVariablesServer); this.fileDialogService = container.get(FileDialogService); this.uploadService = container.get(FileUploadService); } protected async getRootStat(defaultUri: string | undefined): Promise { let rootStat: FileStat | undefined; // Try to use default URI as root if (defaultUri) { try { rootStat = await this.fileService.resolve(new URI(defaultUri)); } catch { rootStat = undefined; } // Try to use as root the parent folder of existing file URI/non existing URI if (rootStat && !rootStat.isDirectory || !rootStat) { try { rootStat = await this.fileService.resolve(new URI(defaultUri).parent); } catch { rootStat = undefined; } } } // Try to use workspace service root if there is no pre-configured URI if (!rootStat) { rootStat = (await this.workspaceService.roots)[0]; } // Try to use current user home if root folder is still not taken if (!rootStat) { const homeDirUri = await this.environments.getHomeDirUri(); try { rootStat = await this.fileService.resolve(new URI(homeDirUri)); } catch { } } return rootStat; } async $showOpenDialog(options: OpenDialogOptionsMain): Promise { const rootStat = await this.getRootStat(options.defaultUri ? options.defaultUri : undefined); if (!rootStat) { throw new Error('Unable to find the rootStat'); } try { const canSelectFiles = typeof options.canSelectFiles === 'boolean' ? options.canSelectFiles : true; const canSelectFolders = typeof options.canSelectFolders === 'boolean' ? options.canSelectFolders : true; let title = options.title; if (!title) { if (canSelectFiles && canSelectFolders) { title = 'Open'; } else { if (canSelectFiles) { title = 'Open File'; } else { title = 'Open Folder'; } if (options.canSelectMany) { title += '(s)'; } } } // Create open file dialog props const dialogProps = { title: title, openLabel: options.openLabel, canSelectFiles: options.canSelectFiles, canSelectFolders: options.canSelectFolders, canSelectMany: options.canSelectMany, filters: options.filters } as OpenFileDialogProps; const result = await this.fileDialogService.showOpenDialog(dialogProps, rootStat); if (Array.isArray(result)) { return result.map(uri => uri.path.toString()); } else { return result ? [result].map(uri => uri.path.toString()) : undefined; } } catch (error) { console.error(error); } return undefined; } async $showSaveDialog(options: SaveDialogOptionsMain): Promise { const rootStat = await this.getRootStat(options.defaultUri ? options.defaultUri : undefined); // File name field should be empty unless the URI is a file let fileNameValue = ''; if (options.defaultUri) { let defaultURIStat: FileStat | undefined; try { defaultURIStat = await this.fileService.resolve(new URI(options.defaultUri)); } catch { } if (defaultURIStat && !defaultURIStat.isDirectory || !defaultURIStat) { fileNameValue = new URI(options.defaultUri).path.base; } } try { // Create save file dialog props const dialogProps = { title: options.title ?? nls.localizeByDefault('Save'), saveLabel: options.saveLabel, filters: options.filters, inputValue: fileNameValue } as SaveFileDialogProps; const result = await this.fileDialogService.showSaveDialog(dialogProps, rootStat); if (result) { return result.path.toString(); } return undefined; } catch (error) { console.error(error); } return undefined; } async $showUploadDialog(options: UploadDialogOptionsMain): Promise { const rootStat = await this.getRootStat(options.defaultUri); // Fail if root not fount if (!rootStat) { throw new Error('Failed to resolve base directory where files should be uploaded'); } const uploadResult = await this.uploadService.upload(rootStat.resource.toString()); if (uploadResult) { return uploadResult.uploaded; } return undefined; } }