1 /*
2  * Nautilus-Actions
3  * A Nautilus extension which offers configurable context menu actions.
4  *
5  * Copyright (C) 2005 The GNOME Foundation
6  * Copyright (C) 2006-2008 Frederic Ruaudel and others (see AUTHORS)
7  * Copyright (C) 2009-2014 Pierre Wieser and others (see AUTHORS)
8  *
9  * Nautilus-Actions is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of
12  * the License, or (at your option) any later version.
13  *
14  * Nautilus-Actions is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Nautilus-Actions; see the file COPYING. If not, see
21  * <http://www.gnu.org/licenses/>.
22  *
23  * Authors:
24  *   Frederic Ruaudel <grumz@grumz.net>
25  *   Rodrigo Moya <rodrigo@gnome-db.org>
26  *   Pierre Wieser <pwieser@trychlos.org>
27  *   ... and many others (see AUTHORS)
28  */
29 
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33 
34 #include <glib/gi18n.h>
35 #include <stdlib.h>
36 
37 #include <api/na-object-api.h>
38 #include <api/na-timeout.h>
39 
40 #include <core/na-iprefs.h>
41 #include <core/na-pivot.h>
42 
43 #include "base-isession.h"
44 #include "nact-iaction-tab.h"
45 #include "nact-icommand-tab.h"
46 #include "nact-ibasenames-tab.h"
47 #include "nact-imimetypes-tab.h"
48 #include "nact-ifolders-tab.h"
49 #include "nact-ischemes-tab.h"
50 #include "nact-icapabilities-tab.h"
51 #include "nact-ienvironment-tab.h"
52 #include "nact-iexecution-tab.h"
53 #include "nact-iproperties-tab.h"
54 #include "nact-main-tab.h"
55 #include "nact-main-statusbar.h"
56 #include "nact-main-window.h"
57 #include "nact-marshal.h"
58 #include "nact-menubar.h"
59 #include "nact-tree-view.h"
60 #include "nact-confirm-logout.h"
61 #include "nact-sort-buttons.h"
62 
63 /* private class data
64  */
65 struct _NactMainWindowClassPrivate {
66 	void *empty;						/* so that gcc -pedantic is happy */
67 };
68 
69 /* private instance data
70  */
71 struct _NactMainWindowPrivate {
72 	gboolean         dispose_has_run;
73 
74 	NAUpdater       *updater;
75 
76 	/**
77 	 * Current action or menu.
78 	 *
79 	 * This is the action or menu which is displayed in tabs Action/Menu
80 	 * and Properties ; it may be different of the exact row being currently
81 	 * selected, e.g. when a sub-profile is edited.
82 	 *
83 	 * Can be null, and this implies that @current_profile is also null,
84 	 * e.g. when the list is empty or in the case of a multiple selection.
85 	 *
86 	 * 'editable' property is set on selection change;
87 	 * This is the actual current writability status of the item at this time.
88 	 */
89 	NAObjectItem    *current_item;
90 	gboolean         editable;
91 	guint            reason;
92 
93 	/**
94 	 * Current profile.
95 	 *
96 	 * This is the profile which is displayed in tab Command;
97 	 * it may be different of the exact row being currently selected,
98 	 * e.g. when an action with only one profile is selected.
99 	 *
100 	 * Can be null if @current_item is a menu, or an action with more
101 	 * than one profile is selected, or the list is empty, or in the
102 	 * case of a multiple selection.
103 	 *
104 	 * In other words, it is not null if:
105 	 * a) a profile is selected,
106 	 * b) an action is selected and it has exactly one profile.
107 	 */
108 	NAObjectProfile *current_profile;
109 
110 	/**
111 	 * Current context.
112 	 *
113 	 * This is the #NAIContext data which corresponds to @current_profile
114 	 * or @current_item, depending of which one is actually selected.
115 	 */
116 	NAIContext      *current_context;
117 
118 	/**
119 	 * Some convenience objects and data.
120 	 */
121 	NactTreeView    *items_view;
122 	gboolean         is_tree_modified;
123 	NactClipboard   *clipboard;
124 	NactMenubar     *menubar;
125 
126 	gulong           pivot_handler_id;
127 	NATimeout        pivot_timeout;
128 };
129 
130 /* properties set against the main window
131  * these are set on selection changes
132  */
133 enum {
134 	MAIN_PROP_0 = 0,
135 
136 	MAIN_PROP_ITEM_ID,
137 	MAIN_PROP_PROFILE_ID,
138 	MAIN_PROP_CONTEXT_ID,
139 	MAIN_PROP_EDITABLE_ID,
140 	MAIN_PROP_REASON_ID,
141 
142 	MAIN_PROP_N_PROPERTIES
143 };
144 
145 /* signals
146  */
147 enum {
148 	MAIN_ITEM_UPDATED,
149 	TAB_ITEM_UPDATED,
150 	SELECTION_CHANGED,
151 	CONTEXT_MENU,
152 	LAST_SIGNAL
153 };
154 
155 static const gchar     *st_xmlui_filename         = PKGUIDIR "/nautilus-actions-config-tool.ui";
156 static const gchar     *st_toplevel_name          = "MainWindow";
157 static const gchar     *st_wsp_name               = NA_IPREFS_MAIN_WINDOW_WSP;
158 
159 static gint             st_burst_timeout          = 2500;		/* burst timeout in msec */
160 static BaseWindowClass *st_parent_class           = NULL;
161 static gint             st_signals[ LAST_SIGNAL ] = { 0 };
162 
163 static GType      register_type( void );
164 static void       class_init( NactMainWindowClass *klass );
165 static void       iaction_tab_iface_init( NactIActionTabInterface *iface, void *user_data );
166 static void       icommand_tab_iface_init( NactICommandTabInterface *iface, void *user_data );
167 static void       ibasenames_tab_iface_init( NactIBasenamesTabInterface *iface, void *user_data );
168 static void       imimetypes_tab_iface_init( NactIMimetypesTabInterface *iface, void *user_data );
169 static void       ifolders_tab_iface_init( NactIFoldersTabInterface *iface, void *user_data );
170 static void       ischemes_tab_iface_init( NactISchemesTabInterface *iface, void *user_data );
171 static void       icapabilities_tab_iface_init( NactICapabilitiesTabInterface *iface, void *user_data );
172 static void       ienvironment_tab_iface_init( NactIEnvironmentTabInterface *iface, void *user_data );
173 static void       iexecution_tab_iface_init( NactIExecutionTabInterface *iface, void *user_data );
174 static void       iproperties_tab_iface_init( NactIPropertiesTabInterface *iface, void *user_data );
175 static void       instance_init( GTypeInstance *instance, gpointer klass );
176 static void       instance_get_property( GObject *object, guint property_id, GValue *value, GParamSpec *spec );
177 static void       instance_set_property( GObject *object, guint property_id, const GValue *value, GParamSpec *spec );
178 static void       instance_constructed( GObject *window );
179 static void       instance_dispose( GObject *window );
180 static void       instance_finalize( GObject *window );
181 
182 static void       on_base_initialize_gtk( NactMainWindow *window, GtkWindow *toplevel, gpointer user_data );
183 static void       on_base_initialize_window( NactMainWindow *window, gpointer user_data );
184 static void       on_base_show_widgets( NactMainWindow *window, gpointer user_data );
185 
186 static void       on_block_items_changed_timeout( NactMainWindow *window );
187 static void       on_tree_view_modified_status_changed( NactMainWindow *window, gboolean is_modified, gpointer user_data );
188 static void       on_tree_view_selection_changed( NactMainWindow *window, GList *selected_items, gpointer user_data );
189 static void       on_selection_changed_cleanup_handler( BaseWindow *window, GList *selected_items );
190 static void       on_tab_updatable_item_updated( NactMainWindow *window, NAIContext *context, guint data, gpointer user_data );
191 static void       raz_selection_properties( NactMainWindow *window );
192 static void       setup_current_selection( NactMainWindow *window, NAObjectId *selected_row );
193 static void       setup_dialog_title( NactMainWindow *window );
194 static void       setup_writability_status( NactMainWindow *window );
195 
196 /* items have changed */
197 static void       on_pivot_items_changed( NAUpdater *updater, NactMainWindow *window );
198 static gboolean   confirm_for_giveup_from_pivot( const NactMainWindow *window );
199 static gboolean   confirm_for_giveup_from_menu( const NactMainWindow *window );
200 static void       load_or_reload_items( NactMainWindow *window );
201 
202 /* application termination */
203 static gboolean   on_base_quit_requested( NactApplication *application, NactMainWindow *window );
204 static gboolean   on_delete_event( GtkWidget *toplevel, GdkEvent *event, NactMainWindow *window );
205 static gboolean   warn_modified( NactMainWindow *window );
206 
207 GType
nact_main_window_get_type(void)208 nact_main_window_get_type( void )
209 {
210 	static GType window_type = 0;
211 
212 	if( !window_type ){
213 		window_type = register_type();
214 	}
215 
216 	return( window_type );
217 }
218 
219 static GType
register_type(void)220 register_type( void )
221 {
222 	static const gchar *thisfn = "nact_main_window_register_type";
223 	GType type;
224 
225 	static GTypeInfo info = {
226 		sizeof( NactMainWindowClass ),
227 		( GBaseInitFunc ) NULL,
228 		( GBaseFinalizeFunc ) NULL,
229 		( GClassInitFunc ) class_init,
230 		NULL,
231 		NULL,
232 		sizeof( NactMainWindow ),
233 		0,
234 		( GInstanceInitFunc ) instance_init
235 	};
236 
237 	static const GInterfaceInfo iaction_tab_iface_info = {
238 		( GInterfaceInitFunc ) iaction_tab_iface_init,
239 		NULL,
240 		NULL
241 	};
242 
243 	static const GInterfaceInfo icommand_tab_iface_info = {
244 		( GInterfaceInitFunc ) icommand_tab_iface_init,
245 		NULL,
246 		NULL
247 	};
248 
249 	static const GInterfaceInfo ibasenames_tab_iface_info = {
250 		( GInterfaceInitFunc ) ibasenames_tab_iface_init,
251 		NULL,
252 		NULL
253 	};
254 
255 	static const GInterfaceInfo imimetypes_tab_iface_info = {
256 		( GInterfaceInitFunc ) imimetypes_tab_iface_init,
257 		NULL,
258 		NULL
259 	};
260 
261 	static const GInterfaceInfo ifolders_tab_iface_info = {
262 		( GInterfaceInitFunc ) ifolders_tab_iface_init,
263 		NULL,
264 		NULL
265 	};
266 
267 	static const GInterfaceInfo ischemes_tab_iface_info = {
268 		( GInterfaceInitFunc ) ischemes_tab_iface_init,
269 		NULL,
270 		NULL
271 	};
272 
273 	static const GInterfaceInfo icapabilities_tab_iface_info = {
274 		( GInterfaceInitFunc ) icapabilities_tab_iface_init,
275 		NULL,
276 		NULL
277 	};
278 
279 	static const GInterfaceInfo ienvironment_tab_iface_info = {
280 		( GInterfaceInitFunc ) ienvironment_tab_iface_init,
281 		NULL,
282 		NULL
283 	};
284 
285 	static const GInterfaceInfo iexecution_tab_iface_info = {
286 		( GInterfaceInitFunc ) iexecution_tab_iface_init,
287 		NULL,
288 		NULL
289 	};
290 
291 	static const GInterfaceInfo iproperties_tab_iface_info = {
292 		( GInterfaceInitFunc ) iproperties_tab_iface_init,
293 		NULL,
294 		NULL
295 	};
296 
297 	g_debug( "%s", thisfn );
298 
299 	type = g_type_register_static( BASE_TYPE_WINDOW, "NactMainWindow", &info, 0 );
300 
301 	g_type_add_interface_static( type, NACT_TYPE_IACTION_TAB, &iaction_tab_iface_info );
302 
303 	g_type_add_interface_static( type, NACT_TYPE_ICOMMAND_TAB, &icommand_tab_iface_info );
304 
305 	g_type_add_interface_static( type, NACT_TYPE_IBASENAMES_TAB, &ibasenames_tab_iface_info );
306 
307 	g_type_add_interface_static( type, NACT_TYPE_IMIMETYPES_TAB, &imimetypes_tab_iface_info );
308 
309 	g_type_add_interface_static( type, NACT_TYPE_IFOLDERS_TAB, &ifolders_tab_iface_info );
310 
311 	g_type_add_interface_static( type, NACT_TYPE_ISCHEMES_TAB, &ischemes_tab_iface_info );
312 
313 	g_type_add_interface_static( type, NACT_TYPE_ICAPABILITIES_TAB, &icapabilities_tab_iface_info );
314 
315 	g_type_add_interface_static( type, NACT_TYPE_IENVIRONMENT_TAB, &ienvironment_tab_iface_info );
316 
317 	g_type_add_interface_static( type, NACT_TYPE_IEXECUTION_TAB, &iexecution_tab_iface_info );
318 
319 	g_type_add_interface_static( type, NACT_TYPE_IPROPERTIES_TAB, &iproperties_tab_iface_info );
320 
321 	return( type );
322 }
323 
324 static void
class_init(NactMainWindowClass * klass)325 class_init( NactMainWindowClass *klass )
326 {
327 	static const gchar *thisfn = "nact_main_window_class_init";
328 	GObjectClass *object_class;
329 
330 	g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
331 
332 	st_parent_class = g_type_class_peek_parent( klass );
333 
334 	object_class = G_OBJECT_CLASS( klass );
335 	object_class->set_property = instance_set_property;
336 	object_class->get_property = instance_get_property;
337 	object_class->constructed = instance_constructed;
338 	object_class->dispose = instance_dispose;
339 	object_class->finalize = instance_finalize;
340 
341 	g_object_class_install_property( object_class, MAIN_PROP_ITEM_ID,
342 			g_param_spec_pointer(
343 					MAIN_PROP_ITEM,
344 					_( "Current NAObjectItem" ),
345 					_( "A pointer to the currently edited NAObjectItem, an action or a menu" ),
346 					G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE ));
347 
348 	g_object_class_install_property( object_class, MAIN_PROP_PROFILE_ID,
349 			g_param_spec_pointer(
350 					MAIN_PROP_PROFILE,
351 					_( "Current NAObjectProfile" ),
352 					_( "A pointer to the currently edited NAObjectProfile" ),
353 					G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE ));
354 
355 	g_object_class_install_property( object_class, MAIN_PROP_CONTEXT_ID,
356 			g_param_spec_pointer(
357 					MAIN_PROP_CONTEXT,
358 					_( "Current NAIContext" ),
359 					_( "A pointer to the currently edited NAIContext" ),
360 					G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE ));
361 
362 	g_object_class_install_property( object_class, MAIN_PROP_EDITABLE_ID,
363 			g_param_spec_boolean(
364 					MAIN_PROP_EDITABLE,
365 					_( "Editable item ?" ),
366 					_( "Whether the item will be able to be updated against its I/O provider" ),
367 					FALSE,
368 					G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE ));
369 
370 	g_object_class_install_property( object_class, MAIN_PROP_REASON_ID,
371 			g_param_spec_int(
372 					MAIN_PROP_REASON,
373 					_( "No edition reason" ),
374 					_( "Why is this item not editable" ),
375 					0, 255, 0,
376 					G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE ));
377 
378 	klass->private = g_new0( NactMainWindowClassPrivate, 1 );
379 
380 	/**
381 	 * NactMainWindow::main-item-updated:
382 	 *
383 	 * This signal is emitted on the BaseWindow when the item has been modified
384 	 * elsewhere that in a tab. The tabs should so update accordingly their
385 	 * widgets.
386 	 *
387 	 * Args:
388 	 * - an OR-ed list of the modified data, or 0 if not relevant.
389 	 */
390 	st_signals[ MAIN_ITEM_UPDATED ] = g_signal_new(
391 			MAIN_SIGNAL_ITEM_UPDATED,
392 			G_TYPE_OBJECT,
393 			G_SIGNAL_RUN_LAST,
394 			0,					/* no default handler */
395 			NULL,
396 			NULL,
397 			nact_cclosure_marshal_VOID__POINTER_UINT,
398 			G_TYPE_NONE,
399 			2,
400 			G_TYPE_POINTER,
401 			G_TYPE_UINT );
402 
403 	/**
404 	 * nact-tab-updatable-item-updated:
405 	 *
406 	 * This signal is emitted by the notebook tabs, when any property
407 	 * of an item has been modified.
408 	 *
409 	 * Args:
410 	 * - an OR-ed list of the modified data, or 0 if not relevant.
411 	 *
412 	 * This main window is rather the only consumer of this message,
413 	 * does its tricks (title, etc.), and then reforward an item-updated
414 	 * message to IActionsList.
415 	 */
416 	st_signals[ TAB_ITEM_UPDATED ] = g_signal_new(
417 			TAB_UPDATABLE_SIGNAL_ITEM_UPDATED,
418 			G_TYPE_OBJECT,
419 			G_SIGNAL_RUN_LAST,
420 			0,					/* no default handler */
421 			NULL,
422 			NULL,
423 			nact_cclosure_marshal_VOID__POINTER_UINT,
424 			G_TYPE_NONE,
425 			2,
426 			G_TYPE_POINTER,
427 			G_TYPE_UINT );
428 
429 	/**
430 	 * NactMainWindow::main-selection-changed:
431 	 *
432 	 * This signal is emitted on the window parent each time the selection
433 	 * has changed in the treeview, after having set the current item/profile/
434 	 * context properties.
435 	 *
436 	 * This way, we are sure that notebook edition tabs which required to
437 	 * have a current item/profile/context will have it, whenever they have
438 	 * connected to the 'selection-changed' signal.
439 	 *
440 	 * Signal args:
441 	 * - a #GList of currently selected #NAObjectItems.
442 	 *
443 	 * Handler prototype:
444 	 * void ( *handler )( BaseWindow *window, GList *selected, gpointer user_data );
445 	 */
446 	st_signals[ SELECTION_CHANGED ] = g_signal_new_class_handler(
447 			MAIN_SIGNAL_SELECTION_CHANGED,
448 			G_TYPE_OBJECT,
449 			G_SIGNAL_RUN_CLEANUP,
450 			G_CALLBACK( on_selection_changed_cleanup_handler ),
451 			NULL,
452 			NULL,
453 			g_cclosure_marshal_VOID__POINTER,
454 			G_TYPE_NONE,
455 			1,
456 			G_TYPE_POINTER );
457 
458 	/**
459 	 * NactMainWindow::main-signal-open-popup
460 	 *
461 	 * This signal is emitted on the BaseWindow parent when the user right
462 	 * clicks somewhere (on an active zone).
463 	 *
464 	 * Signal args:
465 	 * - the GdkEvent
466 	 * - the popup name to be opened.
467 	 *
468 	 * Handler prototype:
469 	 * void ( *handler )( BaseWindow *window, GdkEvent *event, const gchar *popup_name, gpointer user_data );
470 	 */
471 	st_signals[ CONTEXT_MENU ] = g_signal_new(
472 			MAIN_SIGNAL_CONTEXT_MENU,
473 			G_TYPE_OBJECT,
474 			G_SIGNAL_RUN_LAST,
475 			0,
476 			NULL,
477 			NULL,
478 			nact_cclosure_marshal_VOID__POINTER_STRING,
479 			G_TYPE_NONE,
480 			2,
481 			G_TYPE_POINTER,
482 			G_TYPE_STRING);
483 }
484 
485 static void
iaction_tab_iface_init(NactIActionTabInterface * iface,void * user_data)486 iaction_tab_iface_init( NactIActionTabInterface *iface, void *user_data )
487 {
488 	static const gchar *thisfn = "nact_main_window_iaction_tab_iface_init";
489 
490 	g_debug( "%s: iface=%p, user_data=%p", thisfn, ( void * ) iface, ( void * ) user_data );
491 }
492 
493 static void
icommand_tab_iface_init(NactICommandTabInterface * iface,void * user_data)494 icommand_tab_iface_init( NactICommandTabInterface *iface, void *user_data )
495 {
496 	static const gchar *thisfn = "nact_main_window_icommand_tab_iface_init";
497 
498 	g_debug( "%s: iface=%p, user_data=%p", thisfn, ( void * ) iface, ( void * ) user_data );
499 }
500 
501 static void
ibasenames_tab_iface_init(NactIBasenamesTabInterface * iface,void * user_data)502 ibasenames_tab_iface_init( NactIBasenamesTabInterface *iface, void *user_data )
503 {
504 	static const gchar *thisfn = "nact_main_window_ibasenames_tab_iface_init";
505 
506 	g_debug( "%s: iface=%p, user_data=%p", thisfn, ( void * ) iface, ( void * ) user_data );
507 }
508 
509 static void
imimetypes_tab_iface_init(NactIMimetypesTabInterface * iface,void * user_data)510 imimetypes_tab_iface_init( NactIMimetypesTabInterface *iface, void *user_data )
511 {
512 	static const gchar *thisfn = "nact_main_window_imimetypes_tab_iface_init";
513 
514 	g_debug( "%s: iface=%p, user_data=%p", thisfn, ( void * ) iface, ( void * ) user_data );
515 }
516 
517 static void
ifolders_tab_iface_init(NactIFoldersTabInterface * iface,void * user_data)518 ifolders_tab_iface_init( NactIFoldersTabInterface *iface, void *user_data )
519 {
520 	static const gchar *thisfn = "nact_main_window_ifolders_tab_iface_init";
521 
522 	g_debug( "%s: iface=%p, user_data=%p", thisfn, ( void * ) iface, ( void * ) user_data );
523 }
524 
525 static void
ischemes_tab_iface_init(NactISchemesTabInterface * iface,void * user_data)526 ischemes_tab_iface_init( NactISchemesTabInterface *iface, void *user_data )
527 {
528 	static const gchar *thisfn = "nact_main_window_ischemes_tab_iface_init";
529 
530 	g_debug( "%s: iface=%p, user_data=%p", thisfn, ( void * ) iface, ( void * ) user_data );
531 }
532 
533 static void
icapabilities_tab_iface_init(NactICapabilitiesTabInterface * iface,void * user_data)534 icapabilities_tab_iface_init( NactICapabilitiesTabInterface *iface, void *user_data )
535 {
536 	static const gchar *thisfn = "nact_main_window_icapabilities_tab_iface_init";
537 
538 	g_debug( "%s: iface=%p, user_data=%p", thisfn, ( void * ) iface, ( void * ) user_data );
539 }
540 
541 static void
ienvironment_tab_iface_init(NactIEnvironmentTabInterface * iface,void * user_data)542 ienvironment_tab_iface_init( NactIEnvironmentTabInterface *iface, void *user_data )
543 {
544 	static const gchar *thisfn = "nact_main_window_ienvironment_tab_iface_init";
545 
546 	g_debug( "%s: iface=%p, user_data=%p", thisfn, ( void * ) iface, ( void * ) user_data );
547 }
548 
549 static void
iexecution_tab_iface_init(NactIExecutionTabInterface * iface,void * user_data)550 iexecution_tab_iface_init( NactIExecutionTabInterface *iface, void *user_data )
551 {
552 	static const gchar *thisfn = "nact_main_window_iexecution_tab_iface_init";
553 
554 	g_debug( "%s: iface=%p, user_data=%p", thisfn, ( void * ) iface, ( void * ) user_data );
555 }
556 
557 static void
iproperties_tab_iface_init(NactIPropertiesTabInterface * iface,void * user_data)558 iproperties_tab_iface_init( NactIPropertiesTabInterface *iface, void *user_data )
559 {
560 	static const gchar *thisfn = "nact_main_window_iproperties_tab_iface_init";
561 
562 	g_debug( "%s: iface=%p, user_data=%p", thisfn, ( void * ) iface, ( void * ) user_data );
563 }
564 
565 static void
instance_init(GTypeInstance * instance,gpointer klass)566 instance_init( GTypeInstance *instance, gpointer klass )
567 {
568 	static const gchar *thisfn = "nact_main_window_instance_init";
569 	NactMainWindow *self;
570 	NactMainWindowPrivate *priv;
571 
572 	g_return_if_fail( NACT_IS_MAIN_WINDOW( instance ));
573 
574 	g_debug( "%s: instance=%p (%s), klass=%p",
575 			thisfn, ( void * ) instance, G_OBJECT_TYPE_NAME( instance ), ( void * ) klass );
576 
577 	self = NACT_MAIN_WINDOW( instance );
578 	self->private = g_new0( NactMainWindowPrivate, 1 );
579 	priv = self->private;
580 	priv->dispose_has_run = FALSE;
581 
582 	/* initialize timeout parameters when blocking 'pivot-items-changed' handler
583 	 */
584 	priv->pivot_timeout.timeout = st_burst_timeout;
585 	priv->pivot_timeout.handler = ( NATimeoutFunc ) on_block_items_changed_timeout;
586 	priv->pivot_timeout.user_data = self;
587 	priv->pivot_timeout.source_id = 0;
588 }
589 
590 static void
instance_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * spec)591 instance_get_property( GObject *object, guint property_id, GValue *value, GParamSpec *spec )
592 {
593 	NactMainWindow *self;
594 
595 	g_return_if_fail( NACT_IS_MAIN_WINDOW( object ));
596 	self = NACT_MAIN_WINDOW( object );
597 
598 	if( !self->private->dispose_has_run ){
599 
600 		switch( property_id ){
601 			case MAIN_PROP_ITEM_ID:
602 				g_value_set_pointer( value, self->private->current_item );
603 				break;
604 
605 			case MAIN_PROP_PROFILE_ID:
606 				g_value_set_pointer( value, self->private->current_profile );
607 				break;
608 
609 			case MAIN_PROP_CONTEXT_ID:
610 				g_value_set_pointer( value, self->private->current_context );
611 				break;
612 
613 			case MAIN_PROP_EDITABLE_ID:
614 				g_value_set_boolean( value, self->private->editable );
615 				break;
616 
617 			case MAIN_PROP_REASON_ID:
618 				g_value_set_int( value, self->private->reason );
619 				break;
620 
621 			default:
622 				G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, spec );
623 				break;
624 		}
625 	}
626 }
627 
628 static void
instance_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * spec)629 instance_set_property( GObject *object, guint property_id, const GValue *value, GParamSpec *spec )
630 {
631 	NactMainWindow *self;
632 
633 	g_return_if_fail( NACT_IS_MAIN_WINDOW( object ));
634 	self = NACT_MAIN_WINDOW( object );
635 
636 	if( !self->private->dispose_has_run ){
637 
638 		switch( property_id ){
639 			case MAIN_PROP_ITEM_ID:
640 				self->private->current_item = g_value_get_pointer( value );
641 				break;
642 
643 			case MAIN_PROP_PROFILE_ID:
644 				self->private->current_profile = g_value_get_pointer( value );
645 				break;
646 
647 			case MAIN_PROP_CONTEXT_ID:
648 				self->private->current_context = g_value_get_pointer( value );
649 				break;
650 
651 			case MAIN_PROP_EDITABLE_ID:
652 				self->private->editable = g_value_get_boolean( value );
653 				break;
654 
655 			case MAIN_PROP_REASON_ID:
656 				self->private->reason = g_value_get_int( value );
657 				break;
658 
659 			default:
660 				G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, spec );
661 				break;
662 		}
663 	}
664 }
665 
666 static void
instance_constructed(GObject * window)667 instance_constructed( GObject *window )
668 {
669 	static const gchar *thisfn = "nact_main_window_instance_constructed";
670 	NactMainWindowPrivate *priv;
671 	NactApplication *application;
672 
673 	g_return_if_fail( NACT_IS_MAIN_WINDOW( window ));
674 
675 	priv = NACT_MAIN_WINDOW( window )->private;
676 
677 	if( !priv->dispose_has_run ){
678 
679 		/* chain up to the parent class */
680 		if( G_OBJECT_CLASS( st_parent_class )->constructed ){
681 			G_OBJECT_CLASS( st_parent_class )->constructed( window );
682 		}
683 
684 		g_debug( "%s: window=%p (%s)", thisfn, ( void * ) window, G_OBJECT_TYPE_NAME( window ));
685 
686 		/* connect to BaseWindow signals
687 		 * so that convenience objects instanciated later will have this same signal
688 		 * triggered after the one of NactMainWindow
689 		 */
690 		base_window_signal_connect(
691 				BASE_WINDOW( window ),
692 				G_OBJECT( window ),
693 				BASE_SIGNAL_INITIALIZE_GTK,
694 				G_CALLBACK( on_base_initialize_gtk ));
695 
696 		base_window_signal_connect(
697 				BASE_WINDOW( window ),
698 				G_OBJECT( window ),
699 				BASE_SIGNAL_INITIALIZE_WINDOW,
700 				G_CALLBACK( on_base_initialize_window ));
701 
702 		base_window_signal_connect(
703 				BASE_WINDOW( window ),
704 				G_OBJECT( window ),
705 				BASE_SIGNAL_SHOW_WIDGETS,
706 				G_CALLBACK( on_base_show_widgets ));
707 
708 		/* monitor the items stored on the disk for modifications
709 		 * outside of this application
710 		 */
711 		application = NACT_APPLICATION( base_window_get_application( BASE_WINDOW( window )));
712 		priv->updater = nact_application_get_updater( application );
713 
714 		priv->pivot_handler_id = base_window_signal_connect(
715 				BASE_WINDOW( window ),
716 				G_OBJECT( priv->updater ),
717 				PIVOT_SIGNAL_ITEMS_CHANGED,
718 				G_CALLBACK( on_pivot_items_changed ));
719 
720 		/* monitor the updates which originates from each property tab
721 		 */
722 		base_window_signal_connect(
723 				BASE_WINDOW( window ),
724 				G_OBJECT( window ),
725 				TAB_UPDATABLE_SIGNAL_ITEM_UPDATED,
726 				G_CALLBACK( on_tab_updatable_item_updated ));
727 
728 		/* create the menubar and other convenience objects
729 		 */
730 		priv->menubar = nact_menubar_new( BASE_WINDOW( window ));
731 		priv->clipboard = nact_clipboard_new( BASE_WINDOW( window ));
732 
733 		/* initialize the notebook interfaces
734 		 */
735 		nact_iaction_tab_init( NACT_IACTION_TAB( window ));
736 		nact_icommand_tab_init( NACT_ICOMMAND_TAB( window ));
737 		nact_ibasenames_tab_init( NACT_IBASENAMES_TAB( window ));
738 		nact_imimetypes_tab_init( NACT_IMIMETYPES_TAB( window ));
739 		nact_ifolders_tab_init( NACT_IFOLDERS_TAB( window ));
740 		nact_ischemes_tab_init( NACT_ISCHEMES_TAB( window ));
741 		nact_icapabilities_tab_init( NACT_ICAPABILITIES_TAB( window ));
742 		nact_ienvironment_tab_init( NACT_IENVIRONMENT_TAB( window ));
743 		nact_iexecution_tab_init( NACT_IEXECUTION_TAB( window ));
744 		nact_iproperties_tab_init( NACT_IPROPERTIES_TAB( window ));
745 	}
746 }
747 
748 static void
instance_dispose(GObject * window)749 instance_dispose( GObject *window )
750 {
751 	static const gchar *thisfn = "nact_main_window_instance_dispose";
752 	NactMainWindow *self;
753 	GtkWidget *pane;
754 	gint pos;
755 	GtkNotebook *notebook;
756 
757 	g_return_if_fail( NACT_IS_MAIN_WINDOW( window ));
758 
759 	self = NACT_MAIN_WINDOW( window );
760 
761 	if( !self->private->dispose_has_run ){
762 		g_debug( "%s: window=%p (%s)", thisfn, ( void * ) window, G_OBJECT_TYPE_NAME( window ));
763 
764 		self->private->dispose_has_run = TRUE;
765 
766 		gtk_main_quit();
767 
768 		g_object_unref( self->private->clipboard );
769 		g_object_unref( self->private->menubar );
770 
771 		pane = base_window_get_widget( BASE_WINDOW( window ), "MainPaned" );
772 		pos = gtk_paned_get_position( GTK_PANED( pane ));
773 		na_settings_set_uint( NA_IPREFS_MAIN_PANED, pos );
774 
775 		notebook = GTK_NOTEBOOK( base_window_get_widget( BASE_WINDOW( window ), "MainNotebook" ));
776 		pos = gtk_notebook_get_tab_pos( notebook );
777 		na_iprefs_set_tabs_pos( pos );
778 
779 		/* unref items view at last as gtk_tree_model_store_clear() will
780 		 * finalize all objects, thus invaliditing all our references
781 		 */
782 		g_object_unref( self->private->items_view );
783 
784 		/* chain up to the parent class */
785 		if( G_OBJECT_CLASS( st_parent_class )->dispose ){
786 			G_OBJECT_CLASS( st_parent_class )->dispose( window );
787 		}
788 	}
789 }
790 
791 static void
instance_finalize(GObject * window)792 instance_finalize( GObject *window )
793 {
794 	static const gchar *thisfn = "nact_main_window_instance_finalize";
795 	NactMainWindow *self;
796 
797 	g_return_if_fail( NACT_IS_MAIN_WINDOW( window ));
798 
799 	g_debug( "%s: window=%p (%s)", thisfn, ( void * ) window, G_OBJECT_TYPE_NAME( window ));
800 
801 	self = NACT_MAIN_WINDOW( window );
802 
803 	g_free( self->private );
804 
805 	/* chain call to parent class */
806 	if( G_OBJECT_CLASS( st_parent_class )->finalize ){
807 		G_OBJECT_CLASS( st_parent_class )->finalize( window );
808 	}
809 }
810 
811 /**
812  * Returns a newly allocated NactMainWindow object.
813  */
814 NactMainWindow *
nact_main_window_new(const NactApplication * application)815 nact_main_window_new( const NactApplication *application )
816 {
817 	NactMainWindow *window;
818 
819 	g_return_val_if_fail( NACT_IS_APPLICATION( application ), NULL );
820 
821 	window = g_object_new( NACT_TYPE_MAIN_WINDOW,
822 			BASE_PROP_APPLICATION,        application,
823 			BASE_PROP_XMLUI_FILENAME,     st_xmlui_filename,
824 			BASE_PROP_TOPLEVEL_NAME,      st_toplevel_name,
825 			BASE_PROP_WSP_NAME,           st_wsp_name,
826 			BASE_PROP_DESTROY_ON_DISPOSE, TRUE,
827 			NULL );
828 
829 	if( !base_window_init( BASE_WINDOW( window ))){
830 		g_object_unref( window );
831 		window = NULL;
832 	}
833 
834 	return( window );
835 }
836 
837 /*
838  * note that for this NactMainWindow, on_base_initialize_gtk_toplevel() and
839  * on_base_initialize_base_window() are roughly equivalent, as there is only
840  * one occurrence on this window in the application: closing this window
841  * is the same than quitting the application
842  */
843 static void
on_base_initialize_gtk(NactMainWindow * window,GtkWindow * toplevel,gpointer user_data)844 on_base_initialize_gtk( NactMainWindow *window, GtkWindow *toplevel, gpointer user_data )
845 {
846 	static const gchar *thisfn = "nact_main_window_on_base_initialize_gtk";
847 	GtkWidget *tree_parent;
848 	GtkNotebook *notebook;
849 
850 	g_return_if_fail( NACT_IS_MAIN_WINDOW( window ));
851 
852 	if( !window->private->dispose_has_run ){
853 
854 		g_debug( "%s: window=%p, toplevel=%p, user_data=%p",
855 				thisfn,
856 				( void * ) window,
857 				( void * ) toplevel,
858 				( void * ) user_data );
859 
860 		/* create the tree view which will create itself its own tree model
861 		 */
862 		tree_parent = base_window_get_widget( BASE_WINDOW( window ), "MainVBox" );
863 		g_debug( "%s: tree_parent=%p (%s)", thisfn, ( void * ) tree_parent, G_OBJECT_TYPE_NAME( tree_parent ));
864 		window->private->items_view = nact_tree_view_new(
865 				BASE_WINDOW( window ),
866 				GTK_CONTAINER( tree_parent ),
867 				"ActionsList",
868 				TREE_MODE_EDITION );
869 
870 		nact_main_statusbar_initialize_gtk_toplevel( window );
871 
872 		/* enable popup menu on  the notebook
873 		 */
874 		notebook = GTK_NOTEBOOK( base_window_get_widget( BASE_WINDOW( window ), "MainNotebook" ));
875 		gtk_notebook_popup_enable( notebook );
876 	}
877 }
878 
879 static void
on_base_initialize_window(NactMainWindow * window,gpointer user_data)880 on_base_initialize_window( NactMainWindow *window, gpointer user_data )
881 {
882 	static const gchar *thisfn = "nact_main_window_on_base_initialize_window";
883 	guint pos;
884 	GtkWidget *pane;
885 	NactApplication *application;
886 	GtkNotebook *notebook;
887 
888 	g_return_if_fail( NACT_IS_MAIN_WINDOW( window ));
889 
890 	if( !window->private->dispose_has_run ){
891 
892 		g_debug( "%s: window=%p, user_data=%p",
893 				thisfn,
894 				( void * ) window,
895 				( void * ) user_data );
896 
897 		pos = na_settings_get_uint( NA_IPREFS_MAIN_PANED, NULL, NULL );
898 		if( pos ){
899 			pane = base_window_get_widget( BASE_WINDOW( window ), "MainPaned" );
900 			gtk_paned_set_position( GTK_PANED( pane ), pos );
901 		}
902 
903 		/* terminate the application by clicking the top right [X] button
904 		 */
905 		base_window_signal_connect(
906 				BASE_WINDOW( window ),
907 				G_OBJECT( base_window_get_gtk_toplevel( BASE_WINDOW( window ))),
908 				"delete-event",
909 				G_CALLBACK( on_delete_event ));
910 
911 		/* is willing to quit ?
912 		 * connect to the signal of the BaseISession interface
913 		 */
914 		application = NACT_APPLICATION( base_window_get_application( BASE_WINDOW( window )));
915 		base_window_signal_connect(
916 				BASE_WINDOW( window ),
917 				G_OBJECT( application ),
918 				BASE_SIGNAL_QUIT_REQUESTED,
919 				G_CALLBACK( on_base_quit_requested ));
920 
921 		/* connect to treeview signals
922 		 */
923 		base_window_signal_connect(
924 				BASE_WINDOW( window ),
925 				G_OBJECT( window ),
926 				MAIN_SIGNAL_SELECTION_CHANGED,
927 				G_CALLBACK( on_tree_view_selection_changed ));
928 
929 		base_window_signal_connect(
930 				BASE_WINDOW( window ),
931 				G_OBJECT( window ),
932 				TREE_SIGNAL_MODIFIED_STATUS_CHANGED,
933 				G_CALLBACK( on_tree_view_modified_status_changed ));
934 
935 		/* restore the notebook tabs position
936 		 */
937 		pos = na_iprefs_get_tabs_pos( NULL );
938 		notebook = GTK_NOTEBOOK( base_window_get_widget( BASE_WINDOW( window ), "MainNotebook" ));
939 		gtk_notebook_set_tab_pos( notebook, pos );
940 	}
941 }
942 
943 static void
on_base_show_widgets(NactMainWindow * window,gpointer user_data)944 on_base_show_widgets( NactMainWindow *window, gpointer user_data )
945 {
946 	static const gchar *thisfn = "nact_main_window_on_base_show_widgets";
947 
948 	g_return_if_fail( NACT_IS_MAIN_WINDOW( window ));
949 
950 	if( !window->private->dispose_has_run ){
951 
952 		g_debug( "%s: window=%p, user_data=%p", thisfn, ( void * ) window, ( void * ) user_data );
953 
954 		load_or_reload_items( window );
955 	}
956 }
957 
958 /**
959  * nact_main_window_get_clipboard:
960  * @window: this #NactMainWindow instance.
961  *
962  * Returns: the #NactClipboard convenience object.
963  */
964 NactClipboard *
nact_main_window_get_clipboard(const NactMainWindow * window)965 nact_main_window_get_clipboard( const NactMainWindow *window )
966 {
967 	NactClipboard *clipboard;
968 
969 	g_return_val_if_fail( NACT_IS_MAIN_WINDOW( window ), NULL );
970 
971 	clipboard = NULL;
972 
973 	if( !window->private->dispose_has_run ){
974 
975 		clipboard = window->private->clipboard;
976 	}
977 
978 	return( clipboard );
979 }
980 
981 /**
982  * nact_main_window_get_items_view:
983  * @window: this #NactMainWindow instance.
984  *
985  * Returns: The #NactTreeView convenience object.
986  */
987 NactTreeView *
nact_main_window_get_items_view(const NactMainWindow * window)988 nact_main_window_get_items_view( const NactMainWindow *window )
989 {
990 	NactTreeView *view;
991 
992 	g_return_val_if_fail( NACT_IS_MAIN_WINDOW( window ), NULL );
993 
994 	view = NULL;
995 
996 	if( !window->private->dispose_has_run ){
997 
998 		view = window->private->items_view;
999 	}
1000 
1001 	return( view );
1002 }
1003 
1004 /**
1005  * nact_main_window_reload:
1006  * @window: this #NactMainWindow instance.
1007  *
1008  * Refresh the list of items.
1009  * If there is some non-yet saved modifications, a confirmation is
1010  * required before giving up with them.
1011  */
1012 void
nact_main_window_reload(NactMainWindow * window)1013 nact_main_window_reload( NactMainWindow *window )
1014 {
1015 	gboolean reload_ok;
1016 
1017 	g_return_if_fail( NACT_IS_MAIN_WINDOW( window ));
1018 
1019 	if( !window->private->dispose_has_run ){
1020 
1021 		reload_ok = confirm_for_giveup_from_menu( window );
1022 
1023 		if( reload_ok ){
1024 			load_or_reload_items( window );
1025 		}
1026 	}
1027 }
1028 
1029 /**
1030  * nact_main_window_block_reload:
1031  * @window: this #NactMainWindow instance.
1032  *
1033  * Temporarily blocks the handling of pivot-items-changed signal.
1034  */
1035 void
nact_main_window_block_reload(NactMainWindow * window)1036 nact_main_window_block_reload( NactMainWindow *window )
1037 {
1038 	static const gchar *thisfn = "nact_main_window_block_reload";
1039 
1040 	g_return_if_fail( NACT_IS_MAIN_WINDOW( window ));
1041 
1042 	if( !window->private->dispose_has_run ){
1043 
1044 		g_debug( "%s: blocking %s signal", thisfn, PIVOT_SIGNAL_ITEMS_CHANGED );
1045 		g_signal_handler_block( window->private->updater, window->private->pivot_handler_id );
1046 		na_timeout_event( &window->private->pivot_timeout );
1047 	}
1048 }
1049 
1050 static void
on_block_items_changed_timeout(NactMainWindow * window)1051 on_block_items_changed_timeout( NactMainWindow *window )
1052 {
1053 	static const gchar *thisfn = "nact_main_window_on_block_items_changed_timeout";
1054 
1055 	g_return_if_fail( NACT_IS_MAIN_WINDOW( window ));
1056 
1057 	g_debug( "%s: unblocking %s signal", thisfn, PIVOT_SIGNAL_ITEMS_CHANGED );
1058 	g_signal_handler_unblock( window->private->updater, window->private->pivot_handler_id );
1059 }
1060 
1061 /*
1062  * the modification status of the items view has changed
1063  */
1064 static void
on_tree_view_modified_status_changed(NactMainWindow * window,gboolean is_modified,gpointer user_data)1065 on_tree_view_modified_status_changed( NactMainWindow *window, gboolean is_modified, gpointer user_data )
1066 {
1067 	static const gchar *thisfn = "nact_main_window_on_tree_view_modified_status_changed";
1068 
1069 	g_debug( "%s: window=%p, is_modified=%s, user_data=%p",
1070 			thisfn, ( void * ) window, is_modified ? "True":"False", ( void * ) user_data );
1071 
1072 	if( !window->private->dispose_has_run ){
1073 
1074 		window->private->is_tree_modified = is_modified;
1075 		setup_dialog_title( window );
1076 	}
1077 }
1078 
1079 /*
1080  * tree view selection has changed
1081  */
1082 static void
on_tree_view_selection_changed(NactMainWindow * window,GList * selected_items,gpointer user_data)1083 on_tree_view_selection_changed( NactMainWindow *window, GList *selected_items, gpointer user_data )
1084 {
1085 	static const gchar *thisfn = "nact_main_window_on_tree_view_selection_changed";
1086 	guint count;
1087 
1088 	count = g_list_length( selected_items );
1089 
1090 	if( !window->private->dispose_has_run ){
1091 		g_debug( "%s: window=%p, selected_items=%p (count=%d), user_data=%p",
1092 				thisfn, ( void * ) window,
1093 				( void * ) selected_items, count, ( void * ) user_data );
1094 
1095 		raz_selection_properties( window );
1096 
1097 		if( count == 1 ){
1098 			g_return_if_fail( NA_IS_OBJECT_ID( selected_items->data ));
1099 			setup_current_selection( window, NA_OBJECT_ID( selected_items->data ));
1100 			setup_writability_status( window );
1101 		}
1102 
1103 		setup_dialog_title( window );
1104 	}
1105 }
1106 
1107 /*
1108  * cleanup handler for our MAIN_SIGNAL_SELECTION_CHANGED signal
1109  */
1110 static void
on_selection_changed_cleanup_handler(BaseWindow * window,GList * selected_items)1111 on_selection_changed_cleanup_handler( BaseWindow *window, GList *selected_items )
1112 {
1113 	static const gchar *thisfn = "nact_main_window_on_selection_changed_cleanup_handler";
1114 
1115 	g_debug( "%s: window=%p, selected_items=%p (count=%u)",
1116 			thisfn, ( void * ) window,
1117 			( void * ) selected_items, g_list_length( selected_items ));
1118 
1119 	na_object_free_items( selected_items );
1120 }
1121 
1122 static void
on_tab_updatable_item_updated(NactMainWindow * window,NAIContext * context,guint data,gpointer user_data)1123 on_tab_updatable_item_updated( NactMainWindow *window, NAIContext *context, guint data, gpointer user_data )
1124 {
1125 	static const gchar *thisfn = "nact_main_window_on_tab_updatable_item_updated";
1126 
1127 	g_return_if_fail( NACT_IS_MAIN_WINDOW( window ));
1128 
1129 	if( !window->private->dispose_has_run ){
1130 		g_debug( "%s: window=%p, context=%p (%s), data=%u, user_data=%p",
1131 				thisfn, ( void * ) window, ( void * ) context, G_OBJECT_TYPE_NAME( context ),
1132 				data, ( void * ) user_data );
1133 
1134 		if( context ){
1135 			na_object_check_status( context );
1136 		}
1137 	}
1138 }
1139 
1140 static void
raz_selection_properties(NactMainWindow * window)1141 raz_selection_properties( NactMainWindow *window )
1142 {
1143 	window->private->current_item = NULL;
1144 	window->private->current_profile = NULL;
1145 	window->private->current_context = NULL;
1146 	window->private->editable = FALSE;
1147 	window->private->reason = 0;
1148 
1149 	nact_main_statusbar_set_locked( window, FALSE, 0 );
1150 }
1151 
1152 /*
1153  * enter after raz_properties
1154  * only called when only one selected row
1155  */
1156 static void
setup_current_selection(NactMainWindow * window,NAObjectId * selected_row)1157 setup_current_selection( NactMainWindow *window, NAObjectId *selected_row )
1158 {
1159 	guint nb_profiles;
1160 	GList *profiles;
1161 
1162 	if( NA_IS_OBJECT_PROFILE( selected_row )){
1163 		window->private->current_profile = NA_OBJECT_PROFILE( selected_row );
1164 		window->private->current_context = NA_ICONTEXT( selected_row );
1165 		window->private->current_item = NA_OBJECT_ITEM( na_object_get_parent( selected_row ));
1166 
1167 	} else {
1168 		g_return_if_fail( NA_IS_OBJECT_ITEM( selected_row ));
1169 		window->private->current_item = NA_OBJECT_ITEM( selected_row );
1170 		window->private->current_context = NA_ICONTEXT( selected_row );
1171 
1172 		if( NA_IS_OBJECT_ACTION( selected_row )){
1173 			nb_profiles = na_object_get_items_count( selected_row );
1174 
1175 			if( nb_profiles == 1 ){
1176 				profiles = na_object_get_items( selected_row );
1177 				window->private->current_profile = NA_OBJECT_PROFILE( profiles->data );
1178 				window->private->current_context = NA_ICONTEXT( profiles->data );
1179 			}
1180 		}
1181 	}
1182 }
1183 
1184 /*
1185  * the title bar of the main window brings up three informations:
1186  * - the name of the application
1187  * - the name of the currently selected item if there is only one
1188  * - an asterisk if anything has been modified
1189  */
1190 static void
setup_dialog_title(NactMainWindow * window)1191 setup_dialog_title( NactMainWindow *window )
1192 {
1193 	static const gchar *thisfn = "nact_main_window_setup_dialog_title";
1194 	GtkWindow *toplevel;
1195 	NactApplication *application;
1196 	gchar *title;
1197 	gchar *label;
1198 	gchar *tmp;
1199 	gboolean is_modified;
1200 
1201 	g_debug( "%s: window=%p", thisfn, ( void * ) window );
1202 
1203 	application = NACT_APPLICATION( base_window_get_application( BASE_WINDOW( window )));
1204 	title = base_application_get_application_name( BASE_APPLICATION( application ));
1205 
1206 	if( window->private->current_item ){
1207 		label = na_object_get_label( window->private->current_item );
1208 		is_modified = na_object_is_modified( window->private->current_item );
1209 		tmp = g_strdup_printf( "%s%s - %s", is_modified ? "*" : "", label, title );
1210 		g_free( label );
1211 		g_free( title );
1212 		title = tmp;
1213 	}
1214 
1215 	toplevel = base_window_get_gtk_toplevel( BASE_WINDOW( window ));
1216 	gtk_window_set_title( toplevel, title );
1217 	g_free( title );
1218 }
1219 
1220 static void
setup_writability_status(NactMainWindow * window)1221 setup_writability_status( NactMainWindow *window )
1222 {
1223 	g_return_if_fail( NA_IS_OBJECT_ITEM( window->private->current_item ));
1224 
1225 	window->private->editable = na_object_is_finally_writable( window->private->current_item, &window->private->reason );
1226 	nact_main_statusbar_set_locked( window, !window->private->editable, window->private->reason );
1227 }
1228 
1229 /*
1230  * The handler of the signal sent by NAPivot when items have been modified
1231  * in the underlying storage subsystems
1232  */
1233 static void
on_pivot_items_changed(NAUpdater * updater,NactMainWindow * window)1234 on_pivot_items_changed( NAUpdater *updater, NactMainWindow *window )
1235 {
1236 	static const gchar *thisfn = "nact_main_window_on_pivot_items_changed";
1237 	gboolean reload_ok;
1238 
1239 	g_return_if_fail( NA_IS_UPDATER( updater ));
1240 	g_return_if_fail( NACT_IS_MAIN_WINDOW( window ));
1241 
1242 	if( !window->private->dispose_has_run ){
1243 		g_debug( "%s: updater=%p (%s), window=%p (%s)", thisfn,
1244 				( void * ) updater, G_OBJECT_TYPE_NAME( updater ),
1245 				( void * ) window, G_OBJECT_TYPE_NAME( window ));
1246 
1247 		reload_ok = confirm_for_giveup_from_pivot( window );
1248 
1249 		if( reload_ok ){
1250 			load_or_reload_items( window );
1251 		}
1252 	}
1253 }
1254 
1255 /*
1256  * informs the user that the actions in underlying storage subsystem
1257  * have changed, and propose for reloading
1258  *
1259  */
1260 static gboolean
confirm_for_giveup_from_pivot(const NactMainWindow * window)1261 confirm_for_giveup_from_pivot( const NactMainWindow *window )
1262 {
1263 	gboolean reload_ok;
1264 	gchar *first, *second;
1265 
1266 	first = g_strdup(
1267 				_( "One or more actions have been modified in the filesystem.\n"
1268 					"You could keep to work with your current list of actions, "
1269 					"or you may want to reload a fresh one." ));
1270 
1271 	if( window->private->is_tree_modified){
1272 
1273 		gchar *tmp = g_strdup_printf( "%s\n\n%s", first,
1274 				_( "Note that reloading a fresh list of actions requires "
1275 					"that you give up with your current modifications." ));
1276 		g_free( first );
1277 		first = tmp;
1278 	}
1279 
1280 	second = g_strdup( _( "Do you want to reload a fresh list of actions ?" ));
1281 
1282 	reload_ok = base_window_display_yesno_dlg( BASE_WINDOW( window ), first, second );
1283 
1284 	g_free( second );
1285 	g_free( first );
1286 
1287 	return( reload_ok );
1288 }
1289 
1290 /*
1291  * requires a confirmation from the user when is has asked for reloading
1292  * the actions via the Edit menu
1293  */
1294 static gboolean
confirm_for_giveup_from_menu(const NactMainWindow * window)1295 confirm_for_giveup_from_menu( const NactMainWindow *window )
1296 {
1297 	gboolean reload_ok = TRUE;
1298 	gchar *first, *second;
1299 
1300 	if( window->private->is_tree_modified ){
1301 
1302 		first = g_strdup(
1303 					_( "Reloading a fresh list of actions requires "
1304 						"that you give up with your current modifications." ));
1305 
1306 		second = g_strdup( _( "Do you really want to do this ?" ));
1307 
1308 		reload_ok = base_window_display_yesno_dlg( BASE_WINDOW( window ), first, second );
1309 
1310 		g_free( second );
1311 		g_free( first );
1312 	}
1313 
1314 	return( reload_ok );
1315 }
1316 
1317 static void
load_or_reload_items(NactMainWindow * window)1318 load_or_reload_items( NactMainWindow *window )
1319 {
1320 	static const gchar *thisfn = "nact_main_window_load_or_reload_items";
1321 	GList *tree;
1322 
1323 	g_debug( "%s: window=%p", thisfn, ( void * ) window );
1324 
1325 	raz_selection_properties( window );
1326 
1327 	tree = na_updater_load_items( window->private->updater );
1328 	nact_tree_view_fill( window->private->items_view, tree );
1329 
1330 	g_debug( "%s: end of tree view filling", thisfn );
1331 }
1332 
1333 /**
1334  * nact_main_window_quit:
1335  * @window: the #NactMainWindow main window.
1336  *
1337  * Quit the window, thus terminating the application.
1338  *
1339  * Returns: %TRUE if the application will terminate, and the @window object
1340  * is no more valid; %FALSE if the application will continue to run.
1341  */
1342 gboolean
nact_main_window_quit(NactMainWindow * window)1343 nact_main_window_quit( NactMainWindow *window )
1344 {
1345 	static const gchar *thisfn = "nact_main_window_quit";
1346 	gboolean terminated;
1347 
1348 	g_return_val_if_fail( NACT_IS_MAIN_WINDOW( window ), FALSE );
1349 
1350 	terminated = FALSE;
1351 
1352 	if( !window->private->dispose_has_run ){
1353 		g_debug( "%s: window=%p (%s)", thisfn, ( void * ) window, G_OBJECT_TYPE_NAME( window ));
1354 
1355 		if( !window->private->is_tree_modified  || warn_modified( window )){
1356 			g_object_unref( window );
1357 			terminated = TRUE;
1358 		}
1359 	}
1360 
1361 	return( terminated );
1362 }
1363 
1364 /*
1365  * signal handler
1366  * should return %FALSE if it is not willing to quit
1367  * this will also stop the emission of the signal (i.e. the first FALSE wins)
1368  */
1369 static gboolean
on_base_quit_requested(NactApplication * application,NactMainWindow * window)1370 on_base_quit_requested( NactApplication *application, NactMainWindow *window )
1371 {
1372 	static const gchar *thisfn = "nact_main_window_on_base_quit_requested";
1373 	gboolean willing_to;
1374 
1375 	g_return_val_if_fail( NACT_IS_MAIN_WINDOW( window ), TRUE );
1376 
1377 	willing_to = TRUE;
1378 
1379 	if( !window->private->dispose_has_run ){
1380 
1381 		g_debug( "%s: application=%p, window=%p", thisfn, ( void * ) application, ( void * ) window );
1382 
1383 		if( window->private->is_tree_modified ){
1384 			willing_to = nact_confirm_logout_run( window );
1385 		}
1386 	}
1387 
1388 	return( willing_to );
1389 }
1390 
1391 /*
1392  * triggered when the user clicks on the top right [X] button
1393  * returns %TRUE to stop the signal to be propagated (which would cause the
1394  * window to be destroyed); instead we gracefully quit the application
1395  */
1396 static gboolean
on_delete_event(GtkWidget * toplevel,GdkEvent * event,NactMainWindow * window)1397 on_delete_event( GtkWidget *toplevel, GdkEvent *event, NactMainWindow *window )
1398 {
1399 	static const gchar *thisfn = "nact_main_window_on_delete_event";
1400 
1401 	g_debug( "%s: toplevel=%p, event=%p, window=%p",
1402 			thisfn, ( void * ) toplevel, ( void * ) event, ( void * ) window );
1403 
1404 	nact_main_window_quit( window );
1405 
1406 	return( TRUE );
1407 }
1408 
1409 /*
1410  * warn_modified:
1411  * @window: this #NactWindow instance.
1412  *
1413  * Emits a warning if at least one item has been modified.
1414  *
1415  * Returns: %TRUE if the user confirms he wants to quit.
1416  */
1417 static gboolean
warn_modified(NactMainWindow * window)1418 warn_modified( NactMainWindow *window )
1419 {
1420 	gboolean confirm = FALSE;
1421 	gchar *first;
1422 	gchar *second;
1423 
1424 	first = g_strdup_printf( _( "Some items have been modified." ));
1425 	second = g_strdup( _( "Are you sure you want to quit without saving them ?" ));
1426 
1427 	confirm = base_window_display_yesno_dlg( BASE_WINDOW( window ), first, second );
1428 
1429 	g_free( second );
1430 	g_free( first );
1431 
1432 	return( confirm );
1433 }
1434