Commit from LXDE Translation Project with Pootle by user klemenkosir.: 149 of 149...
[lxde/pcmanfm.git] / src / pcmanfm.c
CommitLineData
b6e3c554
HJYP
1/*
2 * pcmanfm.c
d3451adb 3 *
7b7d923f 4 * Copyright 2009 - 2010 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
d3451adb 5 *
b6e3c554
HJYP
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
d3451adb 10 *
b6e3c554
HJYP
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
d3451adb 15 *
b6e3c554
HJYP
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
08e70fea 22#ifdef HAVE_CONFIG_H
b6e3c554 23#include <config.h>
08e70fea
HJYP
24#endif
25
b6e3c554 26#include <gtk/gtk.h>
1098cfe7 27#include <gdk/gdkx.h>
b6e3c554 28#include <stdio.h>
f8f2bfad
HJYP
29#include <glib/gi18n.h>
30
31#include <stdlib.h>
32#include <string.h>
33/* socket is used to keep single instance */
34#include <sys/types.h>
f8f2bfad
HJYP
35#include <signal.h>
36#include <unistd.h> /* for getcwd */
b6e3c554 37
20c0bc9a 38#include <libfm/fm-gtk.h>
4d55886c 39#include "app-config.h"
b6e3c554 40#include "main-win.h"
8505a8ef 41#include "desktop.h"
71f82759 42#include "volume-manager.h"
c5fccf1d 43#include "pref.h"
df6826e0 44#include "pcmanfm.h"
cc448d7f 45#include "single-inst.h"
f8f2bfad 46
94435bc3 47static int signal_pipe[2] = {-1, -1};
f8f2bfad
HJYP
48gboolean daemon_mode = FALSE;
49
f8f2bfad 50static char** files_to_open = NULL;
cc448d7f 51static int n_files_to_open = 0;
f970e846 52static char* profile = NULL;
f8f2bfad
HJYP
53static gboolean no_desktop = FALSE;
54static gboolean show_desktop = FALSE;
55static gboolean desktop_off = FALSE;
56static gboolean desktop_running = FALSE;
107cdb22 57/* static gboolean new_tab = FALSE; */
f8f2bfad
HJYP
58static int show_pref = 0;
59static gboolean desktop_pref = FALSE;
60static char* set_wallpaper = NULL;
107cdb22 61static char* wallpaper_mode = NULL;
f8f2bfad 62static gboolean find_files = FALSE;
5b890032 63static char* ipc_cwd = NULL;
f8f2bfad
HJYP
64
65static int n_pcmanfm_ref = 0;
66
67static GOptionEntry opt_entries[] =
8505a8ef 68{
cda6259f 69 /* options only acceptable by first pcmanfm instance. These options are not passed through IPC */
a48f9fc8 70 { "profile", 'p', 0, G_OPTION_ARG_STRING, &profile, N_("Name of configuration profile"), "<profile name>" },
cda6259f
HJYP
71 { "daemon-mode", 'd', 0, G_OPTION_ARG_NONE, &daemon_mode, N_("Run PCManFM as a daemon"), NULL },
72 { "no-desktop", '\0', 0, G_OPTION_ARG_NONE, &no_desktop, N_("No function. Just to be compatible with nautilus"), NULL },
73
74 /* options that are acceptable for every instance of pcmanfm and will be passed through IPC. */
f8f2bfad
HJYP
75 { "desktop", '\0', 0, G_OPTION_ARG_NONE, &show_desktop, N_("Launch desktop manager"), NULL },
76 { "desktop-off", '\0', 0, G_OPTION_ARG_NONE, &desktop_off, N_("Turn off desktop manager if it's running"), NULL },
f8f2bfad 77 { "desktop-pref", '\0', 0, G_OPTION_ARG_NONE, &desktop_pref, N_("Open desktop preference dialog"), NULL },
f970e846 78 { "set-wallpaper", 'w', 0, G_OPTION_ARG_FILENAME, &set_wallpaper, N_("Set desktop wallpaper"), N_("<image file>") },
107cdb22 79 { "wallpaper-mode", '\0', 0, G_OPTION_ARG_STRING, &wallpaper_mode, N_("Set mode of desktop wallpaper. <mode>=(color|stretch|fit|center|tile)"), N_("<mode>") },
a48f9fc8 80 { "show-pref", '\0', 0, G_OPTION_ARG_INT, &show_pref, N_("Open preference dialog. 'n' is number of the page you want to show (1, 2, 3...)."), "n" },
1d071341 81 /* { "find-files", 'f', 0, G_OPTION_ARG_NONE, &find_files, N_("Open Find Files utility"), NULL }, */
f8f2bfad
HJYP
82 {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &files_to_open, NULL, N_("[FILE1, FILE2,...]")},
83 { NULL }
84};
85
107cdb22
HJYP
86static const char* valid_wallpaper_modes[] = {"color", "stretch", "fit", "center", "tile"};
87
f8f2bfad 88static gboolean pcmanfm_run();
b6e3c554 89
94435bc3
HJYP
90/* it's not safe to call gtk+ functions in unix signal handler
91 * since the process is interrupted here and the state of gtk+ is unpredictable. */
92static void unix_signal_handler(int sig_num)
93{
94 /* postpond the signal handling by using a pipe */
95 write(signal_pipe[1], &sig_num, sizeof(sig_num));
96}
97
98static gboolean on_unix_signal(GIOChannel* ch, GIOCondition cond, gpointer user_data)
99{
100 int sig_num;
101 g_io_channel_read_chars(ch, &sig_num, 1, NULL, NULL);
102 switch(sig_num)
103 {
104 case SIGTERM:
105 default:
106 gtk_main_quit();
107 }
108 return TRUE;
109}
110
1098cfe7 111static void single_inst_cb(const char* cwd, int screen_num)
cc448d7f 112{
cda6259f
HJYP
113 g_free(ipc_cwd);
114 ipc_cwd = g_strdup(cwd);
115
116 if(files_to_open)
cc448d7f 117 {
cda6259f 118 int i;
6033d802 119 n_files_to_open = g_strv_length(files_to_open);
cda6259f
HJYP
120 /* canonicalize filename if needed. */
121 for(i = 0; i < n_files_to_open; ++i)
cc448d7f 122 {
cda6259f
HJYP
123 char* file = files_to_open[i];
124 char* scheme = g_uri_parse_scheme(file);
6033d802 125 g_debug("file: %s", file);
cda6259f 126 if(scheme) /* a valid URI */
cc448d7f 127 {
cda6259f
HJYP
128 /* FIXME: should we canonicalize URIs? and how about file:///? */
129 g_free(scheme);
130 }
131 else /* a file path */
132 {
133 files_to_open[i] = fm_canonicalize_filename(file, cwd);
134 g_free(file);
cc448d7f 135 }
cc448d7f 136 }
cc448d7f 137 }
cda6259f 138 pcmanfm_run();
cc448d7f
HJYP
139}
140
b6e3c554
HJYP
141int main(int argc, char** argv)
142{
4d55886c 143 FmConfig* config;
f8f2bfad 144 GError* err = NULL;
cda6259f 145
f8f2bfad
HJYP
146#ifdef ENABLE_NLS
147 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
148 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
149 textdomain ( GETTEXT_PACKAGE );
150#endif
151
152 /* initialize GTK+ and parse the command line arguments */
153 if(G_UNLIKELY(!gtk_init_with_args(&argc, &argv, "", opt_entries, GETTEXT_PACKAGE, &err)))
154 {
155 g_printf("%s\n", err->message);
c5fccf1d 156 g_error_free(err);
f8f2bfad
HJYP
157 return 1;
158 }
159
cc448d7f 160 /* ensure that there is only one instance of pcmanfm. */
1098cfe7 161 switch(single_inst_init("pcmanfm", single_inst_cb, opt_entries + 3, gdk_x11_get_default_screen()))
cc448d7f
HJYP
162 {
163 case SINGLE_INST_CLIENT: /* we're not the first instance. */
cda6259f 164 single_inst_finalize();
cc448d7f
HJYP
165 gdk_notify_startup_complete();
166 return 0;
167 case SINGLE_INST_ERROR: /* error happened. */
168 single_inst_finalize();
169 return 1;
170 }
f8f2bfad 171
94435bc3
HJYP
172 if(pipe(signal_pipe) == 0)
173 {
174 GIOChannel* ch = g_io_channel_unix_new(signal_pipe[0]);
175 g_io_add_watch(ch, G_IO_IN|G_IO_PRI, (GIOFunc)on_unix_signal, NULL);
176 g_io_channel_unref(ch);
177
178 /* intercept signals */
ae2947d9
HJYP
179 // signal( SIGPIPE, SIG_IGN );
180 signal( SIGHUP, unix_signal_handler );
94435bc3 181 signal( SIGTERM, unix_signal_handler );
ae2947d9
HJYP
182 signal( SIGPOLL, unix_signal_handler );
183 signal( SIGHUP, unix_signal_handler );
94435bc3 184 }
b6e3c554 185
f970e846 186 config = fm_app_config_new(); /* this automatically load libfm config file. */
17d12cc9 187
f970e846 188 /* load pcmanfm-specific config file */
17d12cc9 189 fm_app_config_load_from_profile(FM_APP_CONFIG(config), profile);
f970e846 190
94435bc3 191 fm_gtk_init(config);
f8f2bfad
HJYP
192 /* the main part */
193 if(pcmanfm_run())
194 {
71f82759 195 fm_volume_manager_init();
94435bc3 196 gtk_main();
f8f2bfad
HJYP
197 if(desktop_running)
198 fm_desktop_manager_finalize();
f8f2bfad 199 fm_config_save(config, NULL); /* save libfm config */
17d12cc9 200 fm_app_config_save_profile((FmAppConfig*)config, profile); /* save pcmanfm config */
71f82759 201 fm_volume_manager_finalize();
f8f2bfad 202 }
cc448d7f
HJYP
203
204 single_inst_finalize();
f8f2bfad 205 fm_gtk_finalize();
ae2947d9 206
f8f2bfad 207 g_object_unref(config);
94435bc3 208 return 0;
f8f2bfad
HJYP
209}
210
ee12f37e
HJYP
211static FmJobErrorAction on_file_info_job_error(FmFileInfoJob* job, GError* err, FmJobErrorSeverity severity, gpointer user_data)
212{
16a17d6b 213 if(err->domain == G_IO_ERROR)
ee12f37e 214 {
16a17d6b
HJYP
215 if(err->code == G_IO_ERROR_NOT_MOUNTED)
216 {
217 if(fm_mount_path(NULL, fm_file_info_job_get_current(job), TRUE))
218 return FM_JOB_RETRY;
219 }
220 else if(err->code == G_IO_ERROR_FAILED_HANDLED)
221 return FM_JOB_CONTINUE;
ee12f37e 222 }
16a17d6b 223 fm_show_error(NULL, err->message);
ee12f37e
HJYP
224 return FM_JOB_CONTINUE;
225}
f8f2bfad
HJYP
226
227gboolean pcmanfm_run()
228{
229 gboolean ret = TRUE;
f8f2bfad 230
f8f2bfad
HJYP
231 if(!files_to_open)
232 {
233 /* Launch desktop manager */
234 if(show_desktop)
235 {
236 if(!desktop_running)
237 {
238 fm_desktop_manager_init();
239 desktop_running = TRUE;
240 }
19abb8bf 241 show_desktop = FALSE;
f8f2bfad
HJYP
242 return TRUE;
243 }
244 else if(desktop_off)
245 {
246 if(desktop_running)
247 {
248 desktop_running = FALSE;
249 fm_desktop_manager_finalize();
250 }
19abb8bf 251 desktop_off = FALSE;
f8f2bfad
HJYP
252 return FALSE;
253 }
254 else if(show_pref > 0)
255 {
c5fccf1d
HJYP
256 fm_edit_preference(NULL, show_pref - 1);
257 show_pref = 0;
f8f2bfad
HJYP
258 return TRUE;
259 }
260 else if(desktop_pref)
261 {
c5fccf1d 262 fm_desktop_preference();
19abb8bf 263 desktop_pref = FALSE;
f8f2bfad
HJYP
264 return TRUE;
265 }
107cdb22 266 else
f8f2bfad 267 {
107cdb22
HJYP
268 gboolean need_to_exit = (wallpaper_mode || set_wallpaper);
269 gboolean wallpaper_changed = FALSE;
270 if(set_wallpaper) /* a new wallpaper is assigned */
271 {
272 /* g_debug("\'%s\'", set_wallpaper); */
273 /* Make sure this is a support image file. */
274 if(gdk_pixbuf_get_file_info(set_wallpaper, NULL, NULL))
275 {
276 if(app_config->wallpaper)
277 g_free(app_config->wallpaper);
278 app_config->wallpaper = set_wallpaper;
279 set_wallpaper = NULL;
280 if(! wallpaper_mode) /* if wallpaper mode is not specified */
281 {
282 /* do not use solid color mode; otherwise wallpaper won't be shown. */
283 if(app_config->wallpaper_mode == FM_WP_COLOR)
284 app_config->wallpaper_mode = FM_WP_FIT;
285 }
286 wallpaper_changed = TRUE;
287 }
288 }
289
290 if(wallpaper_mode)
291 {
292 int i = 0;
293 for(i = 0; i < G_N_ELEMENTS(valid_wallpaper_modes); ++i)
294 {
295 if(strcmp(valid_wallpaper_modes[i], wallpaper_mode) == 0)
296 {
297 if(i != app_config->wallpaper_mode)
298 {
299 app_config->wallpaper_mode = i;
300 wallpaper_changed = TRUE;
301 }
302 break;
303 }
304 }
305 g_free(wallpaper_mode);
306 wallpaper_mode = NULL;
307 }
308
309 if(wallpaper_changed)
f8f2bfad 310 {
4b3e1fe9 311 fm_config_emit_changed(FM_CONFIG(app_config), "wallpaper");
17d12cc9 312 fm_app_config_save_profile(app_config, profile);
f8f2bfad 313 }
107cdb22
HJYP
314
315 if(need_to_exit)
316 return FALSE;
f8f2bfad
HJYP
317 }
318 }
319
320 if(G_UNLIKELY(find_files))
321 {
322 /* FIXME: find files */
323 }
324 else
325 {
df6826e0
HJYP
326 if(files_to_open)
327 {
328 char** filename;
0a053694 329 FmJob* job = fm_file_info_job_new(NULL, 0);
cacf261a 330 FmPath* cwd = NULL;
d951adb8 331 GList* infos;
df6826e0
HJYP
332 for(filename=files_to_open; *filename; ++filename)
333 {
cacf261a
HJYP
334 FmPath* path;
335 if( **filename == '/' || strstr(*filename, ":/") ) /* absolute path or URI */
336 path = fm_path_new(*filename);
0d8dce1a
HJYP
337 else if( strcmp(*filename, "~") == 0 ) /* special case for home dir */
338 {
339 path = fm_path_get_home();
340 fm_main_win_add_win(NULL, path);
341 continue;
342 }
cacf261a
HJYP
343 else /* basename */
344 {
345 if(G_UNLIKELY(!cwd))
346 {
347 /* FIXME: This won't work if those filenames are passed via IPC since the receiving process has different cwd. */
348 char* cwd_str = g_get_current_dir();
349 cwd = fm_path_new(cwd_str);
350 g_free(cwd_str);
351 }
352 path = fm_path_new_relative(cwd, *filename);
353 }
4b3e1fe9 354 fm_file_info_job_add(FM_FILE_INFO_JOB(job), path);
df6826e0
HJYP
355 fm_path_unref(path);
356 }
cacf261a
HJYP
357 if(cwd)
358 fm_path_unref(cwd);
ee12f37e 359 g_signal_connect(job, "error", G_CALLBACK(on_file_info_job_error), NULL);
a48f9fc8 360 fm_job_run_sync_with_mainloop(job);
d951adb8
HJYP
361 infos = fm_list_peek_head_link(FM_FILE_INFO_JOB(job)->file_infos);
362 fm_launch_files_simple(NULL, NULL, infos, pcmanfm_open_folder, NULL);
df6826e0 363 g_object_unref(job);
cacf261a 364 ret = (n_pcmanfm_ref >= 1); /* if there is opened window, return true to run the main loop. */
2b46b521
HJYP
365
366 g_strfreev(files_to_open);
367 files_to_open = NULL;
df6826e0
HJYP
368 }
369 else
f8f2bfad 370 {
6d95cbc5
HJYP
371 if(!daemon_mode)
372 {
373 FmPath* path;
374 char* cwd = ipc_cwd ? ipc_cwd : g_get_current_dir();
375 path = fm_path_new_for_path(cwd);
376 fm_main_win_add_win(NULL, path);
377 fm_path_unref(path);
378 g_free(cwd);
379 ipc_cwd = NULL;
380 }
f8f2bfad
HJYP
381 }
382 }
383 return ret;
384}
385
386/* After opening any window/dialog/tool, this should be called. */
387void pcmanfm_ref()
388{
389 ++n_pcmanfm_ref;
390 /* g_debug("ref: %d", n_pcmanfm_ref); */
391}
392
393/* After closing any window/dialog/tool, this should be called.
394 * If the last window is closed and we are not a deamon, pcmanfm will quit.
395 */
396void pcmanfm_unref()
397{
398 --n_pcmanfm_ref;
399 /* g_debug("unref: %d, daemon_mode=%d, desktop_running=%d", n_pcmanfm_ref, daemon_mode, desktop_running); */
400 if( 0 == n_pcmanfm_ref && !daemon_mode && !desktop_running )
401 gtk_main_quit();
b6e3c554 402}
df6826e0
HJYP
403
404gboolean pcmanfm_open_folder(GAppLaunchContext* ctx, GList* folder_infos, gpointer user_data, GError** err)
405{
df6826e0
HJYP
406 GList* l = folder_infos;
407 for(; l; l=l->next)
408 {
409 FmFileInfo* fi = (FmFileInfo*)l->data;
410 fm_main_win_open_in_last_active(fi->path);
411 }
412 return TRUE;
413}
dcecce78
HJYP
414
415void pcmanfm_save_config()
416{
417 fm_config_save(fm_config, NULL);
17d12cc9 418 fm_app_config_save_profile(app_config, profile);
dcecce78 419}
c56a211a
HJYP
420
421void pcmanfm_open_folder_in_terminal(GtkWindow* parent, FmPath* dir)
422{
423 GAppInfo* app;
c56a211a
HJYP
424 char** argv;
425 int argc;
426 if(!fm_config->terminal)
427 {
428 fm_show_error(parent, _("Terminal emulator is not set."));
429 fm_edit_preference(parent, PREF_ADVANCED);
430 return;
431 }
432 if(!g_shell_parse_argv(fm_config->terminal, &argc, &argv, NULL))
433 return;
434 app = g_app_info_create_from_commandline(argv[0], NULL, 0, NULL);
435 g_strfreev(argv);
436 if(app)
437 {
438 GError* err = NULL;
439 GAppLaunchContext* ctx = gdk_app_launch_context_new();
440 char* cwd_str;
441
442 if(fm_path_is_native(dir))
443 cwd_str = fm_path_to_str(dir);
444 else
445 {
446 GFile* gf = fm_path_to_gfile(dir);
447 cwd_str = g_file_get_path(gf);
448 g_object_unref(gf);
449 }
99476ede 450 gdk_app_launch_context_set_screen(GDK_APP_LAUNCH_CONTEXT(ctx), parent ? gtk_widget_get_screen(GTK_WIDGET(parent)) : gdk_screen_get_default());
c56a211a
HJYP
451 gdk_app_launch_context_set_timestamp(GDK_APP_LAUNCH_CONTEXT(ctx), gtk_get_current_event_time());
452 g_chdir(cwd_str); /* FIXME: currently we don't have better way for this. maybe a wrapper script? */
453 g_free(cwd_str);
454 if(!g_app_info_launch(app, NULL, ctx, &err))
455 {
456 fm_show_error(parent, err->message);
457 g_error_free(err);
458 }
459 g_object_unref(ctx);
460 g_object_unref(app);
461 }
462}
7cb06c27
HJYP
463
464/* FIXME: Need to load content of ~/Templates and list available templates in popup menus. */
465void pcmanfm_create_new(GtkWindow* parent, FmPath* cwd, const char* templ)
466{
467 GError* err = NULL;
468 FmPath* dest;
469 char* basename;
470_retry:
471 basename = fm_get_user_input(parent, _("Create New..."), _("Enter a name for the newly created file:"), _("New"));
472 if(!basename)
473 return;
474
475 dest = fm_path_new_child(cwd, basename);
476 g_free(basename);
477
478 if( templ == TEMPL_NAME_FOLDER )
479 {
480 GFile* gf = fm_path_to_gfile(dest);
481 if(!g_file_make_directory(gf, NULL, &err))
482 {
98daf506 483 if(err->domain == G_IO_ERROR && err->code == G_IO_ERROR_EXISTS)
7cb06c27
HJYP
484 {
485 fm_path_unref(dest);
486 g_error_free(err);
487 g_object_unref(gf);
488 err = NULL;
489 goto _retry;
490 }
491 fm_show_error(parent, err->message);
492 g_error_free(err);
493 }
494
495 if(!err) /* select the newly created file */
496 {
497 /*FIXME: this doesn't work since the newly created file will
498 * only be shown after file-created event was fired on its
499 * folder's monitor and after FmFolder handles it in idle
500 * handler. So, we cannot select it since it's not yet in
501 * the folder model now. */
502 /* fm_folder_view_select_file_path(fv, dest); */
503 }
504 g_object_unref(gf);
505 }
506 else if( templ == TEMPL_NAME_BLANK )
507 {
508 GFile* gf = fm_path_to_gfile(dest);
509 GFileOutputStream* f = g_file_create(gf, G_FILE_CREATE_NONE, NULL, &err);
510 if(f)
511 {
512 g_output_stream_close(G_OUTPUT_STREAM(f), NULL, NULL);
513 g_object_unref(f);
514 }
515 else
516 {
98daf506 517 if(err->domain == G_IO_ERROR && err->code == G_IO_ERROR_EXISTS)
7cb06c27
HJYP
518 {
519 fm_path_unref(dest);
520 g_error_free(err);
521 g_object_unref(gf);
522 err = NULL;
523 goto _retry;
524 }
525 fm_show_error(parent, err->message);
526 g_error_free(err);
527 }
528
529 if(!err) /* select the newly created file */
530 {
531 /*FIXME: this doesn't work since the newly created file will
532 * only be shown after file-created event was fired on its
533 * folder's monitor and after FmFolder handles it in idle
534 * handler. So, we cannot select it since it's not yet in
535 * the folder model now. */
536 /* fm_folder_view_select_file_path(fv, dest); */
537 }
538 g_object_unref(gf);
539 }
540 else /* templates in ~/Templates */
541 {
542 FmPath* dir = fm_path_new(g_get_user_special_dir(G_USER_DIRECTORY_TEMPLATES));
543 FmPath* template = fm_path_new_child(dir, templ);
9afd9e19 544 fm_copy_file(parent, template, cwd);
7cb06c27
HJYP
545 fm_path_unref(template);
546 }
547 fm_path_unref(dest);
548}
92f8ca49
HJYP
549
550char* pcmanfm_get_profile_dir(gboolean create)
551{
552 char* dir = g_build_filename(g_get_user_config_dir(), "pcmanfm", profile ? profile : "default", NULL);
553 if(create)
554 g_mkdir_with_parents(dir, 0700);
555 return dir;
556}