1/*
2Copyright (c) 2011 by Simon Schneegans
3
4This program is free software: you can redistribute it and/or modify it
5under the terms of the GNU General Public License as published by the Free
6Software Foundation, either version 3 of the License, or (at your option)
7any later version.
8
9This program is distributed in the hope that it will be useful, but WITHOUT
10ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12more details.
13
14You should have received a copy of the GNU General Public License along with
15this program.  If not, see <http://www.gnu.org/licenses/>.
16*/
17
18namespace GnomePie {
19
20/////////////////////////////////////////////////////////////////////////
21/// A window which allows selection of a new Slice which is about to be
22/// added to a Pie. It can be also used to edit an existing Slice
23/////////////////////////////////////////////////////////////////////////
24
25public class NewSliceWindow : GLib.Object {
26
27    /////////////////////////////////////////////////////////////////////
28    /// This signal gets emitted when the user confirms his selection.
29    /////////////////////////////////////////////////////////////////////
30
31    public signal void on_select(ActionGroup action, bool as_new_slice, int at_position);
32
33    /////////////////////////////////////////////////////////////////////
34    /// The contained list of slice types. It contains both: Groups and
35    /// single actions.
36    /////////////////////////////////////////////////////////////////////
37
38    private SliceTypeList slice_type_list = null;
39
40    /////////////////////////////////////////////////////////////////////
41    /// The IconSelectWindow used for icon selection for a Slice.
42    /////////////////////////////////////////////////////////////////////
43
44    private IconSelectWindow? icon_window = null;
45
46    /////////////////////////////////////////////////////////////////////
47    /// Some widgets of this window. Loaded by a ui-builder and stored
48    /// for later access.
49    /////////////////////////////////////////////////////////////////////
50
51    private Gtk.Dialog window = null;
52    private Gtk.Box name_box = null;
53    private Gtk.Box command_box = null;
54    private Gtk.Button icon_button = null;
55    private Gtk.Box no_options_box = null;
56    private Gtk.Box pie_box = null;
57    private Gtk.Box hotkey_box = null;
58    private Gtk.Box uri_box = null;
59    private Gtk.Box quickaction_box = null;
60    private Gtk.Image icon = null;
61    private Gtk.Entry name_entry = null;
62    private Gtk.Entry command_entry = null;
63    private Gtk.Entry uri_entry = null;
64    private Gtk.CheckButton quickaction_checkbutton = null;
65
66    /////////////////////////////////////////////////////////////////////
67    /// Two custom widgets. For Pie and hotkey selection respectively.
68    /////////////////////////////////////////////////////////////////////
69
70    private PieComboList pie_select = null;
71    private TriggerSelectButton key_select = null;
72
73    /////////////////////////////////////////////////////////////////////
74    /// These members store information on the currently selected Slice.
75    /////////////////////////////////////////////////////////////////////
76
77    private string current_type = "";
78    private string current_icon = "";
79    private string current_id = "";
80    private string current_custom_icon = "";
81    private string current_hotkey = "";
82    private string current_pie_to_open = "";
83
84    /////////////////////////////////////////////////////////////////////
85    /// The position of the edited Slice in its parent Pie.
86    /////////////////////////////////////////////////////////////////////
87
88    private int slice_position = 0;
89
90    /////////////////////////////////////////////////////////////////////
91    /// True, if the Slice i going to be added as a new Slice. Else it
92    /// will edit the Slice at slice_position in its parent Pie.
93    /////////////////////////////////////////////////////////////////////
94
95    private bool add_as_new_slice = true;
96
97    /////////////////////////////////////////////////////////////////////
98    /// C'tor creates a new window.
99    /////////////////////////////////////////////////////////////////////
100
101    public NewSliceWindow() {
102        try {
103
104            Gtk.Builder builder = new Gtk.Builder();
105
106            builder.add_from_file (Paths.ui_files + "/slice_select.ui");
107
108            this.slice_type_list = new SliceTypeList();
109            this.slice_type_list.on_select.connect((type, icon) => {
110
111                this.name_box.hide();
112                this.command_box.hide();
113                this.icon_button.sensitive = false;
114                this.no_options_box.hide();
115                this.pie_box.hide();
116                this.hotkey_box.hide();
117                this.uri_box.hide();
118                this.quickaction_box.hide();
119
120                this.current_type = type;
121
122                switch (type) {
123                    case "bookmarks": case "clipboard": case "devices":
124                    case "menu": case "session": case "window_list":
125                        this.no_options_box.show();
126                        this.set_icon(icon);
127                        break;
128                    case "app":
129                        this.name_box.show();
130                        this.command_box.show();
131                        this.quickaction_box.show();
132                        this.icon_button.sensitive = true;
133                        if (this.current_custom_icon == "") this.set_icon(icon);
134                        else                                this.set_icon(this.current_custom_icon);
135                        break;
136                    case "key":
137                        this.name_box.show();
138                        this.hotkey_box.show();
139                        this.quickaction_box.show();
140                        this.icon_button.sensitive = true;
141                        if (this.current_custom_icon == "") this.set_icon(icon);
142                        else                                this.set_icon(this.current_custom_icon);
143                        break;
144                    case "pie":
145                        this.pie_box.show();
146                        this.quickaction_box.show();
147                        this.set_icon(PieManager.all_pies[this.pie_select.current_id].icon);
148                        break;
149                    case "uri":
150                        this.name_box.show();
151                        this.uri_box.show();
152                        this.quickaction_box.show();
153                        this.icon_button.sensitive = true;
154                        if (this.current_custom_icon == "") this.set_icon(icon);
155                        else                                this.set_icon(this.current_custom_icon);
156                        break;
157                }
158            });
159
160            this.name_box = builder.get_object("name-box") as Gtk.Box;
161            this.command_box = builder.get_object("command-box") as Gtk.Box;
162            this.icon_button = builder.get_object("icon-button") as Gtk.Button;
163            this.no_options_box = builder.get_object("no-options-box") as Gtk.Box;
164            this.pie_box = builder.get_object("pie-box") as Gtk.Box;
165            this.pie_select = new PieComboList();
166            this.pie_select.on_select.connect((id) => {
167                this.current_pie_to_open = id;
168                this.set_icon(PieManager.all_pies[id].icon);
169            });
170
171            this.pie_box.pack_start(this.pie_select, true, true);
172
173            this.hotkey_box = builder.get_object("hotkey-box") as Gtk.Box;
174            this.key_select = new TriggerSelectButton(false);
175            this.hotkey_box.pack_start(this.key_select, false, true);
176            this.key_select.on_select.connect((trigger) => {
177                this.current_hotkey = trigger.name;
178            });
179
180            this.uri_box = builder.get_object("uri-box") as Gtk.Box;
181
182            this.name_entry = builder.get_object("name-entry") as Gtk.Entry;
183            this.uri_entry = builder.get_object("uri-entry") as Gtk.Entry;
184            this.command_entry = builder.get_object("command-entry") as Gtk.Entry;
185            this.quickaction_checkbutton = builder.get_object("quick-action-checkbutton") as Gtk.CheckButton;
186
187            this.quickaction_box = builder.get_object("quickaction-box") as Gtk.Box;
188            this.icon = builder.get_object("icon") as Gtk.Image;
189
190            this.icon_button.clicked.connect(on_icon_button_clicked);
191
192            var scroll_area = builder.get_object("slice-scrolledwindow") as Gtk.ScrolledWindow;
193                scroll_area.add(this.slice_type_list);
194
195            this.window = builder.get_object("window") as Gtk.Dialog;
196
197            (builder.get_object("ok-button") as Gtk.Button).clicked.connect(on_ok_button_clicked);
198            (builder.get_object("cancel-button") as Gtk.Button).clicked.connect(on_cancel_button_clicked);
199
200            this.window.delete_event.connect(this.window.hide_on_delete);
201
202        } catch (GLib.Error e) {
203            error("Could not load UI: %s\n", e.message);
204        }
205    }
206
207    /////////////////////////////////////////////////////////////////////
208    /// Sets the parent window, in order to make this window stay in
209    /// front.
210    /////////////////////////////////////////////////////////////////////
211
212    public void set_parent(Gtk.Window parent) {
213        this.window.set_transient_for(parent);
214    }
215
216    /////////////////////////////////////////////////////////////////////
217    /// Sows the window on the screen.
218    /////////////////////////////////////////////////////////////////////
219
220    public void show() {
221        this.slice_type_list.select_first();
222        this.pie_select.select_first();
223        this.key_select.set_trigger(new Trigger());
224        this.window.show_all();
225    }
226
227    /////////////////////////////////////////////////////////////////////
228    /// Reloads the window.
229    /////////////////////////////////////////////////////////////////////
230
231    public void reload() {
232        this.pie_select.reload();
233    }
234
235    /////////////////////////////////////////////////////////////////////
236    /// Makes all widgets display stuff according to the given action.
237    /////////////////////////////////////////////////////////////////////
238
239    public void set_action(ActionGroup group, int position) {
240        this.set_default(group.parent_id, position);
241
242        this.add_as_new_slice = false;
243        string type = "";
244
245        if (group.get_type().depth() == 2) {
246            var action = group.actions[0];
247            type = ActionRegistry.descriptions[action.get_type().name()].id;
248            this.select_type(type);
249
250            this.set_icon(action.icon);
251            this.quickaction_checkbutton.active = action.is_quickaction;
252            this.name_entry.text = action.name;
253
254            switch (type) {
255                case "app":
256                    this.current_custom_icon = action.icon;
257                    this.command_entry.text = action.real_command;
258                    break;
259                case "key":
260                    this.current_custom_icon = action.icon;
261                    this.current_hotkey = action.real_command;
262                    this.key_select.set_trigger(new Trigger.from_string(action.real_command));
263                    break;
264                case "pie":
265                    this.pie_select.select(action.real_command);
266                    break;
267                case "uri":
268                    this.current_custom_icon = action.icon;
269                    this.uri_entry.text = action.real_command;
270                    break;
271            }
272
273        } else {
274            type = GroupRegistry.descriptions[group.get_type().name()].id;
275            this.select_type(type);
276        }
277    }
278
279    /////////////////////////////////////////////////////////////////////
280    /// Selects a default action.
281    /////////////////////////////////////////////////////////////////////
282
283    public void set_default(string pie_id, int position) {
284        this.slice_position = position;
285        this.add_as_new_slice = true;
286        this.current_custom_icon = "";
287        this.select_type("app");
288        this.current_id = pie_id;
289        this.key_select.set_trigger(new Trigger());
290        this.pie_select.select_first();
291        this.name_entry.text = _("Rename me!");
292        this.command_entry.text = "";
293        this.uri_entry.text = "";
294    }
295
296    /////////////////////////////////////////////////////////////////////
297    /// Selects a specific action type.
298    /////////////////////////////////////////////////////////////////////
299
300    private void select_type(string type) {
301        this.current_type = type;
302        this.slice_type_list.select(type);
303    }
304
305    /////////////////////////////////////////////////////////////////////
306    /// Called, when the user presses the ok button.
307    /////////////////////////////////////////////////////////////////////
308
309    private void on_ok_button_clicked() {
310        this.window.hide();
311
312        ActionGroup group = null;
313
314        switch (this.current_type) {
315            case "bookmarks":   group = new BookmarkGroup(this.current_id);      break;
316            case "clipboard":   group = new ClipboardGroup(this.current_id);     break;
317            case "devices":     group = new DevicesGroup(this.current_id);       break;
318            case "menu":        group = new MenuGroup(this.current_id);          break;
319            case "session":     group = new SessionGroup(this.current_id);       break;
320            case "window_list": group = new WindowListGroup(this.current_id);    break;
321
322            case "app":
323                group = new ActionGroup(this.current_id);
324                group.add_action(new AppAction(this.name_entry.text, this.current_icon,
325                                               this.command_entry.text,
326                                               this.quickaction_checkbutton.active));
327                break;
328            case "key":
329                group = new ActionGroup(this.current_id);
330                group.add_action(new KeyAction(this.name_entry.text, this.current_icon,
331                                               this.current_hotkey,
332                                               this.quickaction_checkbutton.active));
333                break;
334            case "pie":
335                group = new ActionGroup(this.current_id);
336                group.add_action(new PieAction(this.current_pie_to_open,
337                                               this.quickaction_checkbutton.active));
338                break;
339            case "uri":
340                group = new ActionGroup(this.current_id);
341                group.add_action(new UriAction(this.name_entry.text, this.current_icon,
342                                               this.uri_entry.text,
343                                               this.quickaction_checkbutton.active));
344                break;
345        }
346
347        this.on_select(group, this.add_as_new_slice, this.slice_position);
348    }
349
350    /////////////////////////////////////////////////////////////////////
351    /// Called when the user presses the cancel button.
352    /////////////////////////////////////////////////////////////////////
353
354    private void on_cancel_button_clicked() {
355        this.window.hide();
356    }
357
358    /////////////////////////////////////////////////////////////////////
359    /// Called when the user presses the icon select button.
360    /////////////////////////////////////////////////////////////////////
361
362    private void on_icon_button_clicked(Gtk.Button button) {
363        if (this.icon_window == null) {
364            this.icon_window = new IconSelectWindow(this.window);
365            this.icon_window.on_ok.connect((icon) => {
366                this.current_custom_icon = icon;
367                this.set_icon(icon);
368            });
369        }
370
371        this.icon_window.show();
372        this.icon_window.set_icon(this.current_icon);
373    }
374
375    /////////////////////////////////////////////////////////////////////
376    /// Helper method which sets the icon of the icon select button.
377    /// It assures that both can be displayed: A customly chosen image
378    /// from or an icon from the current theme.
379    /////////////////////////////////////////////////////////////////////
380
381    private void set_icon(string icon) {
382        if (icon.contains("/"))
383            try {
384                this.icon.pixbuf = new Gdk.Pixbuf.from_file_at_scale(icon, this.icon.get_pixel_size(),
385                                                                     this.icon.get_pixel_size(), true);
386            } catch (GLib.Error error) {
387                warning(error.message);
388            }
389        else
390            this.icon.icon_name = icon;
391
392        this.current_icon = icon;
393    }
394}
395
396}
397