1//      profile.vala
2//
3//      Copyright 2011 Hong Jen Yee (PCMan) <pcman.tw@pcman.tw@gmail.com>
4//
5//      This program is free software; you can redistribute it and/or modify
6//      it under the terms of the GNU General Public License as published by
7//      the Free Software Foundation; either version 2 of the License, or
8//      (at your option) any later version.
9//
10//      This program is distributed in the hope that it will be useful,
11//      but WITHOUT ANY WARRANTY; without even the implied warranty of
12//      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13//      GNU General Public License for more details.
14//
15//      You should have received a copy of the GNU General Public License
16//      along with this program; if not, write to the Free Software
17//      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18//      MA 02110-1301, USA.
19//
20//
21
22namespace Fm {
23
24public enum FileActionExecMode {
25	NORMAL,
26	TERMINAL,
27	EMBEDDED,
28	DISPLAY_OUTPUT
29}
30
31[Compact]
32public class FileActionProfile {
33
34	public FileActionProfile(KeyFile kf, string profile_name) {
35		id = profile_name;
36		string group_name = @"X-Action-Profile $profile_name";
37		name = Utils.key_file_get_string(kf, group_name, "Name");
38		exec = Utils.key_file_get_string(kf, group_name, "Exec");
39		// stdout.printf("id: %s, Exec: %s\n", id, exec);
40
41		path = Utils.key_file_get_string(kf, group_name, "Path");
42		var s = Utils.key_file_get_string(kf, group_name, "ExecutionMode");
43		if(s == "Normal")
44			exec_mode = FileActionExecMode.NORMAL;
45		else if( s == "Terminal")
46			exec_mode = FileActionExecMode.TERMINAL;
47		else if(s == "Embedded")
48			exec_mode = FileActionExecMode.EMBEDDED;
49		else if( s == "DisplayOutput")
50			exec_mode = FileActionExecMode.DISPLAY_OUTPUT;
51		else
52			exec_mode = FileActionExecMode.NORMAL;
53
54		startup_notify = Utils.key_file_get_bool(kf, group_name, "StartupNotify");
55		startup_wm_class = Utils.key_file_get_string(kf, group_name, "StartupWMClass");
56		exec_as = Utils.key_file_get_string(kf, group_name, "ExecuteAs");
57
58		condition = new FileActionCondition(kf, group_name);
59	}
60
61	private bool launch_once(AppLaunchContext ctx, FileInfo? first_file, List<FileInfo> files, out string? output) {
62		if(exec == null)
63			return false;
64		var exec_cmd = FileActionParameters.expand(exec, files, false, first_file);
65		stdout.printf("Profile: %s\nlaunch command: %s\n\n", id, exec_cmd);
66		bool ret = false;
67		if(exec_mode == FileActionExecMode.DISPLAY_OUTPUT) {
68			try{
69				int exit_status;
70				ret = Process.spawn_command_line_sync(exec_cmd, out output,
71													   null, out exit_status);
72				if(ret)
73					ret = (exit_status == 0);
74			}
75			catch(SpawnError err) {
76			}
77		}
78		else {
79			/*
80			AppInfoCreateFlags flags = AppInfoCreateFlags.NONE;
81			if(startup_notify)
82				flags |= AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION;
83			if(exec_mode == FileActionExecMode.TERMINAL ||
84			   exec_mode == FileActionExecMode.EMBEDDED)
85				flags |= AppInfoCreateFlags.NEEDS_TERMINAL;
86			GLib.AppInfo app = Fm.AppInfo.create_from_commandline(exec, null, flags);
87			stdout.printf("Execute command line: %s\n\n", exec);
88			ret = app.launch(null, ctx);
89			*/
90
91			// NOTE: we cannot use GAppInfo here since GAppInfo does
92			// command line parsing which involving %u, %f, and other
93			// code defined in desktop entry spec.
94			// This may conflict with DES EMA parameters.
95			// FIXME: so how to handle this cleaner?
96			// Maybe we should leave all %% alone and don't translate
97			// them to %. Then GAppInfo will translate them to %, not
98			// codes specified in DES.
99			try {
100				ret = Process.spawn_command_line_async(exec_cmd);
101			}
102			catch(SpawnError err) {
103			}
104		}
105		return ret;
106	}
107
108	public bool launch(AppLaunchContext ctx, List<FileInfo> files, out string? output) {
109		bool plural_form = FileActionParameters.is_plural(exec);
110		bool ret;
111		if(plural_form) { // plural form command, handle all files at a time
112			ret = launch_once(ctx, files.first().data, files, out output);
113		}
114		else { // singular form command, run once for each file
115			StringBuilder all_output = null;
116			if(output != null)
117				all_output = new StringBuilder();
118			foreach(unowned FileInfo fi in files) {
119				string one_output;
120				launch_once(ctx, fi, files, out one_output);
121				if(all_output != null && one_output != null) {
122					// FIXME: how to handle multiple output strings properly?
123					all_output.append(one_output); // is it ok to join them all?
124					all_output.append("\n");
125				}
126			}
127			if(all_output != null && output != null)
128				output = (owned) all_output.str;
129			ret = true;
130		}
131		return ret;
132	}
133
134	public bool match(List<FileInfo> files) {
135		stdout.printf("  match profile: %s\n", id);
136		return condition.match(files);
137	}
138
139	public string id;
140	public string? name;
141	public string exec;
142	public string? path;
143	public FileActionExecMode exec_mode;
144	public bool startup_notify;
145	public string? startup_wm_class;
146	public string? exec_as;
147
148	public FileActionCondition condition;
149}
150
151}
152