| Index: chrome/browser/cocoa/shell_dialogs_mac.mm
|
| diff --git a/chrome/browser/cocoa/shell_dialogs_mac.mm b/chrome/browser/cocoa/shell_dialogs_mac.mm
|
| index c1f0b98e8bb9401fee3c712a9382e88bc77263e1..3458983aa8c93967d08637443eb6268783d64283 100644
|
| --- a/chrome/browser/cocoa/shell_dialogs_mac.mm
|
| +++ b/chrome/browser/cocoa/shell_dialogs_mac.mm
|
| @@ -1,4 +1,4 @@
|
| -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
|
| +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| @@ -14,6 +14,8 @@
|
| #include "base/scoped_cftyperef.h"
|
| #import "base/scoped_nsobject.h"
|
| #include "base/sys_string_conversions.h"
|
| +#include "chrome/browser/cocoa/constrained_window_mac.h"
|
| +#include "chrome/browser/tab_contents/tab_contents.h"
|
|
|
| static const int kFileTypePopupTag = 1234;
|
|
|
| @@ -44,6 +46,9 @@ class SelectFileDialogImpl : public SelectFileDialog {
|
| virtual bool IsRunning(gfx::NativeWindow parent_window) const;
|
| virtual void ListenerDestroyed();
|
|
|
| + // BaseShellDialog override.
|
| + virtual bool IsRunningInTab(const TabContents* parent_tab) const;
|
| +
|
| // SelectFileDialog implementation.
|
| // |params| is user data we pass back via the Listener interface.
|
| virtual void SelectFile(Type type,
|
| @@ -55,20 +60,45 @@ class SelectFileDialogImpl : public SelectFileDialog {
|
| gfx::NativeWindow owning_window,
|
| void* params);
|
|
|
| - // Callback from ObjC bridge.
|
| + // SelectFileDialog override.
|
| + // |params| is user data we pass back via the Listener interface.
|
| + virtual void SelectFileInTab(Type type,
|
| + const string16& title,
|
| + const FilePath& default_path,
|
| + const FileTypeInfo* file_types,
|
| + int file_type_index,
|
| + const FilePath::StringType& default_extension,
|
| + TabContents* owning_tab,
|
| + void* params);
|
| +
|
| + // Callbacks from ObjC bridge.
|
| void FileWasSelected(NSPanel* dialog,
|
| NSWindow* parent_window,
|
| bool was_cancelled,
|
| bool is_multi,
|
| const std::vector<FilePath>& files,
|
| int index);
|
| + void FileWasSelectedInTab(NSPanel* dialog,
|
| + TabContents* parent_tab,
|
| + bool was_cancelled,
|
| + bool was_killed,
|
| + bool is_multi,
|
| + const std::vector<FilePath>& files,
|
| + int index);
|
|
|
| struct SheetContext {
|
| Type type;
|
| - NSWindow* owning_window;
|
| + NSWindow* owning_window; // Only one of |owning_...| should be non-NULL.
|
| + TabContents* owning_tab;
|
| };
|
|
|
| private:
|
| + void NotifyListenerOfFileSelection(bool was_cancelled,
|
| + bool is_multi,
|
| + const std::vector<FilePath>& files,
|
| + int index,
|
| + void* params);
|
| +
|
| // Gets the accessory view for the save dialog.
|
| NSView* GetAccessoryView(const FileTypeInfo* file_types,
|
| int file_type_index);
|
| @@ -82,12 +112,101 @@ class SelectFileDialogImpl : public SelectFileDialog {
|
| // A map from file dialogs to the |params| user data associated with them.
|
| std::map<NSPanel*, void*> params_map_;
|
|
|
| + // A map from dialogs to constrained windows.
|
| + std::map<NSPanel*, ConstrainedWindow*> window_map_;
|
| +
|
| // The set of all parent windows for which we are currently running dialogs.
|
| - std::set<NSWindow*> parents_;
|
| + std::set<NSWindow*> parent_windows_;
|
| +
|
| + // The set of all parent tabs for which we are currently running sheets.
|
| + std::set<const TabContents*> parent_tabs_;
|
|
|
| DISALLOW_COPY_AND_ASSIGN(SelectFileDialogImpl);
|
| };
|
|
|
| +// Class to create a select file sheet as a "constrained window" (i.e., sheet
|
| +// attached to a tab).
|
| +class SelectFileInTabDelegate
|
| + : public ConstrainedWindowMacDelegateSystemSheetParams {
|
| + public:
|
| + SelectFileInTabDelegate()
|
| + : ConstrainedWindowMacDelegateSystemSheetParams(nil) { }
|
| +
|
| + void InitForSavePanel(NSSavePanel* sheet,
|
| + SelectFileDialogImpl* s,
|
| + NSString* directory,
|
| + NSString* file,
|
| + void* context) {
|
| + set_sheet(sheet);
|
| + // Note: we need NSValue's to guard against |directory|, etc. being nil.
|
| + NSArray* params = [NSArray arrayWithObjects:
|
| + [NSValue valueWithPointer:directory],
|
| + [NSValue valueWithPointer:file],
|
| + [NSNull null], // window, must be [NSNull null]
|
| + [[[SelectFileDialogBridge alloc]
|
| + initWithSelectFileDialogImpl:s] autorelease],
|
| + [NSValue valueWithPointer:@selector(endedPanel:withReturn:context:)],
|
| + [NSValue valueWithPointer:context],
|
| + nil];
|
| + set_params(params);
|
| + }
|
| +
|
| + void InitForOpenPanel(NSOpenPanel* sheet,
|
| + SelectFileDialogImpl* s,
|
| + NSString* directory,
|
| + NSString* file,
|
| + NSArray* file_types,
|
| + void* context) {
|
| + set_sheet(sheet);
|
| + // Note: we need NSValue's to guard against |directory|, etc. being nil.
|
| + NSArray* params = [NSArray arrayWithObjects:
|
| + [NSValue valueWithPointer:directory],
|
| + [NSValue valueWithPointer:file],
|
| + [NSValue valueWithPointer:file_types],
|
| + [NSNull null], // window, must be [NSNull null]
|
| + [[[SelectFileDialogBridge alloc]
|
| + initWithSelectFileDialogImpl:s] autorelease],
|
| + [NSValue valueWithPointer:@selector(endedPanel:withReturn:context:)],
|
| + [NSValue valueWithPointer:context],
|
| + nil];
|
| + set_params(params);
|
| + }
|
| +
|
| + // Implementation of method defined in ConstrainedWindowMacDelegate:
|
| + virtual void DeleteDelegate() {
|
| + if (is_sheet_open()) {
|
| + // Close sheet if it's still open; inform the end-sheet routine that it's
|
| + // being closed by the delegate, so it can avoid calling
|
| + // |CloseConstrainedWindow()| (which leads to an attempt to delete us
|
| + // again).
|
| + [NSApp endSheet:(NSSavePanel*)sheet()
|
| + returnCode:kClosedByDelegate];
|
| + }
|
| + delete this;
|
| + }
|
| +
|
| + // Overridden from ConstrainedWindowMacDelegate:
|
| + virtual bool ParentWillDo(ConstrainedWindow::Event event) {
|
| + switch(event) {
|
| + case ConstrainedWindow::kEventNavigate:
|
| + // We don't want to close! (Note: typically, we *shouldn't* be
|
| + // navigating during file selection. However, this happens for open file
|
| + // dialogs run on very new tabs.
|
| + //TODO(viettrungluu): if we allow navigations, then we should close.
|
| + return true;
|
| +
|
| + default:
|
| + break;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + // Return value passed to the end-sheet routine to indicate that the sheet was
|
| + // ended by |DeleteDelegate()|. This just needs to be some value never used by
|
| + // Apple as a return value for the sheet.
|
| + static const int kClosedByDelegate = 658042027;
|
| +};
|
| +
|
| // static
|
| SelectFileDialog* SelectFileDialog::Create(Listener* listener) {
|
| return new SelectFileDialogImpl(listener);
|
| @@ -103,7 +222,11 @@ SelectFileDialogImpl::~SelectFileDialogImpl() {
|
| }
|
|
|
| bool SelectFileDialogImpl::IsRunning(gfx::NativeWindow parent_window) const {
|
| - return parents_.find(parent_window) != parents_.end();
|
| + return (parent_windows_.find(parent_window) != parent_windows_.end());
|
| +}
|
| +
|
| +bool SelectFileDialogImpl::IsRunningInTab(const TabContents* parent_tab) const {
|
| + return (parent_tabs_.find(parent_tab) != parent_tabs_.end());
|
| }
|
|
|
| void SelectFileDialogImpl::ListenerDestroyed() {
|
| @@ -123,7 +246,8 @@ void SelectFileDialogImpl::SelectFile(
|
| type == SELECT_OPEN_FILE ||
|
| type == SELECT_OPEN_MULTI_FILE ||
|
| type == SELECT_SAVEAS_FILE);
|
| - parents_.insert(owning_window);
|
| +
|
| + parent_windows_.insert(owning_window);
|
|
|
| // Note: we need to retain the dialog as owning_window can be null.
|
| // (see http://crbug.com/29213)
|
| @@ -179,6 +303,7 @@ void SelectFileDialogImpl::SelectFile(
|
| SheetContext* context = new SheetContext;
|
| context->type = type;
|
| context->owning_window = owning_window;
|
| + context->owning_tab = NULL;
|
|
|
| if (type == SELECT_SAVEAS_FILE) {
|
| [dialog beginSheetForDirectory:default_dir
|
| @@ -208,21 +333,181 @@ void SelectFileDialogImpl::SelectFile(
|
| types:allowed_file_types
|
| modalForWindow:owning_window
|
| modalDelegate:bridge_.get()
|
| - didEndSelector:@selector(endedPanel:withReturn:context:)
|
| + didEndSelector:@selector(
|
| + endedPanel:withReturn:context:)
|
| contextInfo:context];
|
| }
|
| }
|
|
|
| -void SelectFileDialogImpl::FileWasSelected(NSPanel* dialog,
|
| - NSWindow* parent_window,
|
| - bool was_cancelled,
|
| - bool is_multi,
|
| - const std::vector<FilePath>& files,
|
| - int index) {
|
| +void SelectFileDialogImpl::SelectFileInTab(
|
| + Type type,
|
| + const string16& title,
|
| + const FilePath& default_path,
|
| + const FileTypeInfo* file_types,
|
| + int file_type_index,
|
| + const FilePath::StringType& default_extension,
|
| + TabContents* owning_tab,
|
| + void* params) {
|
| + // Go modeless if no |owning_tab|.
|
| + // TODO(viettrungluu): Despite the docs in shell_dialogs.h, we don't support
|
| + // this (see the TODO in SelectFile()).
|
| + if (!owning_tab) {
|
| + SelectFile(type, title, default_path, file_types, file_type_index,
|
| + default_extension, NULL, params);
|
| + return;
|
| + }
|
| +
|
| + // This shouldn't be needed, but prevents crashing if someone calls us when
|
| + // there's already a constrained dialog.
|
| + if (!owning_tab->CanCreateConstrainedDialog()) {
|
| + // TODO(viettrungluu): This should be a NOTREACHED(), but it's annoying to
|
| + // have my browser constantly die.
|
| + LOG(WARNING) << "Not allowed to create constrained dialog.";
|
| +
|
| + // Make sure the listener doesn't hang.
|
| + if (listener_)
|
| + listener_->FileSelectionCanceled(params);
|
| + return;
|
| + }
|
| +
|
| + DCHECK(type == SELECT_FOLDER ||
|
| + type == SELECT_OPEN_FILE ||
|
| + type == SELECT_OPEN_MULTI_FILE ||
|
| + type == SELECT_SAVEAS_FILE);
|
| + parent_tabs_.insert(owning_tab);
|
| +
|
| + NSSavePanel* dialog = (type == SELECT_SAVEAS_FILE) ? [NSSavePanel savePanel] :
|
| + [NSOpenPanel openPanel];
|
| +
|
| + if (!title.empty())
|
| + [dialog setTitle:base::SysUTF16ToNSString(title)];
|
| +
|
| + NSString* default_dir = nil;
|
| + NSString* default_filename = nil;
|
| + if (!default_path.empty()) {
|
| + default_dir = base::SysUTF8ToNSString(default_path.DirName().value());
|
| + default_filename = base::SysUTF8ToNSString(default_path.BaseName().value());
|
| + }
|
| +
|
| + NSMutableArray* allowed_file_types = nil;
|
| + if (file_types) {
|
| + if (!file_types->extensions.empty()) {
|
| + allowed_file_types = [NSMutableArray array];
|
| + for (size_t i=0; i < file_types->extensions.size(); ++i) {
|
| + const std::vector<FilePath::StringType>& ext_list =
|
| + file_types->extensions[i];
|
| + for (size_t j=0; j < ext_list.size(); ++j) {
|
| + [allowed_file_types addObject:base::SysUTF8ToNSString(ext_list[j])];
|
| + }
|
| + }
|
| + }
|
| + if (type == SELECT_SAVEAS_FILE)
|
| + [dialog setAllowedFileTypes:allowed_file_types];
|
| + // else we'll pass it in when we run the open panel
|
| +
|
| + if (file_types->include_all_files)
|
| + [dialog setAllowsOtherFileTypes:YES];
|
| +
|
| + if (!file_types->extension_description_overrides.empty()) {
|
| + NSView* accessory_view = GetAccessoryView(file_types, file_type_index);
|
| + [dialog setAccessoryView:accessory_view];
|
| + }
|
| + } else {
|
| + // If no type info is specified, anything goes.
|
| + [dialog setAllowsOtherFileTypes:YES];
|
| + }
|
| +
|
| + if (!default_extension.empty())
|
| + [dialog setRequiredFileType:base::SysUTF8ToNSString(default_extension)];
|
| +
|
| + params_map_[dialog] = params;
|
| +
|
| + SheetContext* context = new SheetContext;
|
| + context->type = type;
|
| + context->owning_window = NULL;
|
| + context->owning_tab = owning_tab;
|
| +
|
| + // It will delete itself when its |DeleteDelegate()| method is called.
|
| + SelectFileInTabDelegate* delegate = new SelectFileInTabDelegate;
|
| + DCHECK(delegate);
|
| +
|
| + if (type == SELECT_SAVEAS_FILE) {
|
| + delegate->InitForSavePanel(dialog,
|
| + this,
|
| + default_dir,
|
| + default_filename,
|
| + context);
|
| + } else {
|
| + NSOpenPanel* open_dialog = (NSOpenPanel*)dialog;
|
| +
|
| + if (type == SELECT_OPEN_MULTI_FILE)
|
| + [open_dialog setAllowsMultipleSelection:YES];
|
| + else
|
| + [open_dialog setAllowsMultipleSelection:NO];
|
| +
|
| + if (type == SELECT_FOLDER) {
|
| + [open_dialog setCanChooseFiles:NO];
|
| + [open_dialog setCanChooseDirectories:YES];
|
| + } else {
|
| + [open_dialog setCanChooseFiles:YES];
|
| + [open_dialog setCanChooseDirectories:NO];
|
| + }
|
| +
|
| + delegate->InitForOpenPanel(open_dialog,
|
| + this,
|
| + default_dir,
|
| + default_filename,
|
| + allowed_file_types,
|
| + context);
|
| + }
|
| +
|
| + window_map_[dialog] = owning_tab->CreateConstrainedDialog(delegate);
|
| +}
|
| +
|
| +void SelectFileDialogImpl::FileWasSelected(
|
| + NSPanel* dialog,
|
| + NSWindow* parent_window,
|
| + bool was_cancelled,
|
| + bool is_multi,
|
| + const std::vector<FilePath>& files,
|
| + int index) {
|
| void* params = params_map_[dialog];
|
| params_map_.erase(dialog);
|
| - parents_.erase(parent_window);
|
| + parent_windows_.erase(parent_window);
|
|
|
| + NotifyListenerOfFileSelection(was_cancelled, is_multi, files, index, params);
|
| +}
|
| +
|
| +// The |was_killed| parameter indicates whether or not we need to call
|
| +// |CloseConstrainedWindow()|. If the dialog was ended by
|
| +// |CloseConstrainedWindow()|, the delegate then closes the sheet (leading to
|
| +// this method being called) and deletes itself.
|
| +void SelectFileDialogImpl::FileWasSelectedInTab(
|
| + NSPanel* dialog,
|
| + TabContents* parent_tab,
|
| + bool was_cancelled,
|
| + bool was_killed,
|
| + bool is_multi,
|
| + const std::vector<FilePath>& files,
|
| + int index) {
|
| + void* params = params_map_[dialog];
|
| + params_map_.erase(dialog);
|
| + if (!was_killed) {
|
| + ConstrainedWindow* window = window_map_[dialog];
|
| + window->CloseConstrainedWindow();
|
| + }
|
| + window_map_.erase(dialog);
|
| + parent_tabs_.erase(parent_tab);
|
| +
|
| + NotifyListenerOfFileSelection(was_cancelled, is_multi, files, index, params);
|
| +}
|
| +
|
| +void SelectFileDialogImpl::NotifyListenerOfFileSelection(
|
| + bool was_cancelled,
|
| + bool is_multi,
|
| + const std::vector<FilePath>& files,
|
| + int index,
|
| + void* params) {
|
| if (!listener_)
|
| return;
|
|
|
| @@ -312,12 +597,15 @@ NSView* SelectFileDialogImpl::GetAccessoryView(const FileTypeInfo* file_types,
|
|
|
| SelectFileDialog::Type type = context_struct->type;
|
| NSWindow* parentWindow = context_struct->owning_window;
|
| + TabContents* parentTab = context_struct->owning_tab;
|
| + DCHECK(!(parentWindow && parentTab));
|
| delete context_struct;
|
|
|
| bool isMulti = type == SelectFileDialog::SELECT_OPEN_MULTI_FILE;
|
|
|
| std::vector<FilePath> paths;
|
| - bool did_cancel = returnCode == NSCancelButton;
|
| + // The negative check for cancellation covers the NSRun...Response codes.
|
| + bool did_cancel = (returnCode != NSOKButton);
|
| if (!did_cancel) {
|
| if (type == SelectFileDialog::SELECT_SAVEAS_FILE) {
|
| paths.push_back(FilePath(base::SysNSStringToUTF8([panel filename])));
|
| @@ -339,13 +627,27 @@ NSView* SelectFileDialogImpl::GetAccessoryView(const FileTypeInfo* file_types,
|
| }
|
| }
|
|
|
| - selectFileDialogImpl_->FileWasSelected(panel,
|
| - parentWindow,
|
| - did_cancel,
|
| - isMulti,
|
| - paths,
|
| - index);
|
| - [panel release];
|
| + // Note that |parentWindow| may be null, so we use |parentTab| to check which
|
| + // situation we're in.
|
| + if (!parentTab) {
|
| + selectFileDialogImpl_->FileWasSelected(panel,
|
| + parentWindow,
|
| + did_cancel,
|
| + isMulti,
|
| + paths,
|
| + index);
|
| + [panel release];
|
| + } else {
|
| + bool was_killed =
|
| + (returnCode == SelectFileInTabDelegate::kClosedByDelegate);
|
| + selectFileDialogImpl_->FileWasSelectedInTab(panel,
|
| + parentTab,
|
| + did_cancel,
|
| + was_killed,
|
| + isMulti,
|
| + paths,
|
| + index);
|
| + }
|
| }
|
|
|
| @end
|
|
|