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

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