Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / chrome / browser / ui / libgtk2ui / select_file_dialog_impl_kde.cc
blob5b0c2ea88f0036892a2fc7e82e6e356df4a9b40b
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 <X11/Xlib.h>
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/command_line.h"
12 #include "base/logging.h"
13 #include "base/nix/mime_util_xdg.h"
14 #include "base/nix/xdg_util.h"
15 #include "base/process/launch.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/threading/thread_restrictions.h"
20 #include "chrome/browser/ui/libgtk2ui/select_file_dialog_impl.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "grit/generated_resources.h"
24 #include "grit/ui_strings.h"
25 #include "ui/aura/window_tree_host.h"
26 #include "ui/base/l10n/l10n_util.h"
28 // These conflict with base/tracked_objects.h, so need to come last.
29 #include <gdk/gdkx.h>
30 #include <gtk/gtk.h>
32 using content::BrowserThread;
34 namespace {
36 std::string GetTitle(const std::string& title, int message_id) {
37 return title.empty() ? l10n_util::GetStringUTF8(message_id) : title;
40 const char kKdialogBinary[] = "kdialog";
42 } // namespace
44 namespace libgtk2ui {
46 // Implementation of SelectFileDialog that shows a KDE common dialog for
47 // choosing a file or folder. This acts as a modal dialog.
48 class SelectFileDialogImplKDE : public SelectFileDialogImpl {
49 public:
50 SelectFileDialogImplKDE(Listener* listener,
51 ui::SelectFilePolicy* policy,
52 base::nix::DesktopEnvironment desktop);
54 protected:
55 virtual ~SelectFileDialogImplKDE();
57 // BaseShellDialog implementation:
58 virtual bool IsRunning(gfx::NativeWindow parent_window) const OVERRIDE;
60 // SelectFileDialog implementation.
61 // |params| is user data we pass back via the Listener interface.
62 virtual void SelectFileImpl(
63 Type type,
64 const base::string16& title,
65 const base::FilePath& default_path,
66 const FileTypeInfo* file_types,
67 int file_type_index,
68 const base::FilePath::StringType& default_extension,
69 gfx::NativeWindow owning_window,
70 void* params) OVERRIDE;
72 private:
73 virtual bool HasMultipleFileTypeChoicesImpl() OVERRIDE;
75 struct KDialogParams {
76 KDialogParams(const std::string& type, const std::string& title,
77 const base::FilePath& default_path, XID parent,
78 bool file_operation, bool multiple_selection,
79 void* kdialog_params,
80 void (SelectFileDialogImplKDE::*callback)(XID,
81 const std::string&,
82 int, void*))
83 : type(type), title(title), default_path(default_path), parent(parent),
84 file_operation(file_operation),
85 multiple_selection(multiple_selection),
86 kdialog_params(kdialog_params), callback(callback) {
89 std::string type;
90 std::string title;
91 base::FilePath default_path;
92 XID parent;
93 bool file_operation;
94 bool multiple_selection;
95 void* kdialog_params;
96 void (SelectFileDialogImplKDE::*callback)(XID, const std::string&,
97 int, void*);
100 // Get the filters from |file_types_| and concatenate them into
101 // |filter_string|.
102 std::string GetMimeTypeFilterString();
104 // Get KDialog command line representing the Argv array for KDialog.
105 void GetKDialogCommandLine(const std::string& type, const std::string& title,
106 const base::FilePath& default_path, XID parent,
107 bool file_operation, bool multiple_selection, CommandLine* command_line);
109 // Call KDialog on the FILE thread and post results back to the UI thread.
110 void CallKDialogOutput(const KDialogParams& params);
112 // Notifies the listener that a single file was chosen.
113 void FileSelected(const base::FilePath& path, void* params);
115 // Notifies the listener that multiple files were chosen.
116 void MultiFilesSelected(const std::vector<base::FilePath>& files,
117 void* params);
119 // Notifies the listener that no file was chosen (the action was canceled).
120 // Dialog is passed so we can find that |params| pointer that was passed to
121 // us when we were told to show the dialog.
122 void FileNotSelected(void *params);
124 void CreateSelectFolderDialog(Type type,
125 const std::string& title,
126 const base::FilePath& default_path,
127 XID parent, void* params);
129 void CreateFileOpenDialog(const std::string& title,
130 const base::FilePath& default_path,
131 XID parent, void* params);
133 void CreateMultiFileOpenDialog(const std::string& title,
134 const base::FilePath& default_path,
135 XID parent, void* params);
137 void CreateSaveAsDialog(const std::string& title,
138 const base::FilePath& default_path,
139 XID parent, void* params);
141 // Common function for OnSelectSingleFileDialogResponse and
142 // OnSelectSingleFolderDialogResponse.
143 void SelectSingleFileHelper(const std::string& output, int exit_code,
144 void* params, bool allow_folder);
146 void OnSelectSingleFileDialogResponse(XID parent,
147 const std::string& output,
148 int exit_code, void* params);
149 void OnSelectMultiFileDialogResponse(XID parent,
150 const std::string& output,
151 int exit_code, void* params);
152 void OnSelectSingleFolderDialogResponse(XID parent,
153 const std::string& output,
154 int exit_code, void* params);
156 // Should be either DESKTOP_ENVIRONMENT_KDE3 or DESKTOP_ENVIRONMENT_KDE4.
157 base::nix::DesktopEnvironment desktop_;
159 // The set of all parent windows for which we are currently running
160 // dialogs. This should only be accessed on the UI thread.
161 std::set<XID> parents_;
163 DISALLOW_COPY_AND_ASSIGN(SelectFileDialogImplKDE);
166 // static
167 bool SelectFileDialogImpl::CheckKDEDialogWorksOnUIThread() {
168 // No choice. UI thread can't continue without an answer here. Fortunately we
169 // only do this once, the first time a file dialog is displayed.
170 base::ThreadRestrictions::ScopedAllowIO allow_io;
172 CommandLine::StringVector cmd_vector;
173 cmd_vector.push_back(kKdialogBinary);
174 cmd_vector.push_back("--version");
175 CommandLine command_line(cmd_vector);
176 std::string dummy;
177 return base::GetAppOutput(command_line, &dummy);
180 // static
181 SelectFileDialogImpl* SelectFileDialogImpl::NewSelectFileDialogImplKDE(
182 Listener* listener,
183 ui::SelectFilePolicy* policy,
184 base::nix::DesktopEnvironment desktop) {
185 return new SelectFileDialogImplKDE(listener, policy, desktop);
188 SelectFileDialogImplKDE::SelectFileDialogImplKDE(
189 Listener* listener,
190 ui::SelectFilePolicy* policy,
191 base::nix::DesktopEnvironment desktop)
192 : SelectFileDialogImpl(listener, policy),
193 desktop_(desktop) {
194 DCHECK(desktop_ == base::nix::DESKTOP_ENVIRONMENT_KDE3 ||
195 desktop_ == base::nix::DESKTOP_ENVIRONMENT_KDE4);
198 SelectFileDialogImplKDE::~SelectFileDialogImplKDE() {
201 bool SelectFileDialogImplKDE::IsRunning(gfx::NativeWindow parent_window) const {
202 DCHECK_CURRENTLY_ON(BrowserThread::UI);
203 if (parent_window && parent_window->GetHost()) {
204 XID xid = parent_window->GetHost()->GetAcceleratedWidget();
205 return parents_.find(xid) != parents_.end();
208 return false;
211 // We ignore |default_extension|.
212 void SelectFileDialogImplKDE::SelectFileImpl(
213 Type type,
214 const base::string16& title,
215 const base::FilePath& default_path,
216 const FileTypeInfo* file_types,
217 int file_type_index,
218 const base::FilePath::StringType& default_extension,
219 gfx::NativeWindow owning_window,
220 void* params) {
221 DCHECK_CURRENTLY_ON(BrowserThread::UI);
222 type_ = type;
224 XID window_xid = None;
225 if (owning_window && owning_window->GetHost()) {
226 // |owning_window| can be null when user right-clicks on a downloadable item
227 // and chooses 'Open Link in New Tab' when 'Ask where to save each file
228 // before downloading.' preference is turned on. (http://crbug.com/29213)
229 window_xid = owning_window->GetHost()->GetAcceleratedWidget();
230 parents_.insert(window_xid);
233 std::string title_string = base::UTF16ToUTF8(title);
235 file_type_index_ = file_type_index;
236 if (file_types)
237 file_types_ = *file_types;
238 else
239 file_types_.include_all_files = true;
241 switch (type) {
242 case SELECT_FOLDER:
243 case SELECT_UPLOAD_FOLDER:
244 CreateSelectFolderDialog(type, title_string, default_path,
245 window_xid, params);
246 return;
247 case SELECT_OPEN_FILE:
248 CreateFileOpenDialog(title_string, default_path, window_xid, params);
249 return;
250 case SELECT_OPEN_MULTI_FILE:
251 CreateMultiFileOpenDialog(title_string, default_path, window_xid, params);
252 return;
253 case SELECT_SAVEAS_FILE:
254 CreateSaveAsDialog(title_string, default_path, window_xid, params);
255 return;
256 default:
257 NOTREACHED();
258 return;
262 bool SelectFileDialogImplKDE::HasMultipleFileTypeChoicesImpl() {
263 return file_types_.extensions.size() > 1;
266 std::string SelectFileDialogImplKDE::GetMimeTypeFilterString() {
267 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
268 std::string filter_string;
269 // We need a filter set because the same mime type can appear multiple times.
270 std::set<std::string> filter_set;
271 for (size_t i = 0; i < file_types_.extensions.size(); ++i) {
272 for (size_t j = 0; j < file_types_.extensions[i].size(); ++j) {
273 if (!file_types_.extensions[i][j].empty()) {
274 std::string mime_type = base::nix::GetFileMimeType(base::FilePath(
275 "name").ReplaceExtension(file_types_.extensions[i][j]));
276 filter_set.insert(mime_type);
280 // Add the *.* filter, but only if we have added other filters (otherwise it
281 // is implied).
282 if (file_types_.include_all_files && !file_types_.extensions.empty())
283 filter_set.insert("application/octet-stream");
284 // Create the final output string.
285 filter_string.clear();
286 for (std::set<std::string>::iterator it = filter_set.begin();
287 it != filter_set.end(); ++it) {
288 filter_string.append(*it + " ");
290 return filter_string;
293 void SelectFileDialogImplKDE::CallKDialogOutput(const KDialogParams& params) {
294 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
295 CommandLine::StringVector cmd_vector;
296 cmd_vector.push_back(kKdialogBinary);
297 CommandLine command_line(cmd_vector);
298 GetKDialogCommandLine(params.type, params.title, params.default_path,
299 params.parent, params.file_operation,
300 params.multiple_selection, &command_line);
301 std::string output;
302 int exit_code;
303 // Get output from KDialog
304 base::GetAppOutputWithExitCode(command_line, &output, &exit_code);
305 if (!output.empty())
306 output.erase(output.size() - 1);
308 // Now the dialog is no longer showing, but we can't erase its parent from the
309 // parent set yet because we're on the FILE thread.
310 BrowserThread::PostTask(
311 BrowserThread::UI, FROM_HERE,
312 base::Bind(params.callback, this, params.parent, output, exit_code,
313 params.kdialog_params));
316 void SelectFileDialogImplKDE::GetKDialogCommandLine(const std::string& type,
317 const std::string& title, const base::FilePath& path,
318 XID parent, bool file_operation, bool multiple_selection,
319 CommandLine* command_line) {
320 CHECK(command_line);
322 // Attach to the current Chrome window.
323 if (parent != None) {
324 command_line->AppendSwitchNative(
325 desktop_ == base::nix::DESKTOP_ENVIRONMENT_KDE3 ?
326 "--embed" : "--attach",
327 base::IntToString(parent));
330 // Set the correct title for the dialog.
331 if (!title.empty())
332 command_line->AppendSwitchNative("--title", title);
333 // Enable multiple file selection if we need to.
334 if (multiple_selection) {
335 command_line->AppendSwitch("--multiple");
336 command_line->AppendSwitch("--separate-output");
338 command_line->AppendSwitch(type);
339 // The path should never be empty. If it is, set it to PWD.
340 if (path.empty())
341 command_line->AppendArgPath(base::FilePath("."));
342 else
343 command_line->AppendArgPath(path);
344 // Depending on the type of the operation we need, get the path to the
345 // file/folder and set up mime type filters.
346 if (file_operation)
347 command_line->AppendArg(GetMimeTypeFilterString());
348 VLOG(1) << "KDialog command line: " << command_line->GetCommandLineString();
351 void SelectFileDialogImplKDE::FileSelected(const base::FilePath& path,
352 void* params) {
353 if (type_ == SELECT_SAVEAS_FILE)
354 *last_saved_path_ = path.DirName();
355 else if (type_ == SELECT_OPEN_FILE)
356 *last_opened_path_ = path.DirName();
357 else if (type_ == SELECT_FOLDER)
358 *last_opened_path_ = path;
359 else
360 NOTREACHED();
361 if (listener_) { // What does the filter index actually do?
362 // TODO(dfilimon): Get a reasonable index value from somewhere.
363 listener_->FileSelected(path, 1, params);
367 void SelectFileDialogImplKDE::MultiFilesSelected(
368 const std::vector<base::FilePath>& files, void* params) {
369 *last_opened_path_ = files[0].DirName();
370 if (listener_)
371 listener_->MultiFilesSelected(files, params);
374 void SelectFileDialogImplKDE::FileNotSelected(void* params) {
375 if (listener_)
376 listener_->FileSelectionCanceled(params);
379 void SelectFileDialogImplKDE::CreateSelectFolderDialog(
380 Type type, const std::string& title, const base::FilePath& default_path,
381 XID parent, void *params) {
382 int title_message_id = (type == SELECT_UPLOAD_FOLDER)
383 ? IDS_SELECT_UPLOAD_FOLDER_DIALOG_TITLE
384 : IDS_SELECT_FOLDER_DIALOG_TITLE;
385 BrowserThread::PostTask(
386 BrowserThread::FILE, FROM_HERE,
387 base::Bind(
388 &SelectFileDialogImplKDE::CallKDialogOutput,
389 this,
390 KDialogParams(
391 "--getexistingdirectory",
392 GetTitle(title, title_message_id),
393 default_path.empty() ? *last_opened_path_ : default_path,
394 parent, false, false, params,
395 &SelectFileDialogImplKDE::OnSelectSingleFolderDialogResponse)));
398 void SelectFileDialogImplKDE::CreateFileOpenDialog(
399 const std::string& title, const base::FilePath& default_path,
400 XID parent, void* params) {
401 BrowserThread::PostTask(
402 BrowserThread::FILE, FROM_HERE,
403 base::Bind(
404 &SelectFileDialogImplKDE::CallKDialogOutput,
405 this,
406 KDialogParams(
407 "--getopenfilename",
408 GetTitle(title, IDS_OPEN_FILE_DIALOG_TITLE),
409 default_path.empty() ? *last_opened_path_ : default_path,
410 parent, true, false, params,
411 &SelectFileDialogImplKDE::OnSelectSingleFileDialogResponse)));
414 void SelectFileDialogImplKDE::CreateMultiFileOpenDialog(
415 const std::string& title, const base::FilePath& default_path,
416 XID parent, void* params) {
417 BrowserThread::PostTask(
418 BrowserThread::FILE, FROM_HERE,
419 base::Bind(
420 &SelectFileDialogImplKDE::CallKDialogOutput,
421 this,
422 KDialogParams(
423 "--getopenfilename",
424 GetTitle(title, IDS_OPEN_FILES_DIALOG_TITLE),
425 default_path.empty() ? *last_opened_path_ : default_path,
426 parent, true, true, params,
427 &SelectFileDialogImplKDE::OnSelectMultiFileDialogResponse)));
430 void SelectFileDialogImplKDE::CreateSaveAsDialog(
431 const std::string& title, const base::FilePath& default_path,
432 XID parent, void* params) {
433 BrowserThread::PostTask(
434 BrowserThread::FILE, FROM_HERE,
435 base::Bind(
436 &SelectFileDialogImplKDE::CallKDialogOutput,
437 this,
438 KDialogParams(
439 "--getsavefilename",
440 GetTitle(title, IDS_SAVE_AS_DIALOG_TITLE),
441 default_path.empty() ? *last_saved_path_ : default_path,
442 parent, true, false, params,
443 &SelectFileDialogImplKDE::OnSelectSingleFileDialogResponse)));
446 void SelectFileDialogImplKDE::SelectSingleFileHelper(const std::string& output,
447 int exit_code, void* params, bool allow_folder) {
448 VLOG(1) << "[kdialog] SingleFileResponse: " << output;
449 if (exit_code != 0 || output.empty()) {
450 FileNotSelected(params);
451 return;
454 base::FilePath path(output);
455 if (allow_folder) {
456 FileSelected(path, params);
457 return;
460 if (CallDirectoryExistsOnUIThread(path))
461 FileNotSelected(params);
462 else
463 FileSelected(path, params);
466 void SelectFileDialogImplKDE::OnSelectSingleFileDialogResponse(
467 XID parent, const std::string& output, int exit_code, void* params) {
468 DCHECK_CURRENTLY_ON(BrowserThread::UI);
469 parents_.erase(parent);
470 SelectSingleFileHelper(output, exit_code, params, false);
473 void SelectFileDialogImplKDE::OnSelectSingleFolderDialogResponse(
474 XID parent, const std::string& output, int exit_code, void* params) {
475 DCHECK_CURRENTLY_ON(BrowserThread::UI);
476 parents_.erase(parent);
477 SelectSingleFileHelper(output, exit_code, params, true);
480 void SelectFileDialogImplKDE::OnSelectMultiFileDialogResponse(
481 XID parent, const std::string& output, int exit_code, void* params) {
482 DCHECK_CURRENTLY_ON(BrowserThread::UI);
483 VLOG(1) << "[kdialog] MultiFileResponse: " << output;
485 parents_.erase(parent);
487 if (exit_code != 0 || output.empty()) {
488 FileNotSelected(params);
489 return;
492 std::vector<std::string> filenames;
493 Tokenize(output, "\n", &filenames);
494 std::vector<base::FilePath> filenames_fp;
495 for (std::vector<std::string>::iterator iter = filenames.begin();
496 iter != filenames.end(); ++iter) {
497 base::FilePath path(*iter);
498 if (CallDirectoryExistsOnUIThread(path))
499 continue;
500 filenames_fp.push_back(path);
503 if (filenames_fp.empty()) {
504 FileNotSelected(params);
505 return;
507 MultiFilesSelected(filenames_fp, params);
510 } // namespace libgtk2ui