Disable TabDragController tests that fail with a real compositor.
[chromium-blink-merge.git] / chrome / browser / ui / libgtk2ui / select_file_dialog_impl_kde.cc
blob5546b6ba378bd5a76cd33fe34a191e3e308ffc6d
1 // Copyright (c) 2012 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.
5 #include <set>
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/command_line.h"
10 #include "base/logging.h"
11 #include "base/nix/mime_util_xdg.h"
12 #include "base/nix/xdg_util.h"
13 #include "base/process/launch.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/threading/thread_restrictions.h"
18 #include "chrome/browser/ui/libgtk2ui/select_file_dialog_impl.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "grit/generated_resources.h"
22 #include "grit/ui_strings.h"
23 #include "ui/aura/root_window.h"
24 #include "ui/base/l10n/l10n_util.h"
26 // These conflict with base/tracked_objects.h, so need to come last.
27 #include <gdk/gdkx.h>
28 #include <gtk/gtk.h>
30 using content::BrowserThread;
32 namespace {
34 std::string GetTitle(const std::string& title, int message_id) {
35 return title.empty() ? l10n_util::GetStringUTF8(message_id) : title;
38 const char kKdialogBinary[] = "kdialog";
40 } // namespace
42 namespace libgtk2ui {
44 // Implementation of SelectFileDialog that shows a KDE common dialog for
45 // choosing a file or folder. This acts as a modal dialog.
46 class SelectFileDialogImplKDE : public SelectFileDialogImpl {
47 public:
48 SelectFileDialogImplKDE(Listener* listener,
49 ui::SelectFilePolicy* policy,
50 base::nix::DesktopEnvironment desktop);
52 protected:
53 virtual ~SelectFileDialogImplKDE();
55 // SelectFileDialog implementation.
56 // |params| is user data we pass back via the Listener interface.
57 virtual void SelectFileImpl(
58 Type type,
59 const base::string16& title,
60 const base::FilePath& default_path,
61 const FileTypeInfo* file_types,
62 int file_type_index,
63 const base::FilePath::StringType& default_extension,
64 gfx::NativeWindow owning_window,
65 void* params) OVERRIDE;
67 private:
68 virtual bool HasMultipleFileTypeChoicesImpl() OVERRIDE;
70 struct KDialogParams {
71 KDialogParams(const std::string& type, const std::string& title,
72 const base::FilePath& default_path, gfx::NativeWindow parent,
73 bool file_operation, bool multiple_selection,
74 void* kdialog_params,
75 void (SelectFileDialogImplKDE::*callback)(const std::string&,
76 int, void*))
77 : type(type), title(title), default_path(default_path), parent(parent),
78 file_operation(file_operation),
79 multiple_selection(multiple_selection),
80 kdialog_params(kdialog_params), callback(callback) {
83 std::string type;
84 std::string title;
85 base::FilePath default_path;
86 gfx::NativeWindow parent;
87 bool file_operation;
88 bool multiple_selection;
89 void* kdialog_params;
90 void (SelectFileDialogImplKDE::*callback)(const std::string&, int, void*);
93 // Get the filters from |file_types_| and concatenate them into
94 // |filter_string|.
95 std::string GetMimeTypeFilterString();
97 // Get KDialog command line representing the Argv array for KDialog.
98 void GetKDialogCommandLine(const std::string& type, const std::string& title,
99 const base::FilePath& default_path, gfx::NativeWindow parent,
100 bool file_operation, bool multiple_selection, CommandLine* command_line);
102 // Call KDialog on the FILE thread and post results back to the UI thread.
103 void CallKDialogOutput(const KDialogParams& params);
105 // Notifies the listener that a single file was chosen.
106 void FileSelected(const base::FilePath& path, void* params);
108 // Notifies the listener that multiple files were chosen.
109 void MultiFilesSelected(const std::vector<base::FilePath>& files,
110 void* params);
112 // Notifies the listener that no file was chosen (the action was canceled).
113 // Dialog is passed so we can find that |params| pointer that was passed to
114 // us when we were told to show the dialog.
115 void FileNotSelected(void *params);
117 void CreateSelectFolderDialog(Type type,
118 const std::string& title,
119 const base::FilePath& default_path,
120 gfx::NativeWindow parent, void* params);
122 void CreateFileOpenDialog(const std::string& title,
123 const base::FilePath& default_path,
124 gfx::NativeWindow parent, void* params);
126 void CreateMultiFileOpenDialog(const std::string& title,
127 const base::FilePath& default_path,
128 gfx::NativeWindow parent, void* params);
130 void CreateSaveAsDialog(const std::string& title,
131 const base::FilePath& default_path,
132 gfx::NativeWindow parent, void* params);
134 // Common function for OnSelectSingleFileDialogResponse and
135 // OnSelectSingleFolderDialogResponse.
136 void SelectSingleFileHelper(const std::string& output, int exit_code,
137 void* params, bool allow_folder);
139 void OnSelectSingleFileDialogResponse(const std::string& output,
140 int exit_code, void* params);
141 void OnSelectMultiFileDialogResponse(const std::string& output,
142 int exit_code, void* params);
143 void OnSelectSingleFolderDialogResponse(const std::string& output,
144 int exit_code, void* params);
146 // Should be either DESKTOP_ENVIRONMENT_KDE3 or DESKTOP_ENVIRONMENT_KDE4.
147 base::nix::DesktopEnvironment desktop_;
149 DISALLOW_COPY_AND_ASSIGN(SelectFileDialogImplKDE);
152 // static
153 bool SelectFileDialogImpl::CheckKDEDialogWorksOnUIThread() {
154 // No choice. UI thread can't continue without an answer here. Fortunately we
155 // only do this once, the first time a file dialog is displayed.
156 base::ThreadRestrictions::ScopedAllowIO allow_io;
158 CommandLine::StringVector cmd_vector;
159 cmd_vector.push_back(kKdialogBinary);
160 cmd_vector.push_back("--version");
161 CommandLine command_line(cmd_vector);
162 std::string dummy;
163 return base::GetAppOutput(command_line, &dummy);
166 // static
167 SelectFileDialogImpl* SelectFileDialogImpl::NewSelectFileDialogImplKDE(
168 Listener* listener,
169 ui::SelectFilePolicy* policy,
170 base::nix::DesktopEnvironment desktop) {
171 return new SelectFileDialogImplKDE(listener, policy, desktop);
174 SelectFileDialogImplKDE::SelectFileDialogImplKDE(
175 Listener* listener,
176 ui::SelectFilePolicy* policy,
177 base::nix::DesktopEnvironment desktop)
178 : SelectFileDialogImpl(listener, policy),
179 desktop_(desktop) {
180 DCHECK(desktop_ == base::nix::DESKTOP_ENVIRONMENT_KDE3 ||
181 desktop_ == base::nix::DESKTOP_ENVIRONMENT_KDE4);
184 SelectFileDialogImplKDE::~SelectFileDialogImplKDE() {
187 // We ignore |default_extension|.
188 void SelectFileDialogImplKDE::SelectFileImpl(
189 Type type,
190 const base::string16& title,
191 const base::FilePath& default_path,
192 const FileTypeInfo* file_types,
193 int file_type_index,
194 const base::FilePath::StringType& default_extension,
195 gfx::NativeWindow owning_window,
196 void* params) {
197 type_ = type;
198 // |owning_window| can be null when user right-clicks on a downloadable item
199 // and chooses 'Open Link in New Tab' when 'Ask where to save each file
200 // before downloading.' preference is turned on. (http://crbug.com/29213)
201 if (owning_window)
202 parents_.insert(owning_window);
204 std::string title_string = base::UTF16ToUTF8(title);
206 file_type_index_ = file_type_index;
207 if (file_types)
208 file_types_ = *file_types;
209 else
210 file_types_.include_all_files = true;
212 switch (type) {
213 case SELECT_FOLDER:
214 case SELECT_UPLOAD_FOLDER:
215 CreateSelectFolderDialog(type, title_string, default_path,
216 owning_window, params);
217 return;
218 case SELECT_OPEN_FILE:
219 CreateFileOpenDialog(title_string, default_path, owning_window,
220 params);
221 return;
222 case SELECT_OPEN_MULTI_FILE:
223 CreateMultiFileOpenDialog(title_string, default_path,
224 owning_window, params);
225 return;
226 case SELECT_SAVEAS_FILE:
227 CreateSaveAsDialog(title_string, default_path, owning_window,
228 params);
229 return;
230 default:
231 NOTREACHED();
232 return;
236 bool SelectFileDialogImplKDE::HasMultipleFileTypeChoicesImpl() {
237 return file_types_.extensions.size() > 1;
240 std::string SelectFileDialogImplKDE::GetMimeTypeFilterString() {
241 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
242 std::string filter_string;
243 // We need a filter set because the same mime type can appear multiple times.
244 std::set<std::string> filter_set;
245 for (size_t i = 0; i < file_types_.extensions.size(); ++i) {
246 for (size_t j = 0; j < file_types_.extensions[i].size(); ++j) {
247 if (!file_types_.extensions[i][j].empty()) {
248 std::string mime_type = base::nix::GetFileMimeType(base::FilePath(
249 "name").ReplaceExtension(file_types_.extensions[i][j]));
250 filter_set.insert(mime_type);
254 // Add the *.* filter, but only if we have added other filters (otherwise it
255 // is implied).
256 if (file_types_.include_all_files && !file_types_.extensions.empty())
257 filter_set.insert("application/octet-stream");
258 // Create the final output string.
259 filter_string.clear();
260 for (std::set<std::string>::iterator it = filter_set.begin();
261 it != filter_set.end(); ++it) {
262 filter_string.append(*it + " ");
264 return filter_string;
267 void SelectFileDialogImplKDE::CallKDialogOutput(const KDialogParams& params) {
268 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
269 CommandLine::StringVector cmd_vector;
270 cmd_vector.push_back(kKdialogBinary);
271 CommandLine command_line(cmd_vector);
272 GetKDialogCommandLine(params.type, params.title, params.default_path,
273 params.parent, params.file_operation,
274 params.multiple_selection, &command_line);
275 std::string output;
276 int exit_code;
277 // Get output from KDialog
278 base::GetAppOutputWithExitCode(command_line, &output, &exit_code);
279 if (!output.empty())
280 output.erase(output.size() - 1);
282 // Now the dialog is no longer showing. We can erase its parent from the
283 // parent set.
284 // TODO(erg): FIX THIS.
285 // std::set<GtkWindow*>::iterator iter = parents_.find(params.parent);
286 // if (iter != parents_.end())
287 // parents_.erase(iter);
289 BrowserThread::PostTask(
290 BrowserThread::UI, FROM_HERE,
291 base::Bind(params.callback, this, output, exit_code,
292 params.kdialog_params));
295 void SelectFileDialogImplKDE::GetKDialogCommandLine(const std::string& type,
296 const std::string& title, const base::FilePath& path,
297 gfx::NativeWindow parent, bool file_operation, bool multiple_selection,
298 CommandLine* command_line) {
299 CHECK(command_line);
301 // Attach to the current Chrome window.
302 int window_id = parent->GetDispatcher()->host()->GetAcceleratedWidget();
303 command_line->AppendSwitchNative(
304 desktop_ == base::nix::DESKTOP_ENVIRONMENT_KDE3 ? "--embed" : "--attach",
305 base::IntToString(window_id));
306 // Set the correct title for the dialog.
307 if (!title.empty())
308 command_line->AppendSwitchNative("--title", title);
309 // Enable multiple file selection if we need to.
310 if (multiple_selection) {
311 command_line->AppendSwitch("--multiple");
312 command_line->AppendSwitch("--separate-output");
314 command_line->AppendSwitch(type);
315 // The path should never be empty. If it is, set it to PWD.
316 if (path.empty())
317 command_line->AppendArgPath(base::FilePath("."));
318 else
319 command_line->AppendArgPath(path);
320 // Depending on the type of the operation we need, get the path to the
321 // file/folder and set up mime type filters.
322 if (file_operation)
323 command_line->AppendArg(GetMimeTypeFilterString());
324 VLOG(1) << "KDialog command line: " << command_line->GetCommandLineString();
327 void SelectFileDialogImplKDE::FileSelected(const base::FilePath& path,
328 void* params) {
329 if (type_ == SELECT_SAVEAS_FILE)
330 *last_saved_path_ = path.DirName();
331 else if (type_ == SELECT_OPEN_FILE)
332 *last_opened_path_ = path.DirName();
333 else if (type_ == SELECT_FOLDER)
334 *last_opened_path_ = path;
335 else
336 NOTREACHED();
337 if (listener_) { // What does the filter index actually do?
338 // TODO(dfilimon): Get a reasonable index value from somewhere.
339 listener_->FileSelected(path, 1, params);
343 void SelectFileDialogImplKDE::MultiFilesSelected(
344 const std::vector<base::FilePath>& files, void* params) {
345 *last_opened_path_ = files[0].DirName();
346 if (listener_)
347 listener_->MultiFilesSelected(files, params);
350 void SelectFileDialogImplKDE::FileNotSelected(void* params) {
351 if (listener_)
352 listener_->FileSelectionCanceled(params);
355 void SelectFileDialogImplKDE::CreateSelectFolderDialog(
356 Type type, const std::string& title, const base::FilePath& default_path,
357 gfx::NativeWindow parent, void *params) {
358 int title_message_id = (type == SELECT_UPLOAD_FOLDER)
359 ? IDS_SELECT_UPLOAD_FOLDER_DIALOG_TITLE
360 : IDS_SELECT_FOLDER_DIALOG_TITLE;
361 BrowserThread::PostTask(
362 BrowserThread::FILE, FROM_HERE,
363 base::Bind(
364 &SelectFileDialogImplKDE::CallKDialogOutput,
365 this,
366 KDialogParams(
367 "--getexistingdirectory",
368 GetTitle(title, title_message_id),
369 default_path.empty() ? *last_opened_path_ : default_path,
370 parent, false, false, params,
371 &SelectFileDialogImplKDE::OnSelectSingleFolderDialogResponse)));
374 void SelectFileDialogImplKDE::CreateFileOpenDialog(
375 const std::string& title, const base::FilePath& default_path,
376 gfx::NativeWindow parent, void* params) {
377 BrowserThread::PostTask(
378 BrowserThread::FILE, FROM_HERE,
379 base::Bind(
380 &SelectFileDialogImplKDE::CallKDialogOutput,
381 this,
382 KDialogParams(
383 "--getopenfilename",
384 GetTitle(title, IDS_OPEN_FILE_DIALOG_TITLE),
385 default_path.empty() ? *last_opened_path_ : default_path,
386 parent, true, false, params,
387 &SelectFileDialogImplKDE::OnSelectSingleFileDialogResponse)));
390 void SelectFileDialogImplKDE::CreateMultiFileOpenDialog(
391 const std::string& title, const base::FilePath& default_path,
392 gfx::NativeWindow parent, void* params) {
393 BrowserThread::PostTask(
394 BrowserThread::FILE, FROM_HERE,
395 base::Bind(
396 &SelectFileDialogImplKDE::CallKDialogOutput,
397 this,
398 KDialogParams(
399 "--getopenfilename",
400 GetTitle(title, IDS_OPEN_FILES_DIALOG_TITLE),
401 default_path.empty() ? *last_opened_path_ : default_path,
402 parent, true, true, params,
403 &SelectFileDialogImplKDE::OnSelectMultiFileDialogResponse)));
406 void SelectFileDialogImplKDE::CreateSaveAsDialog(
407 const std::string& title, const base::FilePath& default_path,
408 gfx::NativeWindow parent, void* params) {
409 BrowserThread::PostTask(
410 BrowserThread::FILE, FROM_HERE,
411 base::Bind(
412 &SelectFileDialogImplKDE::CallKDialogOutput,
413 this,
414 KDialogParams(
415 "--getsavefilename",
416 GetTitle(title, IDS_SAVE_AS_DIALOG_TITLE),
417 default_path.empty() ? *last_saved_path_ : default_path,
418 parent, true, false, params,
419 &SelectFileDialogImplKDE::OnSelectSingleFileDialogResponse)));
422 void SelectFileDialogImplKDE::SelectSingleFileHelper(const std::string& output,
423 int exit_code, void* params, bool allow_folder) {
424 VLOG(1) << "[kdialog] SingleFileResponse: " << output;
425 if (exit_code != 0 || output.empty()) {
426 FileNotSelected(params);
427 return;
430 base::FilePath path(output);
431 if (allow_folder) {
432 FileSelected(path, params);
433 return;
436 if (CallDirectoryExistsOnUIThread(path))
437 FileNotSelected(params);
438 else
439 FileSelected(path, params);
442 void SelectFileDialogImplKDE::OnSelectSingleFileDialogResponse(
443 const std::string& output, int exit_code, void* params) {
444 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
445 SelectSingleFileHelper(output, exit_code, params, false);
448 void SelectFileDialogImplKDE::OnSelectSingleFolderDialogResponse(
449 const std::string& output, int exit_code, void* params) {
450 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
451 SelectSingleFileHelper(output, exit_code, params, true);
454 void SelectFileDialogImplKDE::OnSelectMultiFileDialogResponse(
455 const std::string& output, int exit_code, void* params) {
456 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
457 VLOG(1) << "[kdialog] MultiFileResponse: " << output;
459 if (exit_code != 0 || output.empty()) {
460 FileNotSelected(params);
461 return;
464 std::vector<std::string> filenames;
465 Tokenize(output, "\n", &filenames);
466 std::vector<base::FilePath> filenames_fp;
467 for (std::vector<std::string>::iterator iter = filenames.begin();
468 iter != filenames.end(); ++iter) {
469 base::FilePath path(*iter);
470 if (CallDirectoryExistsOnUIThread(path))
471 continue;
472 filenames_fp.push_back(path);
475 if (filenames_fp.empty()) {
476 FileNotSelected(params);
477 return;
479 MultiFilesSelected(filenames_fp, params);
482 } // namespace libgtk2ui