Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(55)

Side by Side Diff: chrome/browser/ui/libgtk2ui/select_file_dialog_impl_kde.cc

Issue 2449243002: Gtk3 ui: Add libgtk3ui as a separate build component (Closed)
Patch Set: Add theme_properties dep to //chrome/browser/ui Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include <stddef.h>
6 #include <X11/Xlib.h>
7
8 #include <memory>
9 #include <set>
10
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/command_line.h"
14 #include "base/logging.h"
15 #include "base/macros.h"
16 #include "base/nix/mime_util_xdg.h"
17 #include "base/nix/xdg_util.h"
18 #include "base/process/launch.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_split.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/threading/thread_restrictions.h"
24 #include "chrome/browser/ui/libgtk2ui/select_file_dialog_impl.h"
25 #include "chrome/grit/generated_resources.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "ui/aura/window_tree_host.h"
28 #include "ui/base/l10n/l10n_util.h"
29 #include "ui/strings/grit/ui_strings.h"
30
31 // These conflict with base/tracked_objects.h, so need to come last.
32 #include <gdk/gdkx.h>
33 #include <gtk/gtk.h>
34
35 using content::BrowserThread;
36
37 namespace {
38
39 std::string GetTitle(const std::string& title, int message_id) {
40 return title.empty() ? l10n_util::GetStringUTF8(message_id) : title;
41 }
42
43 const char kKdialogBinary[] = "kdialog";
44
45 } // namespace
46
47 namespace libgtk2ui {
48
49 // Implementation of SelectFileDialog that shows a KDE common dialog for
50 // choosing a file or folder. This acts as a modal dialog.
51 class SelectFileDialogImplKDE : public SelectFileDialogImpl {
52 public:
53 SelectFileDialogImplKDE(Listener* listener,
54 ui::SelectFilePolicy* policy,
55 base::nix::DesktopEnvironment desktop);
56
57 protected:
58 ~SelectFileDialogImplKDE() override;
59
60 // BaseShellDialog implementation:
61 bool IsRunning(gfx::NativeWindow parent_window) const override;
62
63 // SelectFileDialog implementation.
64 // |params| is user data we pass back via the Listener interface.
65 void SelectFileImpl(Type type,
66 const base::string16& title,
67 const base::FilePath& default_path,
68 const FileTypeInfo* file_types,
69 int file_type_index,
70 const base::FilePath::StringType& default_extension,
71 gfx::NativeWindow owning_window,
72 void* params) override;
73
74 private:
75 bool HasMultipleFileTypeChoicesImpl() override;
76
77 struct KDialogParams {
78 KDialogParams(const std::string& type,
79 const std::string& title,
80 const base::FilePath& default_path,
81 XID parent,
82 bool file_operation,
83 bool multiple_selection)
84 : type(type),
85 title(title),
86 default_path(default_path),
87 parent(parent),
88 file_operation(file_operation),
89 multiple_selection(multiple_selection) {}
90
91 std::string type;
92 std::string title;
93 base::FilePath default_path;
94 XID parent;
95 bool file_operation;
96 bool multiple_selection;
97 };
98
99 struct KDialogOutputParams {
100 std::string output;
101 int exit_code;
102 };
103
104 // Get the filters from |file_types_| and concatenate them into
105 // |filter_string|.
106 std::string GetMimeTypeFilterString();
107
108 // Get KDialog command line representing the Argv array for KDialog.
109 void GetKDialogCommandLine(const std::string& type,
110 const std::string& title,
111 const base::FilePath& default_path,
112 XID parent,
113 bool file_operation,
114 bool multiple_selection,
115 base::CommandLine* command_line);
116
117 // Call KDialog on the FILE thread and return the results.
118 std::unique_ptr<KDialogOutputParams> CallKDialogOutput(
119 const KDialogParams& params);
120
121 // Notifies the listener that a single file was chosen.
122 void FileSelected(const base::FilePath& path, void* params);
123
124 // Notifies the listener that multiple files were chosen.
125 void MultiFilesSelected(const std::vector<base::FilePath>& files,
126 void* params);
127
128 // Notifies the listener that no file was chosen (the action was canceled).
129 // Dialog is passed so we can find that |params| pointer that was passed to
130 // us when we were told to show the dialog.
131 void FileNotSelected(void *params);
132
133 void CreateSelectFolderDialog(Type type,
134 const std::string& title,
135 const base::FilePath& default_path,
136 XID parent, void* params);
137
138 void CreateFileOpenDialog(const std::string& title,
139 const base::FilePath& default_path,
140 XID parent, void* params);
141
142 void CreateMultiFileOpenDialog(const std::string& title,
143 const base::FilePath& default_path,
144 XID parent, void* params);
145
146 void CreateSaveAsDialog(const std::string& title,
147 const base::FilePath& default_path,
148 XID parent, void* params);
149
150 // Common function for OnSelectSingleFileDialogResponse and
151 // OnSelectSingleFolderDialogResponse.
152 void SelectSingleFileHelper(void* params,
153 bool allow_folder,
154 std::unique_ptr<KDialogOutputParams> results);
155
156 void OnSelectSingleFileDialogResponse(
157 XID parent,
158 void* params,
159 std::unique_ptr<KDialogOutputParams> results);
160 void OnSelectMultiFileDialogResponse(
161 XID parent,
162 void* params,
163 std::unique_ptr<KDialogOutputParams> results);
164 void OnSelectSingleFolderDialogResponse(
165 XID parent,
166 void* params,
167 std::unique_ptr<KDialogOutputParams> results);
168
169 // Should be either DESKTOP_ENVIRONMENT_KDE3, KDE4, or KDE5.
170 base::nix::DesktopEnvironment desktop_;
171
172 // The set of all parent windows for which we are currently running
173 // dialogs. This should only be accessed on the UI thread.
174 std::set<XID> parents_;
175
176 DISALLOW_COPY_AND_ASSIGN(SelectFileDialogImplKDE);
177 };
178
179 // static
180 bool SelectFileDialogImpl::CheckKDEDialogWorksOnUIThread() {
181 // No choice. UI thread can't continue without an answer here. Fortunately we
182 // only do this once, the first time a file dialog is displayed.
183 base::ThreadRestrictions::ScopedAllowIO allow_io;
184
185 base::CommandLine::StringVector cmd_vector;
186 cmd_vector.push_back(kKdialogBinary);
187 cmd_vector.push_back("--version");
188 base::CommandLine command_line(cmd_vector);
189 std::string dummy;
190 return base::GetAppOutput(command_line, &dummy);
191 }
192
193 // static
194 SelectFileDialogImpl* SelectFileDialogImpl::NewSelectFileDialogImplKDE(
195 Listener* listener,
196 ui::SelectFilePolicy* policy,
197 base::nix::DesktopEnvironment desktop) {
198 return new SelectFileDialogImplKDE(listener, policy, desktop);
199 }
200
201 SelectFileDialogImplKDE::SelectFileDialogImplKDE(
202 Listener* listener,
203 ui::SelectFilePolicy* policy,
204 base::nix::DesktopEnvironment desktop)
205 : SelectFileDialogImpl(listener, policy),
206 desktop_(desktop) {
207 DCHECK(desktop_ == base::nix::DESKTOP_ENVIRONMENT_KDE3 ||
208 desktop_ == base::nix::DESKTOP_ENVIRONMENT_KDE4 ||
209 desktop_ == base::nix::DESKTOP_ENVIRONMENT_KDE5);
210 }
211
212 SelectFileDialogImplKDE::~SelectFileDialogImplKDE() {
213 }
214
215 bool SelectFileDialogImplKDE::IsRunning(gfx::NativeWindow parent_window) const {
216 DCHECK_CURRENTLY_ON(BrowserThread::UI);
217 if (parent_window && parent_window->GetHost()) {
218 XID xid = parent_window->GetHost()->GetAcceleratedWidget();
219 return parents_.find(xid) != parents_.end();
220 }
221
222 return false;
223 }
224
225 // We ignore |default_extension|.
226 void SelectFileDialogImplKDE::SelectFileImpl(
227 Type type,
228 const base::string16& title,
229 const base::FilePath& default_path,
230 const FileTypeInfo* file_types,
231 int file_type_index,
232 const base::FilePath::StringType& default_extension,
233 gfx::NativeWindow owning_window,
234 void* params) {
235 DCHECK_CURRENTLY_ON(BrowserThread::UI);
236 type_ = type;
237
238 XID window_xid = None;
239 if (owning_window && owning_window->GetHost()) {
240 // |owning_window| can be null when user right-clicks on a downloadable item
241 // and chooses 'Open Link in New Tab' when 'Ask where to save each file
242 // before downloading.' preference is turned on. (http://crbug.com/29213)
243 window_xid = owning_window->GetHost()->GetAcceleratedWidget();
244 parents_.insert(window_xid);
245 }
246
247 std::string title_string = base::UTF16ToUTF8(title);
248
249 file_type_index_ = file_type_index;
250 if (file_types)
251 file_types_ = *file_types;
252 else
253 file_types_.include_all_files = true;
254
255 switch (type) {
256 case SELECT_FOLDER:
257 case SELECT_UPLOAD_FOLDER:
258 CreateSelectFolderDialog(type, title_string, default_path,
259 window_xid, params);
260 return;
261 case SELECT_OPEN_FILE:
262 CreateFileOpenDialog(title_string, default_path, window_xid, params);
263 return;
264 case SELECT_OPEN_MULTI_FILE:
265 CreateMultiFileOpenDialog(title_string, default_path, window_xid, params);
266 return;
267 case SELECT_SAVEAS_FILE:
268 CreateSaveAsDialog(title_string, default_path, window_xid, params);
269 return;
270 default:
271 NOTREACHED();
272 return;
273 }
274 }
275
276 bool SelectFileDialogImplKDE::HasMultipleFileTypeChoicesImpl() {
277 return file_types_.extensions.size() > 1;
278 }
279
280 std::string SelectFileDialogImplKDE::GetMimeTypeFilterString() {
281 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
282 std::string filter_string;
283 // We need a filter set because the same mime type can appear multiple times.
284 std::set<std::string> filter_set;
285 for (size_t i = 0; i < file_types_.extensions.size(); ++i) {
286 for (size_t j = 0; j < file_types_.extensions[i].size(); ++j) {
287 if (!file_types_.extensions[i][j].empty()) {
288 std::string mime_type = base::nix::GetFileMimeType(base::FilePath(
289 "name").ReplaceExtension(file_types_.extensions[i][j]));
290 filter_set.insert(mime_type);
291 }
292 }
293 }
294 // Add the *.* filter, but only if we have added other filters (otherwise it
295 // is implied).
296 if (file_types_.include_all_files && !file_types_.extensions.empty())
297 filter_set.insert("application/octet-stream");
298 // Create the final output string.
299 filter_string.clear();
300 for (std::set<std::string>::iterator it = filter_set.begin();
301 it != filter_set.end(); ++it) {
302 filter_string.append(*it + " ");
303 }
304 return filter_string;
305 }
306
307 std::unique_ptr<SelectFileDialogImplKDE::KDialogOutputParams>
308 SelectFileDialogImplKDE::CallKDialogOutput(const KDialogParams& params) {
309 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
310 base::CommandLine::StringVector cmd_vector;
311 cmd_vector.push_back(kKdialogBinary);
312 base::CommandLine command_line(cmd_vector);
313 GetKDialogCommandLine(params.type, params.title, params.default_path,
314 params.parent, params.file_operation,
315 params.multiple_selection, &command_line);
316
317 auto results = base::MakeUnique<KDialogOutputParams>();
318 // Get output from KDialog
319 base::GetAppOutputWithExitCode(command_line, &results->output,
320 &results->exit_code);
321 if (!results->output.empty())
322 results->output.erase(results->output.size() - 1);
323 return results;
324 }
325
326 void SelectFileDialogImplKDE::GetKDialogCommandLine(
327 const std::string& type,
328 const std::string& title,
329 const base::FilePath& path,
330 XID parent,
331 bool file_operation,
332 bool multiple_selection,
333 base::CommandLine* command_line) {
334 CHECK(command_line);
335
336 // Attach to the current Chrome window.
337 if (parent != None) {
338 command_line->AppendSwitchNative(
339 desktop_ == base::nix::DESKTOP_ENVIRONMENT_KDE3 ?
340 "--embed" : "--attach",
341 base::Uint64ToString(parent));
342 }
343
344 // Set the correct title for the dialog.
345 if (!title.empty())
346 command_line->AppendSwitchNative("--title", title);
347 // Enable multiple file selection if we need to.
348 if (multiple_selection) {
349 command_line->AppendSwitch("--multiple");
350 command_line->AppendSwitch("--separate-output");
351 }
352 command_line->AppendSwitch(type);
353 // The path should never be empty. If it is, set it to PWD.
354 if (path.empty())
355 command_line->AppendArgPath(base::FilePath("."));
356 else
357 command_line->AppendArgPath(path);
358 // Depending on the type of the operation we need, get the path to the
359 // file/folder and set up mime type filters.
360 if (file_operation)
361 command_line->AppendArg(GetMimeTypeFilterString());
362 VLOG(1) << "KDialog command line: " << command_line->GetCommandLineString();
363 }
364
365 void SelectFileDialogImplKDE::FileSelected(const base::FilePath& path,
366 void* params) {
367 if (type_ == SELECT_SAVEAS_FILE)
368 *last_saved_path_ = path.DirName();
369 else if (type_ == SELECT_OPEN_FILE)
370 *last_opened_path_ = path.DirName();
371 else if (type_ == SELECT_FOLDER || type_ == SELECT_UPLOAD_FOLDER)
372 *last_opened_path_ = path;
373 else
374 NOTREACHED();
375 if (listener_) { // What does the filter index actually do?
376 // TODO(dfilimon): Get a reasonable index value from somewhere.
377 listener_->FileSelected(path, 1, params);
378 }
379 }
380
381 void SelectFileDialogImplKDE::MultiFilesSelected(
382 const std::vector<base::FilePath>& files, void* params) {
383 *last_opened_path_ = files[0].DirName();
384 if (listener_)
385 listener_->MultiFilesSelected(files, params);
386 }
387
388 void SelectFileDialogImplKDE::FileNotSelected(void* params) {
389 if (listener_)
390 listener_->FileSelectionCanceled(params);
391 }
392
393 void SelectFileDialogImplKDE::CreateSelectFolderDialog(
394 Type type, const std::string& title, const base::FilePath& default_path,
395 XID parent, void *params) {
396 int title_message_id = (type == SELECT_UPLOAD_FOLDER)
397 ? IDS_SELECT_UPLOAD_FOLDER_DIALOG_TITLE
398 : IDS_SELECT_FOLDER_DIALOG_TITLE;
399 BrowserThread::PostTaskAndReplyWithResult(
400 BrowserThread::FILE, FROM_HERE,
401 base::Bind(
402 &SelectFileDialogImplKDE::CallKDialogOutput, this,
403 KDialogParams(
404 "--getexistingdirectory", GetTitle(title, title_message_id),
405 default_path.empty() ? *last_opened_path_ : default_path, parent,
406 false, false)),
407 base::Bind(&SelectFileDialogImplKDE::OnSelectSingleFolderDialogResponse,
408 this, parent, params));
409 }
410
411 void SelectFileDialogImplKDE::CreateFileOpenDialog(
412 const std::string& title, const base::FilePath& default_path,
413 XID parent, void* params) {
414 BrowserThread::PostTaskAndReplyWithResult(
415 BrowserThread::FILE, FROM_HERE,
416 base::Bind(
417 &SelectFileDialogImplKDE::CallKDialogOutput, this,
418 KDialogParams(
419 "--getopenfilename", GetTitle(title, IDS_OPEN_FILE_DIALOG_TITLE),
420 default_path.empty() ? *last_opened_path_ : default_path, parent,
421 true, false)),
422 base::Bind(&SelectFileDialogImplKDE::OnSelectSingleFileDialogResponse,
423 this, parent, params));
424 }
425
426 void SelectFileDialogImplKDE::CreateMultiFileOpenDialog(
427 const std::string& title, const base::FilePath& default_path,
428 XID parent, void* params) {
429 BrowserThread::PostTaskAndReplyWithResult(
430 BrowserThread::FILE, FROM_HERE,
431 base::Bind(
432 &SelectFileDialogImplKDE::CallKDialogOutput, this,
433 KDialogParams(
434 "--getopenfilename", GetTitle(title, IDS_OPEN_FILES_DIALOG_TITLE),
435 default_path.empty() ? *last_opened_path_ : default_path, parent,
436 true, true)),
437 base::Bind(&SelectFileDialogImplKDE::OnSelectMultiFileDialogResponse,
438 this, parent, params));
439 }
440
441 void SelectFileDialogImplKDE::CreateSaveAsDialog(
442 const std::string& title, const base::FilePath& default_path,
443 XID parent, void* params) {
444 BrowserThread::PostTaskAndReplyWithResult(
445 BrowserThread::FILE, FROM_HERE,
446 base::Bind(
447 &SelectFileDialogImplKDE::CallKDialogOutput, this,
448 KDialogParams("--getsavefilename",
449 GetTitle(title, IDS_SAVE_AS_DIALOG_TITLE),
450 default_path.empty() ? *last_saved_path_ : default_path,
451 parent, true, false)),
452 base::Bind(&SelectFileDialogImplKDE::OnSelectSingleFileDialogResponse,
453 this, parent, params));
454 }
455
456 void SelectFileDialogImplKDE::SelectSingleFileHelper(
457 void* params,
458 bool allow_folder,
459 std::unique_ptr<KDialogOutputParams> results) {
460 VLOG(1) << "[kdialog] SingleFileResponse: " << results->output;
461 if (results->exit_code || results->output.empty()) {
462 FileNotSelected(params);
463 return;
464 }
465
466 base::FilePath path(results->output);
467 if (allow_folder) {
468 FileSelected(path, params);
469 return;
470 }
471
472 if (CallDirectoryExistsOnUIThread(path))
473 FileNotSelected(params);
474 else
475 FileSelected(path, params);
476 }
477
478 void SelectFileDialogImplKDE::OnSelectSingleFileDialogResponse(
479 XID parent,
480 void* params,
481 std::unique_ptr<KDialogOutputParams> results) {
482 DCHECK_CURRENTLY_ON(BrowserThread::UI);
483 parents_.erase(parent);
484 SelectSingleFileHelper(params, false, std::move(results));
485 }
486
487 void SelectFileDialogImplKDE::OnSelectSingleFolderDialogResponse(
488 XID parent,
489 void* params,
490 std::unique_ptr<KDialogOutputParams> results) {
491 DCHECK_CURRENTLY_ON(BrowserThread::UI);
492 parents_.erase(parent);
493 SelectSingleFileHelper(params, true, std::move(results));
494 }
495
496 void SelectFileDialogImplKDE::OnSelectMultiFileDialogResponse(
497 XID parent,
498 void* params,
499 std::unique_ptr<KDialogOutputParams> results) {
500 DCHECK_CURRENTLY_ON(BrowserThread::UI);
501 VLOG(1) << "[kdialog] MultiFileResponse: " << results->output;
502
503 parents_.erase(parent);
504
505 if (results->exit_code || results->output.empty()) {
506 FileNotSelected(params);
507 return;
508 }
509
510 std::vector<base::FilePath> filenames_fp;
511 for (const base::StringPiece& line :
512 base::SplitStringPiece(results->output, "\n", base::KEEP_WHITESPACE,
513 base::SPLIT_WANT_NONEMPTY)) {
514 base::FilePath path(line);
515 if (CallDirectoryExistsOnUIThread(path))
516 continue;
517 filenames_fp.push_back(path);
518 }
519
520 if (filenames_fp.empty()) {
521 FileNotSelected(params);
522 return;
523 }
524 MultiFilesSelected(filenames_fp, params);
525 }
526
527 } // namespace libgtk2ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698