summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorerg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-19 19:30:22 +0000
committererg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-07-19 19:30:22 +0000
commit0e5603f8db3f1b072ec88a64cc314168497f8036 (patch)
tree97f9fae4cf3a7d5b34eff0d9cc5d6927d646ec8d /ui
parentb5780e67d5f23190ff85fdfc841eec2df142a560 (diff)
downloadchromium_src-0e5603f8db3f1b072ec88a64cc314168497f8036.zip
chromium_src-0e5603f8db3f1b072ec88a64cc314168497f8036.tar.gz
chromium_src-0e5603f8db3f1b072ec88a64cc314168497f8036.tar.bz2
Shell dialogs: move the android/mac implementations.
BUG=134529 TEST=none Review URL: https://chromiumcodereview.appspot.com/10804013 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@147508 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r--ui/base/dialogs/select_file_dialog.cc13
-rw-r--r--ui/base/dialogs/select_file_dialog_mac.h19
-rw-r--r--ui/base/dialogs/select_file_dialog_mac.mm442
-rw-r--r--ui/base/strings/ui_strings.grd6
-rw-r--r--ui/ui.gyp3
5 files changed, 479 insertions, 4 deletions
diff --git a/ui/base/dialogs/select_file_dialog.cc b/ui/base/dialogs/select_file_dialog.cc
index 7f0d6dd..3cd9b22 100644
--- a/ui/base/dialogs/select_file_dialog.cc
+++ b/ui/base/dialogs/select_file_dialog.cc
@@ -14,6 +14,8 @@
#if defined(OS_WIN)
#include "ui/base/dialogs/select_file_dialog_win.h"
+#elif defined(OS_MACOSX)
+#include "ui/base/dialogs/select_file_dialog_mac.h"
#endif
namespace {
@@ -52,10 +54,7 @@ void SelectFileDialog::SetFactory(ui::SelectFileDialogFactory* factory) {
dialog_factory_ = factory;
}
-// TODO(erg): As each implementation moves into ui/base/dialogs, consolidate
-// the Create() methods into the following single method, which has to check
-// all options.
-#if defined(USE_AURA) || defined(OS_WIN)
+#if !defined(TOOLKIT_GTK)
// static
SelectFileDialog* SelectFileDialog::Create(Listener* listener,
ui::SelectFilePolicy* policy) {
@@ -70,7 +69,13 @@ SelectFileDialog* SelectFileDialog::Create(Listener* listener,
// TODO(erg): Add other OSs one by one here.
#if defined(OS_WIN) && !defined(USE_AURA)
+ // TODO(port): The windows people need this to work in aura, too.
return CreateWinSelectFileDialog(listener, policy);
+#elif defined(OS_MACOSX) && !defined(USE_AURA)
+ return CreateMacSelectFileDialog(listener, policy);
+#elif defined(OS_ANDROID)
+ // see crbug.com/116131 to track implemenation of SelectFileDialog
+ NOTIMPLEMENTED();
#endif
return NULL;
diff --git a/ui/base/dialogs/select_file_dialog_mac.h b/ui/base/dialogs/select_file_dialog_mac.h
new file mode 100644
index 0000000..e346f41
--- /dev/null
+++ b/ui/base/dialogs/select_file_dialog_mac.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2012 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.
+
+#ifndef UI_BASE_DIALOGS_SELECT_FILE_DIALOG_MAC_H_
+#define UI_BASE_DIALOGS_SELECT_FILE_DIALOG_MAC_H_
+
+#include "ui/base/dialogs/select_file_dialog.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace ui {
+
+SelectFileDialog* CreateMacSelectFileDialog(
+ SelectFileDialog::Listener* listener,
+ SelectFilePolicy* policy);
+
+} // namespace ui
+
+#endif // UI_BASE_DIALOGS_SELECT_FILE_DIALOG_MAC_H_
diff --git a/ui/base/dialogs/select_file_dialog_mac.mm b/ui/base/dialogs/select_file_dialog_mac.mm
new file mode 100644
index 0000000..33f205e
--- /dev/null
+++ b/ui/base/dialogs/select_file_dialog_mac.mm
@@ -0,0 +1,442 @@
+// Copyright (c) 2012 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.
+
+#include "ui/base/dialogs/select_file_dialog.h"
+
+#import <Cocoa/Cocoa.h>
+#include <CoreServices/CoreServices.h>
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/mac/bundle_locations.h"
+#import "base/mac/cocoa_protocols.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/mac_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#import "base/memory/scoped_nsobject.h"
+#include "base/sys_string_conversions.h"
+#include "base/threading/thread_restrictions.h"
+#include "grit/ui_strings.h"
+#import "ui/base/cocoa/nib_loading.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+
+namespace {
+
+const int kFileTypePopupTag = 1234;
+
+CFStringRef CreateUTIFromExtension(const FilePath::StringType& ext) {
+ base::mac::ScopedCFTypeRef<CFStringRef> ext_cf(
+ base::SysUTF8ToCFStringRef(ext));
+ return UTTypeCreatePreferredIdentifierForTag(
+ kUTTagClassFilenameExtension, ext_cf.get(), NULL);
+}
+
+} // namespace
+
+class SelectFileDialogImpl;
+
+// A bridge class to act as the modal delegate to the save/open sheet and send
+// the results to the C++ class.
+@interface SelectFileDialogBridge : NSObject<NSOpenSavePanelDelegate> {
+ @private
+ SelectFileDialogImpl* selectFileDialogImpl_; // WEAK; owns us
+}
+
+- (id)initWithSelectFileDialogImpl:(SelectFileDialogImpl*)s;
+- (void)endedPanel:(NSSavePanel*)panel
+ withReturn:(int)returnCode
+ context:(void*)context;
+
+// NSSavePanel delegate method
+- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename;
+
+@end
+
+// Implementation of SelectFileDialog that shows Cocoa dialogs for choosing a
+// file or folder.
+class SelectFileDialogImpl : public ui::SelectFileDialog {
+ public:
+ explicit SelectFileDialogImpl(Listener* listener,
+ ui::SelectFilePolicy* policy);
+
+ // BaseShellDialog implementation.
+ virtual bool IsRunning(gfx::NativeWindow parent_window) const;
+ virtual void ListenerDestroyed();
+
+ // Callback from ObjC bridge.
+ void FileWasSelected(NSSavePanel* dialog,
+ NSWindow* parent_window,
+ bool was_cancelled,
+ bool is_multi,
+ const std::vector<FilePath>& files,
+ int index);
+
+ bool ShouldEnableFilename(NSSavePanel* dialog, NSString* filename);
+
+ struct SheetContext {
+ Type type;
+ NSWindow* owning_window;
+ };
+
+ protected:
+ // SelectFileDialog implementation.
+ // |params| is user data we pass back via the Listener interface.
+ virtual void SelectFileImpl(Type type,
+ const string16& title,
+ const FilePath& default_path,
+ const FileTypeInfo* file_types,
+ int file_type_index,
+ const FilePath::StringType& default_extension,
+ gfx::NativeWindow owning_window,
+ void* params) OVERRIDE;
+
+ private:
+ virtual ~SelectFileDialogImpl();
+
+ // Gets the accessory view for the save dialog.
+ NSView* GetAccessoryView(const FileTypeInfo* file_types,
+ int file_type_index);
+
+ virtual bool HasMultipleFileTypeChoicesImpl();
+
+ // The bridge for results from Cocoa to return to us.
+ scoped_nsobject<SelectFileDialogBridge> bridge_;
+
+ // A map from file dialogs to the |params| user data associated with them.
+ std::map<NSSavePanel*, void*> params_map_;
+
+ // The set of all parent windows for which we are currently running dialogs.
+ std::set<NSWindow*> parents_;
+
+ // A map from file dialogs to their types.
+ std::map<NSSavePanel*, Type> type_map_;
+
+ bool hasMultipleFileTypeChoices_;
+
+ DISALLOW_COPY_AND_ASSIGN(SelectFileDialogImpl);
+};
+
+SelectFileDialogImpl::SelectFileDialogImpl(Listener* listener,
+ ui::SelectFilePolicy* policy)
+ : SelectFileDialog(listener, policy),
+ bridge_([[SelectFileDialogBridge alloc]
+ initWithSelectFileDialogImpl:this]) {
+}
+
+bool SelectFileDialogImpl::IsRunning(gfx::NativeWindow parent_window) const {
+ return parents_.find(parent_window) != parents_.end();
+}
+
+void SelectFileDialogImpl::ListenerDestroyed() {
+ listener_ = NULL;
+}
+
+void SelectFileDialogImpl::FileWasSelected(NSSavePanel* 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);
+ type_map_.erase(dialog);
+
+ [dialog setDelegate:nil];
+
+ if (!listener_)
+ return;
+
+ if (was_cancelled) {
+ listener_->FileSelectionCanceled(params);
+ } else {
+ if (is_multi) {
+ listener_->MultiFilesSelected(files, params);
+ } else {
+ listener_->FileSelected(files[0], index, params);
+ }
+ }
+}
+
+bool SelectFileDialogImpl::ShouldEnableFilename(NSSavePanel* dialog,
+ NSString* filename) {
+ // If this is a single open file dialog, disable selecting packages.
+ if (type_map_[dialog] != SELECT_OPEN_FILE)
+ return true;
+
+ return ![[NSWorkspace sharedWorkspace] isFilePackageAtPath:filename];
+}
+
+void SelectFileDialogImpl::SelectFileImpl(
+ Type type,
+ const string16& title,
+ const FilePath& default_path,
+ const FileTypeInfo* file_types,
+ int file_type_index,
+ const FilePath::StringType& default_extension,
+ gfx::NativeWindow owning_window,
+ void* params) {
+ DCHECK(type == SELECT_FOLDER ||
+ type == SELECT_OPEN_FILE ||
+ type == SELECT_OPEN_MULTI_FILE ||
+ type == SELECT_SAVEAS_FILE);
+ parents_.insert(owning_window);
+
+ // Note: we need to retain the dialog as owning_window can be null.
+ // (See http://crbug.com/29213 .)
+ NSSavePanel* dialog;
+ if (type == SELECT_SAVEAS_FILE)
+ dialog = [[NSSavePanel savePanel] retain];
+ else
+ dialog = [[NSOpenPanel openPanel] retain];
+
+ if (!title.empty())
+ [dialog setTitle:base::SysUTF16ToNSString(title)];
+
+ NSString* default_dir = nil;
+ NSString* default_filename = nil;
+ if (!default_path.empty()) {
+ // The file dialog is going to do a ton of stats anyway. Not much
+ // point in eliminating this one.
+ base::ThreadRestrictions::ScopedAllowIO allow_io;
+ if (file_util::DirectoryExists(default_path)) {
+ default_dir = base::SysUTF8ToNSString(default_path.value());
+ } else {
+ default_dir = base::SysUTF8ToNSString(default_path.DirName().value());
+ default_filename =
+ base::SysUTF8ToNSString(default_path.BaseName().value());
+ }
+ }
+
+ NSArray* allowed_file_types = nil;
+ if (file_types) {
+ if (!file_types->extensions.empty()) {
+ // While the example given in the header for FileTypeInfo lists an example
+ // |file_types->extensions| value as
+ // { { "htm", "html" }, { "txt" } }
+ // it is not always the case that the given extensions in one of the sub-
+ // lists are all synonyms. In fact, in the case of a <select> element with
+ // multiple "accept" types, all the extensions allowed for all the types
+ // will be part of one list. To be safe, allow the types of all the
+ // specified extensions.
+ NSMutableSet* file_type_set = [NSMutableSet set];
+ 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) {
+ base::mac::ScopedCFTypeRef<CFStringRef> uti(
+ CreateUTIFromExtension(ext_list[j]));
+ [file_type_set addObject:base::mac::CFToNSCast(uti.get())];
+ }
+ }
+ allowed_file_types = [file_type_set allObjects];
+ }
+ 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.size() > 1) {
+ 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];
+ }
+ hasMultipleFileTypeChoices_ =
+ file_types ? file_types->extensions.size() > 1 : true;
+
+ if (!default_extension.empty())
+ [dialog setRequiredFileType:base::SysUTF8ToNSString(default_extension)];
+
+ params_map_[dialog] = params;
+ type_map_[dialog] = type;
+
+ SheetContext* context = new SheetContext;
+
+ // |context| should never be NULL, but we are seeing indications otherwise.
+ // This CHECK is here to confirm if we are actually getting NULL
+ // |context|s. http://crbug.com/58959
+ CHECK(context);
+ context->type = type;
+ context->owning_window = owning_window;
+
+ if (type == SELECT_SAVEAS_FILE) {
+ [dialog setCanSelectHiddenExtension:YES];
+ [dialog beginSheetForDirectory:default_dir
+ file:default_filename
+ modalForWindow:owning_window
+ modalDelegate:bridge_.get()
+ didEndSelector:@selector(endedPanel:withReturn:context:)
+ contextInfo: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];
+ [open_dialog setCanCreateDirectories:YES];
+ NSString *prompt = l10n_util::GetNSString(IDS_SELECT_FOLDER_BUTTON_TITLE);
+ [open_dialog setPrompt:prompt];
+ } else {
+ [open_dialog setCanChooseFiles:YES];
+ [open_dialog setCanChooseDirectories:NO];
+ }
+
+ [open_dialog setDelegate:bridge_.get()];
+ [open_dialog beginSheetForDirectory:default_dir
+ file:default_filename
+ types:allowed_file_types
+ modalForWindow:owning_window
+ modalDelegate:bridge_.get()
+ didEndSelector:@selector(endedPanel:withReturn:context:)
+ contextInfo:context];
+ }
+}
+
+SelectFileDialogImpl::~SelectFileDialogImpl() {
+ // Walk through the open dialogs and close them all. Use a temporary vector
+ // to hold the pointers, since we can't delete from the map as we're iterating
+ // through it.
+ std::vector<NSSavePanel*> panels;
+ for (std::map<NSSavePanel*, void*>::iterator it = params_map_.begin();
+ it != params_map_.end(); ++it) {
+ panels.push_back(it->first);
+ }
+
+ for (std::vector<NSSavePanel*>::iterator it = panels.begin();
+ it != panels.end(); ++it) {
+ [*it cancel:*it];
+ }
+}
+
+NSView* SelectFileDialogImpl::GetAccessoryView(const FileTypeInfo* file_types,
+ int file_type_index) {
+ DCHECK(file_types);
+ NSView* accessory_view = ui::GetViewFromNib(@"SaveAccessoryView");
+ if (!accessory_view)
+ return nil;
+
+ NSPopUpButton* popup = [accessory_view viewWithTag:kFileTypePopupTag];
+ DCHECK(popup);
+
+ size_t type_count = file_types->extensions.size();
+ for (size_t type = 0; type < type_count; ++type) {
+ NSString* type_description;
+ if (type < file_types->extension_description_overrides.size()) {
+ type_description = base::SysUTF16ToNSString(
+ file_types->extension_description_overrides[type]);
+ } else {
+ // No description given for a list of extensions; pick the first one from
+ // the list (arbitrarily) and use its description.
+ const std::vector<FilePath::StringType>& ext_list =
+ file_types->extensions[type];
+ DCHECK(!ext_list.empty());
+ base::mac::ScopedCFTypeRef<CFStringRef> uti(
+ CreateUTIFromExtension(ext_list[0]));
+ base::mac::ScopedCFTypeRef<CFStringRef> description(
+ UTTypeCopyDescription(uti.get()));
+
+ type_description =
+ [[base::mac::CFToNSCast(description.get()) retain] autorelease];
+ }
+ [popup addItemWithTitle:type_description];
+ }
+
+ [popup selectItemAtIndex:file_type_index - 1]; // 1-based
+ return accessory_view;
+}
+
+bool SelectFileDialogImpl::HasMultipleFileTypeChoicesImpl() {
+ return hasMultipleFileTypeChoices_;
+}
+
+@implementation SelectFileDialogBridge
+
+- (id)initWithSelectFileDialogImpl:(SelectFileDialogImpl*)s {
+ self = [super init];
+ if (self != nil) {
+ selectFileDialogImpl_ = s;
+ }
+ return self;
+}
+
+- (void)endedPanel:(NSSavePanel*)panel
+ withReturn:(int)returnCode
+ context:(void*)context {
+ // |context| should never be NULL, but we are seeing indications otherwise.
+ // This CHECK is here to confirm if we are actually getting NULL
+ // |context|s. http://crbug.com/58959
+ CHECK(context);
+
+ int index = 0;
+ SelectFileDialogImpl::SheetContext* context_struct =
+ (SelectFileDialogImpl::SheetContext*)context;
+
+ ui::SelectFileDialog::Type type = context_struct->type;
+ NSWindow* parentWindow = context_struct->owning_window;
+ delete context_struct;
+
+ bool isMulti = type == ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
+
+ std::vector<FilePath> paths;
+ bool did_cancel = returnCode == NSCancelButton;
+ if (!did_cancel) {
+ if (type == ui::SelectFileDialog::SELECT_SAVEAS_FILE) {
+ paths.push_back(FilePath(base::SysNSStringToUTF8([panel filename])));
+
+ NSView* accessoryView = [panel accessoryView];
+ if (accessoryView) {
+ NSPopUpButton* popup = [accessoryView viewWithTag:kFileTypePopupTag];
+ if (popup) {
+ // File type indexes are 1-based.
+ index = [popup indexOfSelectedItem] + 1;
+ }
+ } else {
+ index = 1;
+ }
+ } else {
+ CHECK([panel isKindOfClass:[NSOpenPanel class]]);
+ NSArray* filenames = [static_cast<NSOpenPanel*>(panel) filenames];
+ for (NSString* filename in filenames)
+ paths.push_back(FilePath(base::SysNSStringToUTF8(filename)));
+ }
+ }
+
+ selectFileDialogImpl_->FileWasSelected(panel,
+ parentWindow,
+ did_cancel,
+ isMulti,
+ paths,
+ index);
+ [panel release];
+}
+
+- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename {
+ return selectFileDialogImpl_->ShouldEnableFilename(sender, filename);
+}
+
+@end
+
+namespace ui {
+
+SelectFileDialog* CreateMacSelectFileDialog(
+ SelectFileDialog::Listener* listener,
+ SelectFilePolicy* policy) {
+ return new SelectFileDialogImpl(listener, policy);
+}
+
+} // namespace ui
diff --git a/ui/base/strings/ui_strings.grd b/ui/base/strings/ui_strings.grd
index 96be92e..f0e6f6c 100644
--- a/ui/base/strings/ui_strings.grd
+++ b/ui/base/strings/ui_strings.grd
@@ -194,6 +194,12 @@ need to be translated for each locale.-->
<ph name="SAVEAS_EXTENSION_TYPE">$1<ex>EXE</ex></ph> File (.<ph name="SAVEAS_EXTENSION_NAME">$2<ex>exe</ex></ph>)
</message>
+ <if expr="is_macosx">
+ <message name="IDS_SELECT_FOLDER_BUTTON_TITLE" desc="The name of the Select button in the folder selection dialog.">
+ Select
+ </message>
+ </if>
+
<!--Accessible name/action strings-->
<message name="IDS_APP_ACCACTION_PRESS" desc="The accessible default action for a button.">
Press
diff --git a/ui/ui.gyp b/ui/ui.gyp
index 7ff7f36..dabb377 100644
--- a/ui/ui.gyp
+++ b/ui/ui.gyp
@@ -115,6 +115,8 @@
'base/dialogs/select_file_dialog.h',
'base/dialogs/select_file_dialog_factory.cc',
'base/dialogs/select_file_dialog_factory.h',
+ 'base/dialogs/select_file_dialog_mac.h',
+ 'base/dialogs/select_file_dialog_mac.mm',
'base/dialogs/select_file_dialog_win.cc',
'base/dialogs/select_file_dialog_win.h',
'base/dialogs/select_file_policy.cc',
@@ -472,6 +474,7 @@
['exclude', 'gfx/gtk_util.h'],
['exclude', 'gfx/screen_gtk.cc'],
['exclude', 'gfx/screen_win.cc'],
+ ['exclude', 'base/dialogs/select_file_dialog_mac.mm'],
['exclude', 'base/dialogs/select_file_dialog_win.cc'],
['exclude', 'base/dragdrop/drag_utils_win.cc'],
['exclude', 'base/win/mouse_wheel_util.cc'],