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

Side by Side Diff: chrome/browser/ui/gtk/dialogs_kde.cc

Issue 7885002: Added support for KDE file dialogs. (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: '' Created 9 years, 3 months 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 | Annotate | Revision Log
« no previous file with comments | « chrome/browser/ui/gtk/dialogs_common.cc ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(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 std::string& type, const std::string& title,
59 const FilePath& default_path, gfx::NativeWindow parent,
60 bool file_operation, bool multiple_selection, void* params,
61 void (SelectFileDialogImplKDE::*callback)(const std::string&,
62 int, void*));
63
64 // Notifies the listener that a single file was chosen.
65 void FileSelected(const FilePath& path, void* params);
66
67 // Notifies the listener that multiple files were chosen.
68 void MultiFilesSelected(const std::vector<FilePath>& files, void* params);
69
70 // Notifies the listener that no file was chosen (the action was canceled).
71 // Dialog is passed so we can find that |params| pointer that was passed to
72 // us when we were told to show the dialog.
73 void FileNotSelected(void *params);
74
75 void CreateSelectFolderDialog(const std::string& title,
76 const FilePath& default_path,
77 gfx::NativeWindow parent, void* params);
78
79 void CreateFileOpenDialog(const std::string& title,
80 const FilePath& default_path,
81 gfx::NativeWindow parent, void* params);
82
83 void CreateMultiFileOpenDialog(const std::string& title,
84 const FilePath& default_path,
85 gfx::NativeWindow parent, void* params);
86
87 void CreateSaveAsDialog(const std::string& title,
88 const FilePath& default_path,
89 gfx::NativeWindow parent, void* params);
90
91 // Common function for OnSelectSingleFileDialogResponse and
92 // OnSelectSingleFolderDialogResponse.
93 void SelectSingleFileHelper(const std::string& output, int exit_code,
94 void* params, bool allow_folder);
95
96 void OnSelectSingleFileDialogResponse(const std::string& output,
97 int exit_code, void* params);
98 void OnSelectMultiFileDialogResponse(const std::string& output,
99 int exit_code, void* params);
100 void OnSelectSingleFolderDialogResponse(const std::string& output,
101 int exit_code, void* params);
102
103 DISALLOW_COPY_AND_ASSIGN(SelectFileDialogImplKDE);
104 };
105
106 SelectFileDialogImpl* SelectFileDialogImpl::NewSelectFileDialogImplKDE(
107 Listener* listener) {
108 return new SelectFileDialogImplKDE(listener);
109 }
110
111 SelectFileDialogImplKDE::SelectFileDialogImplKDE(Listener* listener)
112 : SelectFileDialogImpl(listener) {
113 }
114
115 SelectFileDialogImplKDE::~SelectFileDialogImplKDE() {
116 }
117
118 // We ignore |default_extension|.
119 void SelectFileDialogImplKDE::SelectFileImpl(
120 Type type,
121 const string16& title,
122 const FilePath& default_path,
123 const FileTypeInfo* file_types,
124 int file_type_index,
125 const FilePath::StringType& default_extension,
126 gfx::NativeWindow owning_window,
127 void* params) {
128 type_ = type;
129 // |owning_window| can be null when user right-clicks on a downloadable item
130 // and chooses 'Open Link in New Tab' when 'Ask where to save each file
131 // before downloading.' preference is turned on. (http://crbug.com/29213)
132 if (owning_window)
133 parents_.insert(owning_window);
134
135 std::string title_string = UTF16ToUTF8(title);
136
137 file_type_index_ = file_type_index;
138 if (file_types)
139 file_types_ = *file_types;
140 else
141 file_types_.include_all_files = true;
142
143 switch (type) {
144 case SELECT_FOLDER:
145 CreateSelectFolderDialog(title_string, default_path,
146 owning_window, params);
147 return;
148 case SELECT_OPEN_FILE:
149 CreateFileOpenDialog(title_string, default_path, owning_window,
150 params);
151 return;
152 case SELECT_OPEN_MULTI_FILE:
153 CreateMultiFileOpenDialog(title_string, default_path,
154 owning_window, params);
155 return;
156 case SELECT_SAVEAS_FILE:
157 CreateSaveAsDialog(title_string, default_path, owning_window,
158 params);
159 return;
160 default:
161 NOTREACHED();
162 return;
163 }
164 }
165
166 std::string SelectFileDialogImplKDE::GetMimeTypeFilterString() {
167 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
168 std::string filter_string;
169 // We need a filter set because the same mime type can appear multiple times.
170 std::set<std::string> filter_set;
171 for (size_t i = 0; i < file_types_.extensions.size(); ++i) {
172 for (size_t j = 0; j < file_types_.extensions[i].size(); ++j) {
173 if (!file_types_.extensions[i][j].empty()) {
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 it != filter_set.end(); ++it) {
188 filter_string.append(*it + " ");
189 }
190 return filter_string;
191 }
192
193 void SelectFileDialogImplKDE::CallKDialogOutput(
194 const std::string& type, const std::string& title,
195 const FilePath& default_path, gfx::NativeWindow parent,
196 bool file_operation, bool multiple_selection, void* params,
197 void (SelectFileDialogImplKDE::*callback)(const std::string&, int, void*)) {
198 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
199 CommandLine command_line(FilePath("kdialog"));
200 GetKDialogCommandLine(type, title, default_path, parent, file_operation,
201 multiple_selection, &command_line);
202 std::string output;
203 int exit_code;
204 // Get output from KDialog
205 base::GetAppOutputWithExitCode(command_line, &output, &exit_code);
206 if (!output.empty())
207 output.erase(output.size() - 1);
208 // Now the dialog is no longer showing. We can erase its parent from the
209 // parent set.
210 std::set<GtkWindow*>::iterator iter = parents_.find(parent);
211 if (iter != parents_.end())
212 parents_.erase(iter);
213 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
214 NewRunnableMethod(this, callback, output, exit_code, params));
215 }
216
217 void SelectFileDialogImplKDE::GetKDialogCommandLine(const std::string& type,
218 const std::string& title, const FilePath& path,
219 gfx::NativeWindow parent, bool file_operation, bool multiple_selection,
220 CommandLine* command_line) {
221 if (!command_line) {
222 VLOG(1) << "Command line for KDialog is NULL" << std::endl;
223 return;
224 }
225 // Attach to the current Chrome window.
226 GdkWindow* gdk_window =
227 gtk_widget_get_window(GTK_WIDGET((parent)));
228 int window_id = GDK_DRAWABLE_XID(gdk_window);
229 command_line->AppendSwitchNative("--attach", base::IntToString(window_id));
230 // Set the correct title for the dialog.
231 if (!title.empty()) {
232 command_line->AppendSwitchNative("--title", title);
233 }
234 // Enable multiple file selection if we need to.
235 if (multiple_selection) {
236 command_line->AppendSwitch("--multiple");
237 command_line->AppendSwitch("--separate-output");
238 }
239 command_line->AppendSwitch(type);
240 // The path should never be empty. If it is, set it to PWD.
241 if (path.empty()) {
242 command_line->AppendArgPath(FilePath("."));
243 } else {
244 command_line->AppendArgPath(path);
245 }
246 // Depending on the type of the operation we need, get the path to the
247 // file/folder and set up mime type filters.
248 if (file_operation) {
249 command_line->AppendArg(GetMimeTypeFilterString());
250 }
251 VLOG(1) << "KDialog command line: "
252 << command_line->GetCommandLineString() << std::endl;
253 }
254
255 void SelectFileDialogImplKDE::FileSelected(const FilePath& path, void* params) {
256 if (type_ == SELECT_SAVEAS_FILE)
257 *last_saved_path_ = path.DirName();
258 else if (type_ == SELECT_OPEN_FILE)
259 *last_opened_path_ = path.DirName();
260 else if (type_ == SELECT_FOLDER)
261 *last_opened_path_ = path;
262 else
263 NOTREACHED();
264 if (listener_) { // What does the filter index actually do?
265 // TODO(dfilimon): Get a reasonable index value from somewhere.
266 listener_->FileSelected(path, 1, params);
267 }
268 }
269
270 void SelectFileDialogImplKDE::MultiFilesSelected(
271 const std::vector<FilePath>& files, void* params) {
272 *last_opened_path_ = files[0].DirName();
273 if (listener_)
274 listener_->MultiFilesSelected(files, params);
275 }
276
277 void SelectFileDialogImplKDE::FileNotSelected(void* params) {
278 if (listener_)
279 listener_->FileSelectionCanceled(params);
280 }
281
282 void SelectFileDialogImplKDE::CreateSelectFolderDialog(
283 const std::string& title, const FilePath& default_path,
284 gfx::NativeWindow parent, void *params) {
285 std::string title_string = !title.empty() ? title :
286 l10n_util::GetStringUTF8(IDS_SELECT_FOLDER_DIALOG_TITLE);
287
288 Task* dialog_task =
289 NewRunnableMethod(
290 this, &SelectFileDialogImplKDE::CallKDialogOutput,
291 std::string("--getexistingdirectory"), title,
292 default_path.empty() ? *last_opened_path_ : default_path,
293 parent, false, false, params,
294 &SelectFileDialogImplKDE::OnSelectSingleFolderDialogResponse);
295 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, dialog_task);
296 }
297
298 void SelectFileDialogImplKDE::CreateFileOpenDialog(
299 const std::string& title, const FilePath& default_path,
300 gfx::NativeWindow parent, void* params) {
301 std::string title_string = !title.empty() ? title :
302 l10n_util::GetStringUTF8(IDS_OPEN_FILE_DIALOG_TITLE);
303
304 Task* dialog_task =
305 NewRunnableMethod(
306 this, &SelectFileDialogImplKDE::CallKDialogOutput,
307 std::string("--getopenfilename"), title,
308 default_path.empty() ? *last_opened_path_ : default_path,
309 parent, true, false, params,
310 &SelectFileDialogImplKDE::OnSelectSingleFileDialogResponse);
311 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, dialog_task);
312 }
313
314 void SelectFileDialogImplKDE::CreateMultiFileOpenDialog(
315 const std::string& title, const FilePath& default_path,
316 gfx::NativeWindow parent, void* params) {
317 std::string title_string = !title.empty() ? title :
318 l10n_util::GetStringUTF8(IDS_OPEN_FILES_DIALOG_TITLE);
319
320 Task* dialog_task =
321 NewRunnableMethod(
322 this, &SelectFileDialogImplKDE::CallKDialogOutput,
323 std::string("--getopenfilename"), title,
324 default_path.empty() ? *last_opened_path_ : default_path,
325 parent, true, true, params,
326 &SelectFileDialogImplKDE::OnSelectMultiFileDialogResponse);
327 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, dialog_task);
328 }
329
330 void SelectFileDialogImplKDE::CreateSaveAsDialog(
331 const std::string& title, const FilePath& default_path,
332 gfx::NativeWindow parent, void* params) {
333 std::string title_string = !title.empty() ? title :
334 l10n_util::GetStringUTF8(IDS_SAVE_AS_DIALOG_TITLE);
335
336 Task* dialog_task =
337 NewRunnableMethod(
338 this, &SelectFileDialogImplKDE::CallKDialogOutput,
339 std::string("--getsavefilename"), title,
340 default_path.empty() ? *last_saved_path_ : default_path,
341 parent, true, false, params,
342 &SelectFileDialogImplKDE::OnSelectSingleFileDialogResponse);
343 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, dialog_task);
344 }
345
346 void SelectFileDialogImplKDE::SelectSingleFileHelper(const std::string& output,
347 int exit_code, void* params, bool allow_folder) {
348 VLOG(1) << "[kdialog] SingleFileResponse: " << output << std::endl;
349 if (exit_code != 0 || output.empty()) {
350 FileNotSelected(params);
351 return;
352 }
353
354 FilePath path(output);
355 if (allow_folder) {
356 FileSelected(path, params);
357 return;
358 }
359
360 if (CallDirectoryExistsOnUIThread(path))
361 FileNotSelected(params);
362 else
363 FileSelected(path, params);
364 }
365
366 void SelectFileDialogImplKDE::OnSelectSingleFileDialogResponse(
367 const std::string& output, int exit_code, void* params) {
368 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
369 SelectSingleFileHelper(output, exit_code, params, false);
370 }
371
372 void SelectFileDialogImplKDE::OnSelectSingleFolderDialogResponse(
373 const std::string& output, int exit_code, void* params) {
374 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
375 SelectSingleFileHelper(output, exit_code, params, true);
376 }
377
378 void SelectFileDialogImplKDE::OnSelectMultiFileDialogResponse(
379 const std::string& output, int exit_code, void* params) {
380 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
381 VLOG(1) << "[kdialog] MultiFileResponse: " << output << std::endl;
382
383 if (exit_code != 0 || output.empty()) {
384 FileNotSelected(params);
385 return;
386 }
387
388 std::vector<std::string> filenames;
389 Tokenize(output, "\n", &filenames);
390 std::vector<FilePath> filenames_fp;
391 for (std::vector<std::string>::iterator iter = filenames.begin();
392 iter != filenames.end(); ++iter) {
393 FilePath path(*iter);
394 if (CallDirectoryExistsOnUIThread(path))
395 continue;
396 filenames_fp.push_back(path);
397 }
398
399 if (filenames_fp.empty()) {
400 FileNotSelected(params);
401 return;
402 }
403 MultiFilesSelected(filenames_fp, params);
404 }
405
OLDNEW
« no previous file with comments | « chrome/browser/ui/gtk/dialogs_common.cc ('k') | chrome/chrome_browser.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698