Revert 168224 - Update V8 to version 3.15.4.
[chromium-blink-merge.git] / chrome / browser / ui / libgtk2ui / select_file_dialog_impl_kde.cc
blobfc7f539a088f6d19cb4bcab06b843fe3cc6f6c9f
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_util.h"
14 #include "base/string_number_conversions.h"
15 #include "base/string_util.h"
16 #include "base/threading/thread_restrictions.h"
17 #include "base/utf_string_conversions.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/base/l10n/l10n_util.h"
25 // These conflict with base/tracked_objects.h, so need to come last.
26 #include <gdk/gdkx.h>
27 #include <gtk/gtk.h>
29 using content::BrowserThread;
31 namespace {
33 std::string GetTitle(const std::string& title, int message_id) {
34 return title.empty() ? l10n_util::GetStringUTF8(message_id) : title;
37 const char kKdialogBinary[] = "kdialog";
39 } // namespace
41 namespace libgtk2ui {
43 // Implementation of SelectFileDialog that shows a KDE common dialog for
44 // choosing a file or folder. This acts as a modal dialog.
45 class SelectFileDialogImplKDE : public SelectFileDialogImpl {
46 public:
47 SelectFileDialogImplKDE(Listener* listener,
48 ui::SelectFilePolicy* policy,
49 base::nix::DesktopEnvironment desktop);
51 protected:
52 virtual ~SelectFileDialogImplKDE();
54 // SelectFileDialog implementation.
55 // |params| is user data we pass back via the Listener interface.
56 virtual void SelectFileImpl(Type type,
57 const string16& title,
58 const FilePath& default_path,
59 const FileTypeInfo* file_types,
60 int file_type_index,
61 const FilePath::StringType& default_extension,
62 gfx::NativeWindow owning_window,
63 void* params) OVERRIDE;
65 private:
66 virtual bool HasMultipleFileTypeChoicesImpl() OVERRIDE;
68 struct KDialogParams {
69 KDialogParams(const std::string& type, const std::string& title,
70 const FilePath& default_path, gfx::NativeWindow parent,
71 bool file_operation, bool multiple_selection,
72 void* kdialog_params,
73 void (SelectFileDialogImplKDE::*callback)(const std::string&,
74 int, void*))
75 : type(type), title(title), default_path(default_path), parent(parent),
76 file_operation(file_operation),
77 multiple_selection(multiple_selection),
78 kdialog_params(kdialog_params), callback(callback) {
81 std::string type;
82 std::string title;
83 FilePath default_path;
84 gfx::NativeWindow parent;
85 bool file_operation;
86 bool multiple_selection;
87 void* kdialog_params;
88 void (SelectFileDialogImplKDE::*callback)(const std::string&, int, void*);
91 // Get the filters from |file_types_| and concatenate them into
92 // |filter_string|.
93 std::string GetMimeTypeFilterString();
95 // Get KDialog command line representing the Argv array for KDialog.
96 void GetKDialogCommandLine(const std::string& type, const std::string& title,
97 const FilePath& default_path, gfx::NativeWindow parent,
98 bool file_operation, bool multiple_selection, CommandLine* command_line);
100 // Call KDialog on the FILE thread and post results back to the UI thread.
101 void CallKDialogOutput(const KDialogParams& params);
103 // Notifies the listener that a single file was chosen.
104 void FileSelected(const FilePath& path, void* params);
106 // Notifies the listener that multiple files were chosen.
107 void MultiFilesSelected(const std::vector<FilePath>& files, void* params);
109 // Notifies the listener that no file was chosen (the action was canceled).
110 // Dialog is passed so we can find that |params| pointer that was passed to
111 // us when we were told to show the dialog.
112 void FileNotSelected(void *params);
114 void CreateSelectFolderDialog(const std::string& title,
115 const FilePath& default_path,
116 gfx::NativeWindow parent, void* params);
118 void CreateFileOpenDialog(const std::string& title,
119 const FilePath& default_path,
120 gfx::NativeWindow parent, void* params);
122 void CreateMultiFileOpenDialog(const std::string& title,
123 const FilePath& default_path,
124 gfx::NativeWindow parent, void* params);
126 void CreateSaveAsDialog(const std::string& title,
127 const FilePath& default_path,
128 gfx::NativeWindow parent, void* params);
130 // Common function for OnSelectSingleFileDialogResponse and
131 // OnSelectSingleFolderDialogResponse.
132 void SelectSingleFileHelper(const std::string& output, int exit_code,
133 void* params, bool allow_folder);
135 void OnSelectSingleFileDialogResponse(const std::string& output,
136 int exit_code, void* params);
137 void OnSelectMultiFileDialogResponse(const std::string& output,
138 int exit_code, void* params);
139 void OnSelectSingleFolderDialogResponse(const std::string& output,
140 int exit_code, void* params);
142 // Should be either DESKTOP_ENVIRONMENT_KDE3 or DESKTOP_ENVIRONMENT_KDE4.
143 base::nix::DesktopEnvironment desktop_;
145 DISALLOW_COPY_AND_ASSIGN(SelectFileDialogImplKDE);
148 // static
149 bool SelectFileDialogImpl::CheckKDEDialogWorksOnUIThread() {
150 // No choice. UI thread can't continue without an answer here. Fortunately we
151 // only do this once, the first time a file dialog is displayed.
152 base::ThreadRestrictions::ScopedAllowIO allow_io;
154 CommandLine::StringVector cmd_vector;
155 cmd_vector.push_back(kKdialogBinary);
156 cmd_vector.push_back("--version");
157 CommandLine command_line(cmd_vector);
158 std::string dummy;
159 return base::GetAppOutput(command_line, &dummy);
162 // static
163 SelectFileDialogImpl* SelectFileDialogImpl::NewSelectFileDialogImplKDE(
164 Listener* listener,
165 ui::SelectFilePolicy* policy,
166 base::nix::DesktopEnvironment desktop) {
167 return new SelectFileDialogImplKDE(listener, policy, desktop);
170 SelectFileDialogImplKDE::SelectFileDialogImplKDE(
171 Listener* listener,
172 ui::SelectFilePolicy* policy,
173 base::nix::DesktopEnvironment desktop)
174 : SelectFileDialogImpl(listener, policy),
175 desktop_(desktop) {
176 DCHECK(desktop_ == base::nix::DESKTOP_ENVIRONMENT_KDE3 ||
177 desktop_ == base::nix::DESKTOP_ENVIRONMENT_KDE4);
180 SelectFileDialogImplKDE::~SelectFileDialogImplKDE() {
183 // We ignore |default_extension|.
184 void SelectFileDialogImplKDE::SelectFileImpl(
185 Type type,
186 const string16& title,
187 const FilePath& default_path,
188 const FileTypeInfo* file_types,
189 int file_type_index,
190 const FilePath::StringType& default_extension,
191 gfx::NativeWindow owning_window,
192 void* params) {
193 type_ = type;
194 // |owning_window| can be null when user right-clicks on a downloadable item
195 // and chooses 'Open Link in New Tab' when 'Ask where to save each file
196 // before downloading.' preference is turned on. (http://crbug.com/29213)
197 if (owning_window)
198 parents_.insert(owning_window);
200 std::string title_string = UTF16ToUTF8(title);
202 file_type_index_ = file_type_index;
203 if (file_types)
204 file_types_ = *file_types;
205 else
206 file_types_.include_all_files = true;
208 switch (type) {
209 case SELECT_FOLDER:
210 CreateSelectFolderDialog(title_string, default_path,
211 owning_window, params);
212 return;
213 case SELECT_OPEN_FILE:
214 CreateFileOpenDialog(title_string, default_path, owning_window,
215 params);
216 return;
217 case SELECT_OPEN_MULTI_FILE:
218 CreateMultiFileOpenDialog(title_string, default_path,
219 owning_window, params);
220 return;
221 case SELECT_SAVEAS_FILE:
222 CreateSaveAsDialog(title_string, default_path, owning_window,
223 params);
224 return;
225 default:
226 NOTREACHED();
227 return;
231 bool SelectFileDialogImplKDE::HasMultipleFileTypeChoicesImpl() {
232 return file_types_.extensions.size() > 1;
235 std::string SelectFileDialogImplKDE::GetMimeTypeFilterString() {
236 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
237 std::string filter_string;
238 // We need a filter set because the same mime type can appear multiple times.
239 std::set<std::string> filter_set;
240 for (size_t i = 0; i < file_types_.extensions.size(); ++i) {
241 for (size_t j = 0; j < file_types_.extensions[i].size(); ++j) {
242 if (!file_types_.extensions[i][j].empty()) {
243 std::string mime_type = base::nix::GetFileMimeType(
244 FilePath("name").ReplaceExtension(file_types_.extensions[i][j]));
245 filter_set.insert(mime_type);
249 // Add the *.* filter, but only if we have added other filters (otherwise it
250 // is implied).
251 if (file_types_.include_all_files && !file_types_.extensions.empty())
252 filter_set.insert("application/octet-stream");
253 // Create the final output string.
254 filter_string.clear();
255 for (std::set<std::string>::iterator it = filter_set.begin();
256 it != filter_set.end(); ++it) {
257 filter_string.append(*it + " ");
259 return filter_string;
262 void SelectFileDialogImplKDE::CallKDialogOutput(const KDialogParams& params) {
263 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
264 CommandLine::StringVector cmd_vector;
265 cmd_vector.push_back(kKdialogBinary);
266 CommandLine command_line(cmd_vector);
267 GetKDialogCommandLine(params.type, params.title, params.default_path,
268 params.parent, params.file_operation,
269 params.multiple_selection, &command_line);
270 std::string output;
271 int exit_code;
272 // Get output from KDialog
273 base::GetAppOutputWithExitCode(command_line, &output, &exit_code);
274 if (!output.empty())
275 output.erase(output.size() - 1);
277 // Now the dialog is no longer showing. We can erase its parent from the
278 // parent set.
279 // TODO(erg): FIX THIS.
280 // std::set<GtkWindow*>::iterator iter = parents_.find(params.parent);
281 // if (iter != parents_.end())
282 // parents_.erase(iter);
284 BrowserThread::PostTask(
285 BrowserThread::UI, FROM_HERE,
286 base::Bind(params.callback, this, output, exit_code,
287 params.kdialog_params));
290 void SelectFileDialogImplKDE::GetKDialogCommandLine(const std::string& type,
291 const std::string& title, const FilePath& path,
292 gfx::NativeWindow parent, bool file_operation, bool multiple_selection,
293 CommandLine* command_line) {
294 CHECK(command_line);
296 // Attach to the current Chrome window.
297 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET((parent)));
298 int window_id = GDK_DRAWABLE_XID(gdk_window);
299 command_line->AppendSwitchNative(
300 desktop_ == base::nix::DESKTOP_ENVIRONMENT_KDE3 ? "--embed" : "--attach",
301 base::IntToString(window_id));
302 // Set the correct title for the dialog.
303 if (!title.empty())
304 command_line->AppendSwitchNative("--title", title);
305 // Enable multiple file selection if we need to.
306 if (multiple_selection) {
307 command_line->AppendSwitch("--multiple");
308 command_line->AppendSwitch("--separate-output");
310 command_line->AppendSwitch(type);
311 // The path should never be empty. If it is, set it to PWD.
312 if (path.empty())
313 command_line->AppendArgPath(FilePath("."));
314 else
315 command_line->AppendArgPath(path);
316 // Depending on the type of the operation we need, get the path to the
317 // file/folder and set up mime type filters.
318 if (file_operation)
319 command_line->AppendArg(GetMimeTypeFilterString());
320 VLOG(1) << "KDialog command line: " << command_line->GetCommandLineString();
323 void SelectFileDialogImplKDE::FileSelected(const FilePath& path, void* params) {
324 if (type_ == SELECT_SAVEAS_FILE)
325 *last_saved_path_ = path.DirName();
326 else if (type_ == SELECT_OPEN_FILE)
327 *last_opened_path_ = path.DirName();
328 else if (type_ == SELECT_FOLDER)
329 *last_opened_path_ = path;
330 else
331 NOTREACHED();
332 if (listener_) { // What does the filter index actually do?
333 // TODO(dfilimon): Get a reasonable index value from somewhere.
334 listener_->FileSelected(path, 1, params);
338 void SelectFileDialogImplKDE::MultiFilesSelected(
339 const std::vector<FilePath>& files, void* params) {
340 *last_opened_path_ = files[0].DirName();
341 if (listener_)
342 listener_->MultiFilesSelected(files, params);
345 void SelectFileDialogImplKDE::FileNotSelected(void* params) {
346 if (listener_)
347 listener_->FileSelectionCanceled(params);
350 void SelectFileDialogImplKDE::CreateSelectFolderDialog(
351 const std::string& title, const FilePath& default_path,
352 gfx::NativeWindow parent, void *params) {
353 BrowserThread::PostTask(
354 BrowserThread::FILE, FROM_HERE,
355 base::Bind(
356 &SelectFileDialogImplKDE::CallKDialogOutput,
357 this,
358 KDialogParams(
359 "--getexistingdirectory",
360 GetTitle(title, IDS_SELECT_FOLDER_DIALOG_TITLE),
361 default_path.empty() ? *last_opened_path_ : default_path,
362 parent, false, false, params,
363 &SelectFileDialogImplKDE::OnSelectSingleFolderDialogResponse)));
366 void SelectFileDialogImplKDE::CreateFileOpenDialog(
367 const std::string& title, const FilePath& default_path,
368 gfx::NativeWindow parent, void* params) {
369 BrowserThread::PostTask(
370 BrowserThread::FILE, FROM_HERE,
371 base::Bind(
372 &SelectFileDialogImplKDE::CallKDialogOutput,
373 this,
374 KDialogParams(
375 "--getopenfilename",
376 GetTitle(title, IDS_OPEN_FILE_DIALOG_TITLE),
377 default_path.empty() ? *last_opened_path_ : default_path,
378 parent, true, false, params,
379 &SelectFileDialogImplKDE::OnSelectSingleFileDialogResponse)));
382 void SelectFileDialogImplKDE::CreateMultiFileOpenDialog(
383 const std::string& title, const FilePath& default_path,
384 gfx::NativeWindow parent, void* params) {
385 BrowserThread::PostTask(
386 BrowserThread::FILE, FROM_HERE,
387 base::Bind(
388 &SelectFileDialogImplKDE::CallKDialogOutput,
389 this,
390 KDialogParams(
391 "--getopenfilename",
392 GetTitle(title, IDS_OPEN_FILES_DIALOG_TITLE),
393 default_path.empty() ? *last_opened_path_ : default_path,
394 parent, true, true, params,
395 &SelectFileDialogImplKDE::OnSelectMultiFileDialogResponse)));
398 void SelectFileDialogImplKDE::CreateSaveAsDialog(
399 const std::string& title, const FilePath& default_path,
400 gfx::NativeWindow parent, void* params) {
401 BrowserThread::PostTask(
402 BrowserThread::FILE, FROM_HERE,
403 base::Bind(
404 &SelectFileDialogImplKDE::CallKDialogOutput,
405 this,
406 KDialogParams(
407 "--getsavefilename",
408 GetTitle(title, IDS_SAVE_AS_DIALOG_TITLE),
409 default_path.empty() ? *last_saved_path_ : default_path,
410 parent, true, false, params,
411 &SelectFileDialogImplKDE::OnSelectSingleFileDialogResponse)));
414 void SelectFileDialogImplKDE::SelectSingleFileHelper(const std::string& output,
415 int exit_code, void* params, bool allow_folder) {
416 VLOG(1) << "[kdialog] SingleFileResponse: " << output;
417 if (exit_code != 0 || output.empty()) {
418 FileNotSelected(params);
419 return;
422 FilePath path(output);
423 if (allow_folder) {
424 FileSelected(path, params);
425 return;
428 if (CallDirectoryExistsOnUIThread(path))
429 FileNotSelected(params);
430 else
431 FileSelected(path, params);
434 void SelectFileDialogImplKDE::OnSelectSingleFileDialogResponse(
435 const std::string& output, int exit_code, void* params) {
436 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
437 SelectSingleFileHelper(output, exit_code, params, false);
440 void SelectFileDialogImplKDE::OnSelectSingleFolderDialogResponse(
441 const std::string& output, int exit_code, void* params) {
442 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
443 SelectSingleFileHelper(output, exit_code, params, true);
446 void SelectFileDialogImplKDE::OnSelectMultiFileDialogResponse(
447 const std::string& output, int exit_code, void* params) {
448 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
449 VLOG(1) << "[kdialog] MultiFileResponse: " << output;
451 if (exit_code != 0 || output.empty()) {
452 FileNotSelected(params);
453 return;
456 std::vector<std::string> filenames;
457 Tokenize(output, "\n", &filenames);
458 std::vector<FilePath> filenames_fp;
459 for (std::vector<std::string>::iterator iter = filenames.begin();
460 iter != filenames.end(); ++iter) {
461 FilePath path(*iter);
462 if (CallDirectoryExistsOnUIThread(path))
463 continue;
464 filenames_fp.push_back(path);
467 if (filenames_fp.empty()) {
468 FileNotSelected(params);
469 return;
471 MultiFilesSelected(filenames_fp, params);
474 } // namespace libgtk2ui