Chromium Code Reviews
|
| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include <gdk/gdkx.h> | |
| 6 #include <gtk/gtk.h> | |
| 7 #include <map> | |
| 8 #include <set> | |
| 9 | |
| 10 #include "base/command_line.h" | |
| 11 #include "base/file_util.h" | |
| 12 #include "base/logging.h" | |
| 13 #include "base/message_loop.h" | |
| 14 #include "base/mime_util.h" | |
| 15 #include "base/process_util.h" | |
| 16 #include "base/string_number_conversions.h" | |
| 17 #include "base/string_util.h" | |
| 18 #include "base/sys_string_conversions.h" | |
| 19 #include "base/threading/thread.h" | |
| 20 #include "base/threading/thread_restrictions.h" | |
| 21 #include "base/utf_string_conversions.h" | |
| 22 #include "chrome/browser/ui/gtk/dialogs_common.h" | |
| 23 #include "chrome/browser/ui/shell_dialogs.h" | |
| 24 #include "content/browser/browser_thread.h" | |
| 25 #include "grit/generated_resources.h" | |
| 26 #include "ui/base/l10n/l10n_util.h" | |
| 27 | |
| 28 // Implementation of SelectFileDialog that shows a KDE common dialog for | |
| 29 // choosing a file or folder. This acts as a modal dialog. | |
| 30 class SelectFileDialogImplKDE : public SelectFileDialogImpl { | |
| 31 public: | |
| 32 explicit SelectFileDialogImplKDE(Listener* listener); | |
| 33 ~SelectFileDialogImplKDE(); | |
| 34 | |
| 35 protected: | |
| 36 // SelectFileDialog implementation. | |
| 37 // |params| is user data we pass back via the Listener interface. | |
| 38 virtual void SelectFileImpl(Type type, | |
| 39 const string16& title, | |
| 40 const FilePath& default_path, | |
| 41 const FileTypeInfo* file_types, | |
| 42 int file_type_index, | |
| 43 const FilePath::StringType& default_extension, | |
| 44 gfx::NativeWindow owning_window, | |
| 45 void* params); | |
| 46 | |
| 47 private: | |
| 48 // Get the filters from |file_types_| and concatenate them into | |
| 49 // |filter_string|. | |
| 50 std::string GetMimeTypeFilterString(); | |
| 51 | |
| 52 // Get KDialog command line representing the Argv array for KDialog. | |
| 53 void GetKDialogCommandLine(const std::string& type, const std::string& title, | |
| 54 const FilePath& default_path, gfx::NativeWindow parent, | |
| 55 bool file_operation, bool multiple_selection, CommandLine* command_line); | |
| 56 | |
| 57 // Call KDialog on the FILE thread and post results back to the UI thread. | |
| 58 void CallKDialogOutput(const CommandLine& command_line, void* params, | |
| 59 gfx::NativeWindow parent, | |
| 60 void (SelectFileDialogImplKDE::*callback)(const std::string&, | |
| 61 int, void*)); | |
| 62 | |
| 63 // Notifies the listener that a single file was chosen. | |
| 64 void FileSelected(const FilePath& path, void* params); | |
| 65 | |
| 66 // Notifies the listener that multiple files were chosen. | |
| 67 void MultiFilesSelected(const std::vector<FilePath>& files, void* params); | |
| 68 | |
| 69 // Notifies the listener that no file was chosen (the action was canceled). | |
| 70 // Dialog is passed so we can find that |params| pointer that was passed to | |
| 71 // us when we were told to show the dialog. | |
| 72 void FileNotSelected(void *params); | |
| 73 | |
| 74 void CreateSelectFolderDialog(const std::string& title, | |
| 75 const FilePath& default_path, | |
| 76 gfx::NativeWindow parent, void* params); | |
| 77 | |
| 78 void CreateFileOpenDialog(const std::string& title, | |
| 79 const FilePath& default_path, | |
| 80 gfx::NativeWindow parent, void* params); | |
| 81 | |
| 82 void CreateMultiFileOpenDialog(const std::string& title, | |
| 83 const FilePath& default_path, | |
| 84 gfx::NativeWindow parent, void* params); | |
| 85 | |
| 86 void CreateSaveAsDialog(const std::string& title, | |
| 87 const FilePath& default_path, | |
| 88 gfx::NativeWindow parent, void* params); | |
| 89 | |
| 90 // Common function for OnSelectSingleFileDialogResponse and | |
| 91 // OnSelectSingleFolderDialogResponse. | |
| 92 void SelectSingleFileHelper(const std::string& output, int exit_code, | |
| 93 void* params, bool allow_folder); | |
| 94 | |
| 95 void OnSelectSingleFileDialogResponse(const std::string& output, | |
| 96 int exit_code, void* params); | |
| 97 void OnSelectMultiFileDialogResponse(const std::string& output, | |
| 98 int exit_code, void* params); | |
| 99 void OnSelectSingleFolderDialogResponse(const std::string& output, | |
| 100 int exit_code, void* params); | |
| 101 | |
| 102 DISALLOW_COPY_AND_ASSIGN(SelectFileDialogImplKDE); | |
| 103 }; | |
| 104 | |
| 105 SelectFileDialogImpl* SelectFileDialogImpl::NewSelectFileDialogImplKDE( | |
| 106 Listener* listener) { | |
| 107 return new SelectFileDialogImplKDE(listener); | |
| 108 } | |
| 109 | |
| 110 SelectFileDialogImplKDE::SelectFileDialogImplKDE(Listener* listener) | |
| 111 : SelectFileDialogImpl(listener) { | |
| 112 } | |
| 113 | |
| 114 SelectFileDialogImplKDE::~SelectFileDialogImplKDE() { | |
| 115 } | |
| 116 | |
| 117 // We ignore |default_extension|. | |
| 118 void SelectFileDialogImplKDE::SelectFileImpl( | |
| 119 Type type, | |
| 120 const string16& title, | |
| 121 const FilePath& default_path, | |
| 122 const FileTypeInfo* file_types, | |
| 123 int file_type_index, | |
| 124 const FilePath::StringType& default_extension, | |
| 125 gfx::NativeWindow owning_window, | |
| 126 void* params) { | |
| 127 type_ = type; | |
| 128 // |owning_window| can be null when user right-clicks on a downloadable item | |
| 129 // and chooses 'Open Link in New Tab' when 'Ask where to save each file | |
| 130 // before downloading.' preference is turned on. (http://crbug.com/29213) | |
| 131 if (owning_window) | |
| 132 parents_.insert(owning_window); | |
| 133 | |
| 134 std::string title_string = UTF16ToUTF8(title); | |
| 135 | |
| 136 file_type_index_ = file_type_index; | |
| 137 if (file_types) | |
| 138 file_types_ = *file_types; | |
| 139 else | |
| 140 file_types_.include_all_files = true; | |
| 141 | |
| 142 switch (type) { | |
| 143 case SELECT_FOLDER: | |
| 144 CreateSelectFolderDialog(title_string, default_path, | |
| 145 owning_window, params); | |
| 146 return; | |
| 147 case SELECT_OPEN_FILE: | |
| 148 CreateFileOpenDialog(title_string, default_path, owning_window, | |
| 149 params); | |
| 150 return; | |
| 151 case SELECT_OPEN_MULTI_FILE: | |
| 152 CreateMultiFileOpenDialog(title_string, default_path, | |
| 153 owning_window, params); | |
| 154 return; | |
| 155 case SELECT_SAVEAS_FILE: | |
| 156 CreateSaveAsDialog(title_string, default_path, owning_window, | |
| 157 params); | |
| 158 return; | |
| 159 default: | |
| 160 NOTREACHED(); | |
| 161 return; | |
| 162 } | |
| 163 } | |
| 164 | |
| 165 std::string SelectFileDialogImplKDE::GetMimeTypeFilterString() { | |
| 166 std::string filter_string; | |
| 167 // We need a filter set because the same mime type can appear multiple times. | |
| 168 std::set<std::string> filter_set; | |
| 169 for (size_t i = 0; i < file_types_.extensions.size(); ++i) { | |
| 170 for (size_t j = 0; j < file_types_.extensions[i].size(); ++j) { | |
| 171 if (!file_types_.extensions[i][j].empty()) { | |
| 172 // Allow IO in the file dialog. http://crbug.com/72637 | |
| 173 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
|
Mike Mammarella
2011/09/14 06:42:24
It looks like the reason you need this is that Get
dfilimon
2011/09/14 22:06:08
Right, I've uploaded a change incorporating your o
| |
| 174 std::string mime_type = mime_util::GetFileMimeType( | |
| 175 FilePath("name").ReplaceExtension(file_types_.extensions[i][j])); | |
| 176 filter_set.insert(mime_type); | |
| 177 } | |
| 178 } | |
| 179 } | |
| 180 // Add the *.* filter, but only if we have added other filters (otherwise it | |
| 181 // is implied). | |
| 182 if (file_types_.include_all_files && !file_types_.extensions.empty()) | |
| 183 filter_set.insert("application/octet-stream"); | |
| 184 // Create the final output string. | |
| 185 filter_string.clear(); | |
| 186 for (std::set<std::string>::iterator it = filter_set.begin(), | |
| 187 e = filter_set.end(); it != e; ++it) { | |
|
Mike Mammarella
2011/09/14 06:42:24
This loop construct took me a moment to figure out
dfilimon
2011/09/14 22:06:08
I removed it for clarity.
| |
| 188 filter_string.append(*it + " "); | |
| 189 } | |
| 190 return filter_string; | |
| 191 } | |
| 192 | |
| 193 void SelectFileDialogImplKDE::CallKDialogOutput( | |
| 194 const CommandLine& command_line, void* params, gfx::NativeWindow parent, | |
| 195 void (SelectFileDialogImplKDE::*callback)(const std::string&, int, void*)) { | |
| 196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 197 std::string output; | |
| 198 int exit_code; | |
| 199 // Get output from KDialog | |
| 200 base::GetAppOutputWithExitCode(command_line, &output, &exit_code); | |
| 201 if (!output.empty()) | |
| 202 output.erase(output.size() - 1, 1); | |
| 203 // Now the dialog is no longer showing. We can erase its parent from the | |
| 204 // parent set. | |
| 205 std::set<GtkWindow*>::iterator iter = parents_.find(parent); | |
| 206 if (iter != parents_.end()) | |
| 207 parents_.erase(iter); | |
| 208 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 209 NewRunnableMethod(this, callback, output, exit_code, params)); | |
| 210 } | |
| 211 | |
| 212 void SelectFileDialogImplKDE::GetKDialogCommandLine(const std::string& type, | |
| 213 const std::string& title, const FilePath& path, | |
| 214 gfx::NativeWindow parent, bool file_operation, bool multiple_selection, | |
| 215 CommandLine* command_line) { | |
| 216 if (!command_line) { | |
| 217 LOG(ERROR) << "Command line for KDialog is NULL" << std::endl; | |
| 218 return; | |
| 219 } | |
| 220 // Attach to the current Chrome window. | |
| 221 GdkWindow* gdk_window = | |
| 222 gtk_widget_get_window(GTK_WIDGET((parent))); | |
| 223 int window_id = GDK_DRAWABLE_XID(gdk_window); | |
| 224 command_line->AppendSwitchASCII("--attach", base::IntToString(window_id)); | |
| 225 // Set the correct title for the dialog. | |
| 226 if (!title.empty()) { | |
| 227 command_line->AppendSwitchASCII("--title", title); | |
|
Mike Mammarella
2011/09/14 06:42:24
What happens if the title is not ASCII?
dfilimon
2011/09/14 22:06:08
Yeah, you're right! Shame on me, especially being
| |
| 228 } | |
| 229 // Enable multiple file selection if we need to. | |
| 230 if (multiple_selection) { | |
| 231 command_line->AppendSwitch("--multiple"); | |
| 232 command_line->AppendSwitch("--separate-output"); | |
| 233 } | |
| 234 command_line->AppendSwitch(type); | |
| 235 command_line->AppendArgPath(path); | |
| 236 // Depending on the type of the operation we need, get the path to the | |
| 237 // file/folder and set up mime type filters. | |
| 238 if (file_operation) { | |
| 239 command_line->AppendArg(GetMimeTypeFilterString()); | |
| 240 } | |
| 241 LOG(INFO) << "KDialog command line: " | |
|
Mike Mammarella
2011/09/14 06:42:24
It seems that a while back we switched to mostly d
dfilimon
2011/09/14 22:06:08
Done.
| |
| 242 << command_line->GetCommandLineString() << std::endl; | |
| 243 } | |
| 244 | |
| 245 void SelectFileDialogImplKDE::FileSelected(const FilePath& path, void* params) { | |
| 246 if (type_ == SELECT_SAVEAS_FILE) | |
| 247 *last_saved_path_ = path.DirName(); | |
| 248 else if (type_ == SELECT_OPEN_FILE) | |
| 249 *last_opened_path_ = path.DirName(); | |
| 250 else if (type_ == SELECT_FOLDER) | |
| 251 *last_opened_path_ = path; | |
| 252 else | |
| 253 NOTREACHED(); | |
| 254 if (listener_) { // What does the filter index actually do? | |
| 255 // TODO(dfilimon): Get a reasonable index value from somewhere. | |
| 256 listener_->FileSelected(path, 1, params); | |
| 257 } | |
| 258 } | |
| 259 | |
| 260 void SelectFileDialogImplKDE::MultiFilesSelected( | |
| 261 const std::vector<FilePath>& files, void* params) { | |
| 262 *last_opened_path_ = files[0].DirName(); | |
| 263 if (listener_) | |
| 264 listener_->MultiFilesSelected(files, params); | |
| 265 } | |
| 266 | |
| 267 void SelectFileDialogImplKDE::FileNotSelected(void* params) { | |
| 268 if (listener_) | |
| 269 listener_->FileSelectionCanceled(params); | |
| 270 } | |
| 271 | |
| 272 void SelectFileDialogImplKDE::CreateSelectFolderDialog( | |
| 273 const std::string& title, const FilePath& default_path, | |
| 274 gfx::NativeWindow parent, void *params) { | |
| 275 std::string title_string = !title.empty() ? title : | |
| 276 l10n_util::GetStringUTF8(IDS_SELECT_FOLDER_DIALOG_TITLE); | |
| 277 | |
| 278 CommandLine command_line(FilePath("kdialog")); | |
| 279 GetKDialogCommandLine( | |
| 280 "--getexistingdirectory", title, | |
| 281 default_path.empty() ? *last_saved_path_ : default_path, | |
| 282 parent, false, false, &command_line); | |
| 283 Task* dialog_task = | |
| 284 NewRunnableMethod( | |
| 285 this, &SelectFileDialogImplKDE::CallKDialogOutput, | |
| 286 command_line, params, parent, | |
| 287 &SelectFileDialogImplKDE::OnSelectSingleFolderDialogResponse); | |
| 288 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, dialog_task); | |
| 289 } | |
| 290 | |
| 291 void SelectFileDialogImplKDE::CreateFileOpenDialog( | |
| 292 const std::string& title, const FilePath& default_path, | |
| 293 gfx::NativeWindow parent, void* params) { | |
| 294 std::string title_string = !title.empty() ? title : | |
| 295 l10n_util::GetStringUTF8(IDS_OPEN_FILE_DIALOG_TITLE); | |
| 296 | |
| 297 CommandLine command_line(FilePath("kdialog")); | |
| 298 GetKDialogCommandLine( | |
| 299 "--getopenfilename", title, | |
| 300 default_path.empty() ? *last_saved_path_ : default_path, | |
| 301 parent, true, false, &command_line); | |
| 302 Task* dialog_task = | |
| 303 NewRunnableMethod( | |
| 304 this, &SelectFileDialogImplKDE::CallKDialogOutput, | |
| 305 command_line, params, parent, | |
| 306 &SelectFileDialogImplKDE::OnSelectSingleFileDialogResponse); | |
| 307 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, dialog_task); | |
| 308 } | |
| 309 | |
| 310 void SelectFileDialogImplKDE::CreateMultiFileOpenDialog( | |
| 311 const std::string& title, const FilePath& default_path, | |
| 312 gfx::NativeWindow parent, void* params) { | |
| 313 std::string title_string = !title.empty() ? title : | |
| 314 l10n_util::GetStringUTF8(IDS_OPEN_FILES_DIALOG_TITLE); | |
| 315 | |
| 316 CommandLine command_line(FilePath("kdialog")); | |
| 317 GetKDialogCommandLine( | |
| 318 "--getopenfilename", title, | |
| 319 default_path.empty() ? *last_saved_path_ : default_path, | |
| 320 parent, true, true, &command_line); | |
| 321 Task* dialog_task = | |
| 322 NewRunnableMethod( | |
| 323 this, &SelectFileDialogImplKDE::CallKDialogOutput, | |
| 324 command_line, params, parent, | |
| 325 &SelectFileDialogImplKDE::OnSelectMultiFileDialogResponse); | |
| 326 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, dialog_task); | |
| 327 } | |
| 328 | |
| 329 void SelectFileDialogImplKDE::CreateSaveAsDialog( | |
| 330 const std::string& title, const FilePath& default_path, | |
| 331 gfx::NativeWindow parent, void* params) { | |
| 332 std::string title_string = !title.empty() ? title : | |
| 333 l10n_util::GetStringUTF8(IDS_SAVE_AS_DIALOG_TITLE); | |
| 334 | |
| 335 CommandLine command_line(FilePath("kdialog")); | |
| 336 GetKDialogCommandLine( | |
| 337 "--getsavefilename", title, | |
| 338 default_path.empty() ? *last_saved_path_ : default_path, | |
| 339 parent, true, false, &command_line); | |
| 340 Task* dialog_task = | |
| 341 NewRunnableMethod( | |
| 342 this, &SelectFileDialogImplKDE::CallKDialogOutput, | |
| 343 command_line, params, parent, | |
| 344 &SelectFileDialogImplKDE::OnSelectSingleFileDialogResponse); | |
| 345 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, dialog_task); | |
| 346 } | |
| 347 | |
| 348 void SelectFileDialogImplKDE::SelectSingleFileHelper(const std::string& output, | |
| 349 int exit_code, void* params, bool allow_folder) { | |
| 350 LOG(INFO) << "[kdialog] SingleFileResponse: " << output << std::endl; | |
| 351 if (exit_code != 0 || output.empty()) { | |
| 352 FileNotSelected(params); | |
| 353 return; | |
| 354 } | |
| 355 | |
| 356 FilePath path(output); | |
| 357 if (allow_folder) { | |
| 358 FileSelected(path, params); | |
| 359 return; | |
| 360 } | |
| 361 | |
| 362 if (CallDirectoryExistsOnUIThread(path)) | |
| 363 FileNotSelected(params); | |
| 364 else | |
| 365 FileSelected(path, params); | |
| 366 } | |
| 367 | |
| 368 void SelectFileDialogImplKDE::OnSelectSingleFileDialogResponse( | |
| 369 const std::string& output, int exit_code, void* params) { | |
| 370 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 371 SelectSingleFileHelper(output, exit_code, params, false); | |
| 372 } | |
| 373 | |
| 374 void SelectFileDialogImplKDE::OnSelectSingleFolderDialogResponse( | |
| 375 const std::string& output, int exit_code, void* params) { | |
| 376 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 377 SelectSingleFileHelper(output, exit_code, params, true); | |
| 378 } | |
| 379 | |
| 380 void SelectFileDialogImplKDE::OnSelectMultiFileDialogResponse( | |
| 381 const std::string& output, int exit_code, void* params) { | |
| 382 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 383 LOG(INFO) << "[kdialog] MultiFileResponse: " << output << std::endl; | |
| 384 | |
| 385 if (exit_code != 0 || output.empty()) { | |
| 386 FileNotSelected(params); | |
| 387 return; | |
| 388 } | |
| 389 | |
| 390 std::vector<std::string> filenames; | |
| 391 Tokenize(output, "\n", &filenames); | |
| 392 std::vector<FilePath> filenames_fp; | |
| 393 for (std::vector<std::string>::iterator iter = filenames.begin(); | |
| 394 iter != filenames.end(); ++iter) { | |
| 395 FilePath path(*iter); | |
| 396 if (CallDirectoryExistsOnUIThread(path)) | |
| 397 continue; | |
| 398 filenames_fp.push_back(path); | |
| 399 } | |
| 400 | |
| 401 if (filenames_fp.empty()) { | |
| 402 FileNotSelected(params); | |
| 403 return; | |
| 404 } | |
| 405 MultiFilesSelected(filenames_fp, params); | |
| 406 } | |
| 407 | |
|
Mike Mammarella
2011/09/14 06:42:24
Please remove the trailing whitespace lines.
dfilimon
2011/09/14 22:06:08
Done.
| |
| 408 | |
| OLD | NEW |