1 /*
2  * This file is part of gtkD.
3  *
4  * gtkD is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License
6  * as published by the Free Software Foundation; either version 3
7  * of the License, or (at your option) any later version, with
8  * some exceptions, please read the COPYING file.
9  *
10  * gtkD 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 Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with gtkD; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
18  */
19 
20 // generated automatically - do not change
21 // find conversion definition on APILookup.txt
22 // implement new conversion functionalities on the wrap.utils pakage
23 
24 
25 module gio.DriveT;
26 
27 public  import gio.AsyncResultIF;
28 public  import gio.Cancellable;
29 public  import gio.Icon;
30 public  import gio.IconIF;
31 public  import gio.MountOperation;
32 public  import glib.ErrorG;
33 public  import glib.GException;
34 public  import glib.ListG;
35 public  import glib.Str;
36 public  import gobject.ObjectG;
37 public  import gobject.Signals;
38 public  import gtkc.gdktypes;
39 public  import gtkc.gio;
40 public  import gtkc.giotypes;
41 public  import std.algorithm;
42 
43 
44 /**
45  * #GDrive - this represent a piece of hardware connected to the machine.
46  * It's generally only created for removable hardware or hardware with
47  * removable media.
48  * 
49  * #GDrive is a container class for #GVolume objects that stem from
50  * the same piece of media. As such, #GDrive abstracts a drive with
51  * (or without) removable media and provides operations for querying
52  * whether media is available, determining whether media change is
53  * automatically detected and ejecting the media.
54  * 
55  * If the #GDrive reports that media isn't automatically detected, one
56  * can poll for media; typically one should not do this periodically
57  * as a poll for media operation is potententially expensive and may
58  * spin up the drive creating noise.
59  * 
60  * #GDrive supports starting and stopping drives with authentication
61  * support for the former. This can be used to support a diverse set
62  * of use cases including connecting/disconnecting iSCSI devices,
63  * powering down external disk enclosures and starting/stopping
64  * multi-disk devices such as RAID devices. Note that the actual
65  * semantics and side-effects of starting/stopping a #GDrive may vary
66  * according to implementation. To choose the correct verbs in e.g. a
67  * file manager, use g_drive_get_start_stop_type().
68  * 
69  * For porting from GnomeVFS note that there is no equivalent of
70  * #GDrive in that API.
71  */
72 public template DriveT(TStruct)
73 {
74 	/** Get the main Gtk struct */
75 	public GDrive* getDriveStruct()
76 	{
77 		return cast(GDrive*)getStruct();
78 	}
79 
80 
81 	/**
82 	 * Checks if a drive can be ejected.
83 	 *
84 	 * Return: %TRUE if the @drive can be ejected, %FALSE otherwise.
85 	 */
86 	public bool canEject()
87 	{
88 		return g_drive_can_eject(getDriveStruct()) != 0;
89 	}
90 
91 	/**
92 	 * Checks if a drive can be polled for media changes.
93 	 *
94 	 * Return: %TRUE if the @drive can be polled for media changes,
95 	 *     %FALSE otherwise.
96 	 */
97 	public bool canPollForMedia()
98 	{
99 		return g_drive_can_poll_for_media(getDriveStruct()) != 0;
100 	}
101 
102 	/**
103 	 * Checks if a drive can be started.
104 	 *
105 	 * Return: %TRUE if the @drive can be started, %FALSE otherwise.
106 	 *
107 	 * Since: 2.22
108 	 */
109 	public bool canStart()
110 	{
111 		return g_drive_can_start(getDriveStruct()) != 0;
112 	}
113 
114 	/**
115 	 * Checks if a drive can be started degraded.
116 	 *
117 	 * Return: %TRUE if the @drive can be started degraded, %FALSE otherwise.
118 	 *
119 	 * Since: 2.22
120 	 */
121 	public bool canStartDegraded()
122 	{
123 		return g_drive_can_start_degraded(getDriveStruct()) != 0;
124 	}
125 
126 	/**
127 	 * Checks if a drive can be stopped.
128 	 *
129 	 * Return: %TRUE if the @drive can be stopped, %FALSE otherwise.
130 	 *
131 	 * Since: 2.22
132 	 */
133 	public bool canStop()
134 	{
135 		return g_drive_can_stop(getDriveStruct()) != 0;
136 	}
137 
138 	/**
139 	 * Asynchronously ejects a drive.
140 	 *
141 	 * When the operation is finished, @callback will be called.
142 	 * You can then call g_drive_eject_finish() to obtain the
143 	 * result of the operation.
144 	 *
145 	 * Deprecated: Use g_drive_eject_with_operation() instead.
146 	 *
147 	 * Params:
148 	 *     flags = flags affecting the unmount if required for eject
149 	 *     cancellable = optional #GCancellable object, %NULL to ignore.
150 	 *     callback = a #GAsyncReadyCallback, or %NULL.
151 	 *     userData = user data to pass to @callback
152 	 */
153 	public void eject(GMountUnmountFlags flags, Cancellable cancellable, GAsyncReadyCallback callback, void* userData)
154 	{
155 		g_drive_eject(getDriveStruct(), flags, (cancellable is null) ? null : cancellable.getCancellableStruct(), callback, userData);
156 	}
157 
158 	/**
159 	 * Finishes ejecting a drive.
160 	 *
161 	 * Deprecated: Use g_drive_eject_with_operation_finish() instead.
162 	 *
163 	 * Params:
164 	 *     result = a #GAsyncResult.
165 	 *
166 	 * Return: %TRUE if the drive has been ejected successfully,
167 	 *     %FALSE otherwise.
168 	 *
169 	 * Throws: GException on failure.
170 	 */
171 	public bool ejectFinish(AsyncResultIF result)
172 	{
173 		GError* err = null;
174 		
175 		auto p = g_drive_eject_finish(getDriveStruct(), (result is null) ? null : result.getAsyncResultStruct(), &err) != 0;
176 		
177 		if (err !is null)
178 		{
179 			throw new GException( new ErrorG(err) );
180 		}
181 		
182 		return p;
183 	}
184 
185 	/**
186 	 * Ejects a drive. This is an asynchronous operation, and is
187 	 * finished by calling g_drive_eject_with_operation_finish() with the @drive
188 	 * and #GAsyncResult data returned in the @callback.
189 	 *
190 	 * Params:
191 	 *     flags = flags affecting the unmount if required for eject
192 	 *     mountOperation = a #GMountOperation or %NULL to avoid
193 	 *         user interaction.
194 	 *     cancellable = optional #GCancellable object, %NULL to ignore.
195 	 *     callback = a #GAsyncReadyCallback, or %NULL.
196 	 *     userData = user data passed to @callback.
197 	 *
198 	 * Since: 2.22
199 	 */
200 	public void ejectWithOperation(GMountUnmountFlags flags, MountOperation mountOperation, Cancellable cancellable, GAsyncReadyCallback callback, void* userData)
201 	{
202 		g_drive_eject_with_operation(getDriveStruct(), flags, (mountOperation is null) ? null : mountOperation.getMountOperationStruct(), (cancellable is null) ? null : cancellable.getCancellableStruct(), callback, userData);
203 	}
204 
205 	/**
206 	 * Finishes ejecting a drive. If any errors occurred during the operation,
207 	 * @error will be set to contain the errors and %FALSE will be returned.
208 	 *
209 	 * Params:
210 	 *     result = a #GAsyncResult.
211 	 *
212 	 * Return: %TRUE if the drive was successfully ejected. %FALSE otherwise.
213 	 *
214 	 * Since: 2.22
215 	 *
216 	 * Throws: GException on failure.
217 	 */
218 	public bool ejectWithOperationFinish(AsyncResultIF result)
219 	{
220 		GError* err = null;
221 		
222 		auto p = g_drive_eject_with_operation_finish(getDriveStruct(), (result is null) ? null : result.getAsyncResultStruct(), &err) != 0;
223 		
224 		if (err !is null)
225 		{
226 			throw new GException( new ErrorG(err) );
227 		}
228 		
229 		return p;
230 	}
231 
232 	/**
233 	 * Gets the kinds of identifiers that @drive has.
234 	 * Use g_drive_get_identifier() to obtain the identifiers
235 	 * themselves.
236 	 *
237 	 * Return: a %NULL-terminated
238 	 *     array of strings containing kinds of identifiers. Use g_strfreev()
239 	 *     to free.
240 	 */
241 	public string[] enumerateIdentifiers()
242 	{
243 		auto retStr = g_drive_enumerate_identifiers(getDriveStruct());
244 		
245 		scope(exit) Str.freeStringArray(retStr);
246 		return Str.toStringArray(retStr);
247 	}
248 
249 	/**
250 	 * Gets the icon for @drive.
251 	 *
252 	 * Return: #GIcon for the @drive.
253 	 *     Free the returned object with g_object_unref().
254 	 */
255 	public IconIF getIcon()
256 	{
257 		auto p = g_drive_get_icon(getDriveStruct());
258 		
259 		if(p is null)
260 		{
261 			return null;
262 		}
263 		
264 		return ObjectG.getDObject!(Icon, IconIF)(cast(GIcon*) p, true);
265 	}
266 
267 	/**
268 	 * Gets the identifier of the given kind for @drive.
269 	 *
270 	 * Params:
271 	 *     kind = the kind of identifier to return
272 	 *
273 	 * Return: a newly allocated string containing the
274 	 *     requested identfier, or %NULL if the #GDrive
275 	 *     doesn't have this kind of identifier.
276 	 */
277 	public string getIdentifier(string kind)
278 	{
279 		auto retStr = g_drive_get_identifier(getDriveStruct(), Str.toStringz(kind));
280 		
281 		scope(exit) Str.freeString(retStr);
282 		return Str.toString(retStr);
283 	}
284 
285 	/**
286 	 * Gets the name of @drive.
287 	 *
288 	 * Return: a string containing @drive's name. The returned
289 	 *     string should be freed when no longer needed.
290 	 */
291 	public string getName()
292 	{
293 		auto retStr = g_drive_get_name(getDriveStruct());
294 		
295 		scope(exit) Str.freeString(retStr);
296 		return Str.toString(retStr);
297 	}
298 
299 	/**
300 	 * Gets the sort key for @drive, if any.
301 	 *
302 	 * Return: Sorting key for @drive or %NULL if no such key is available.
303 	 *
304 	 * Since: 2.32
305 	 */
306 	public string getSortKey()
307 	{
308 		return Str.toString(g_drive_get_sort_key(getDriveStruct()));
309 	}
310 
311 	/**
312 	 * Gets a hint about how a drive can be started/stopped.
313 	 *
314 	 * Return: A value from the #GDriveStartStopType enumeration.
315 	 *
316 	 * Since: 2.22
317 	 */
318 	public GDriveStartStopType getStartStopType()
319 	{
320 		return g_drive_get_start_stop_type(getDriveStruct());
321 	}
322 
323 	/**
324 	 * Gets the icon for @drive.
325 	 *
326 	 * Return: symbolic #GIcon for the @drive.
327 	 *     Free the returned object with g_object_unref().
328 	 *
329 	 * Since: 2.34
330 	 */
331 	public IconIF getSymbolicIcon()
332 	{
333 		auto p = g_drive_get_symbolic_icon(getDriveStruct());
334 		
335 		if(p is null)
336 		{
337 			return null;
338 		}
339 		
340 		return ObjectG.getDObject!(Icon, IconIF)(cast(GIcon*) p, true);
341 	}
342 
343 	/**
344 	 * Get a list of mountable volumes for @drive.
345 	 *
346 	 * The returned list should be freed with g_list_free(), after
347 	 * its elements have been unreffed with g_object_unref().
348 	 *
349 	 * Return: #GList containing any #GVolume objects on the given @drive.
350 	 */
351 	public ListG getVolumes()
352 	{
353 		auto p = g_drive_get_volumes(getDriveStruct());
354 		
355 		if(p is null)
356 		{
357 			return null;
358 		}
359 		
360 		return new ListG(cast(GList*) p, true);
361 	}
362 
363 	/**
364 	 * Checks if the @drive has media. Note that the OS may not be polling
365 	 * the drive for media changes; see g_drive_is_media_check_automatic()
366 	 * for more details.
367 	 *
368 	 * Return: %TRUE if @drive has media, %FALSE otherwise.
369 	 */
370 	public bool hasMedia()
371 	{
372 		return g_drive_has_media(getDriveStruct()) != 0;
373 	}
374 
375 	/**
376 	 * Check if @drive has any mountable volumes.
377 	 *
378 	 * Return: %TRUE if the @drive contains volumes, %FALSE otherwise.
379 	 */
380 	public bool hasVolumes()
381 	{
382 		return g_drive_has_volumes(getDriveStruct()) != 0;
383 	}
384 
385 	/**
386 	 * Checks if @drive is capabable of automatically detecting media changes.
387 	 *
388 	 * Return: %TRUE if the @drive is capabable of automatically detecting
389 	 *     media changes, %FALSE otherwise.
390 	 */
391 	public bool isMediaCheckAutomatic()
392 	{
393 		return g_drive_is_media_check_automatic(getDriveStruct()) != 0;
394 	}
395 
396 	/**
397 	 * Checks if the @drive supports removable media.
398 	 *
399 	 * Return: %TRUE if @drive supports removable media, %FALSE otherwise.
400 	 */
401 	public bool isMediaRemovable()
402 	{
403 		return g_drive_is_media_removable(getDriveStruct()) != 0;
404 	}
405 
406 	/**
407 	 * Checks if the #GDrive and/or its media is considered removable by the user.
408 	 * See g_drive_is_media_removable().
409 	 *
410 	 * Return: %TRUE if @drive and/or its media is considered removable, %FALSE otherwise.
411 	 *
412 	 * Since: 2.50
413 	 */
414 	public bool isRemovable()
415 	{
416 		return g_drive_is_removable(getDriveStruct()) != 0;
417 	}
418 
419 	/**
420 	 * Asynchronously polls @drive to see if media has been inserted or removed.
421 	 *
422 	 * When the operation is finished, @callback will be called.
423 	 * You can then call g_drive_poll_for_media_finish() to obtain the
424 	 * result of the operation.
425 	 *
426 	 * Params:
427 	 *     cancellable = optional #GCancellable object, %NULL to ignore.
428 	 *     callback = a #GAsyncReadyCallback, or %NULL.
429 	 *     userData = user data to pass to @callback
430 	 */
431 	public void pollForMedia(Cancellable cancellable, GAsyncReadyCallback callback, void* userData)
432 	{
433 		g_drive_poll_for_media(getDriveStruct(), (cancellable is null) ? null : cancellable.getCancellableStruct(), callback, userData);
434 	}
435 
436 	/**
437 	 * Finishes an operation started with g_drive_poll_for_media() on a drive.
438 	 *
439 	 * Params:
440 	 *     result = a #GAsyncResult.
441 	 *
442 	 * Return: %TRUE if the drive has been poll_for_mediaed successfully,
443 	 *     %FALSE otherwise.
444 	 *
445 	 * Throws: GException on failure.
446 	 */
447 	public bool pollForMediaFinish(AsyncResultIF result)
448 	{
449 		GError* err = null;
450 		
451 		auto p = g_drive_poll_for_media_finish(getDriveStruct(), (result is null) ? null : result.getAsyncResultStruct(), &err) != 0;
452 		
453 		if (err !is null)
454 		{
455 			throw new GException( new ErrorG(err) );
456 		}
457 		
458 		return p;
459 	}
460 
461 	/**
462 	 * Asynchronously starts a drive.
463 	 *
464 	 * When the operation is finished, @callback will be called.
465 	 * You can then call g_drive_start_finish() to obtain the
466 	 * result of the operation.
467 	 *
468 	 * Params:
469 	 *     flags = flags affecting the start operation.
470 	 *     mountOperation = a #GMountOperation or %NULL to avoid
471 	 *         user interaction.
472 	 *     cancellable = optional #GCancellable object, %NULL to ignore.
473 	 *     callback = a #GAsyncReadyCallback, or %NULL.
474 	 *     userData = user data to pass to @callback
475 	 *
476 	 * Since: 2.22
477 	 */
478 	public void start(GDriveStartFlags flags, MountOperation mountOperation, Cancellable cancellable, GAsyncReadyCallback callback, void* userData)
479 	{
480 		g_drive_start(getDriveStruct(), flags, (mountOperation is null) ? null : mountOperation.getMountOperationStruct(), (cancellable is null) ? null : cancellable.getCancellableStruct(), callback, userData);
481 	}
482 
483 	/**
484 	 * Finishes starting a drive.
485 	 *
486 	 * Params:
487 	 *     result = a #GAsyncResult.
488 	 *
489 	 * Return: %TRUE if the drive has been started successfully,
490 	 *     %FALSE otherwise.
491 	 *
492 	 * Since: 2.22
493 	 *
494 	 * Throws: GException on failure.
495 	 */
496 	public bool startFinish(AsyncResultIF result)
497 	{
498 		GError* err = null;
499 		
500 		auto p = g_drive_start_finish(getDriveStruct(), (result is null) ? null : result.getAsyncResultStruct(), &err) != 0;
501 		
502 		if (err !is null)
503 		{
504 			throw new GException( new ErrorG(err) );
505 		}
506 		
507 		return p;
508 	}
509 
510 	/**
511 	 * Asynchronously stops a drive.
512 	 *
513 	 * When the operation is finished, @callback will be called.
514 	 * You can then call g_drive_stop_finish() to obtain the
515 	 * result of the operation.
516 	 *
517 	 * Params:
518 	 *     flags = flags affecting the unmount if required for stopping.
519 	 *     mountOperation = a #GMountOperation or %NULL to avoid
520 	 *         user interaction.
521 	 *     cancellable = optional #GCancellable object, %NULL to ignore.
522 	 *     callback = a #GAsyncReadyCallback, or %NULL.
523 	 *     userData = user data to pass to @callback
524 	 *
525 	 * Since: 2.22
526 	 */
527 	public void stop(GMountUnmountFlags flags, MountOperation mountOperation, Cancellable cancellable, GAsyncReadyCallback callback, void* userData)
528 	{
529 		g_drive_stop(getDriveStruct(), flags, (mountOperation is null) ? null : mountOperation.getMountOperationStruct(), (cancellable is null) ? null : cancellable.getCancellableStruct(), callback, userData);
530 	}
531 
532 	/**
533 	 * Finishes stopping a drive.
534 	 *
535 	 * Params:
536 	 *     result = a #GAsyncResult.
537 	 *
538 	 * Return: %TRUE if the drive has been stopped successfully,
539 	 *     %FALSE otherwise.
540 	 *
541 	 * Since: 2.22
542 	 *
543 	 * Throws: GException on failure.
544 	 */
545 	public bool stopFinish(AsyncResultIF result)
546 	{
547 		GError* err = null;
548 		
549 		auto p = g_drive_stop_finish(getDriveStruct(), (result is null) ? null : result.getAsyncResultStruct(), &err) != 0;
550 		
551 		if (err !is null)
552 		{
553 			throw new GException( new ErrorG(err) );
554 		}
555 		
556 		return p;
557 	}
558 
559 	protected class OnChangedDelegateWrapper
560 	{
561 		void delegate(DriveIF) dlg;
562 		gulong handlerId;
563 		ConnectFlags flags;
564 		this(void delegate(DriveIF) dlg, gulong handlerId, ConnectFlags flags)
565 		{
566 			this.dlg = dlg;
567 			this.handlerId = handlerId;
568 			this.flags = flags;
569 		}
570 	}
571 	protected OnChangedDelegateWrapper[] onChangedListeners;
572 
573 	/**
574 	 * Emitted when the drive's state has changed.
575 	 */
576 	gulong addOnChanged(void delegate(DriveIF) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0)
577 	{
578 		onChangedListeners ~= new OnChangedDelegateWrapper(dlg, 0, connectFlags);
579 		onChangedListeners[onChangedListeners.length - 1].handlerId = Signals.connectData(
580 			this,
581 			"changed",
582 			cast(GCallback)&callBackChanged,
583 			cast(void*)onChangedListeners[onChangedListeners.length - 1],
584 			cast(GClosureNotify)&callBackChangedDestroy,
585 			connectFlags);
586 		return onChangedListeners[onChangedListeners.length - 1].handlerId;
587 	}
588 	
589 	extern(C) static void callBackChanged(GDrive* driveStruct,OnChangedDelegateWrapper wrapper)
590 	{
591 		wrapper.dlg(wrapper.outer);
592 	}
593 	
594 	extern(C) static void callBackChangedDestroy(OnChangedDelegateWrapper wrapper, GClosure* closure)
595 	{
596 		wrapper.outer.internalRemoveOnChanged(wrapper);
597 	}
598 
599 	protected void internalRemoveOnChanged(OnChangedDelegateWrapper source)
600 	{
601 		foreach(index, wrapper; onChangedListeners)
602 		{
603 			if (wrapper.dlg == source.dlg && wrapper.flags == source.flags && wrapper.handlerId == source.handlerId)
604 			{
605 				onChangedListeners[index] = null;
606 				onChangedListeners = std.algorithm.remove(onChangedListeners, index);
607 				break;
608 			}
609 		}
610 	}
611 	
612 
613 	protected class OnDisconnectedDelegateWrapper
614 	{
615 		void delegate(DriveIF) dlg;
616 		gulong handlerId;
617 		ConnectFlags flags;
618 		this(void delegate(DriveIF) dlg, gulong handlerId, ConnectFlags flags)
619 		{
620 			this.dlg = dlg;
621 			this.handlerId = handlerId;
622 			this.flags = flags;
623 		}
624 	}
625 	protected OnDisconnectedDelegateWrapper[] onDisconnectedListeners;
626 
627 	/**
628 	 * This signal is emitted when the #GDrive have been
629 	 * disconnected. If the recipient is holding references to the
630 	 * object they should release them so the object can be
631 	 * finalized.
632 	 */
633 	gulong addOnDisconnected(void delegate(DriveIF) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0)
634 	{
635 		onDisconnectedListeners ~= new OnDisconnectedDelegateWrapper(dlg, 0, connectFlags);
636 		onDisconnectedListeners[onDisconnectedListeners.length - 1].handlerId = Signals.connectData(
637 			this,
638 			"disconnected",
639 			cast(GCallback)&callBackDisconnected,
640 			cast(void*)onDisconnectedListeners[onDisconnectedListeners.length - 1],
641 			cast(GClosureNotify)&callBackDisconnectedDestroy,
642 			connectFlags);
643 		return onDisconnectedListeners[onDisconnectedListeners.length - 1].handlerId;
644 	}
645 	
646 	extern(C) static void callBackDisconnected(GDrive* driveStruct,OnDisconnectedDelegateWrapper wrapper)
647 	{
648 		wrapper.dlg(wrapper.outer);
649 	}
650 	
651 	extern(C) static void callBackDisconnectedDestroy(OnDisconnectedDelegateWrapper wrapper, GClosure* closure)
652 	{
653 		wrapper.outer.internalRemoveOnDisconnected(wrapper);
654 	}
655 
656 	protected void internalRemoveOnDisconnected(OnDisconnectedDelegateWrapper source)
657 	{
658 		foreach(index, wrapper; onDisconnectedListeners)
659 		{
660 			if (wrapper.dlg == source.dlg && wrapper.flags == source.flags && wrapper.handlerId == source.handlerId)
661 			{
662 				onDisconnectedListeners[index] = null;
663 				onDisconnectedListeners = std.algorithm.remove(onDisconnectedListeners, index);
664 				break;
665 			}
666 		}
667 	}
668 	
669 
670 	protected class OnEjectButtonDelegateWrapper
671 	{
672 		void delegate(DriveIF) dlg;
673 		gulong handlerId;
674 		ConnectFlags flags;
675 		this(void delegate(DriveIF) dlg, gulong handlerId, ConnectFlags flags)
676 		{
677 			this.dlg = dlg;
678 			this.handlerId = handlerId;
679 			this.flags = flags;
680 		}
681 	}
682 	protected OnEjectButtonDelegateWrapper[] onEjectButtonListeners;
683 
684 	/**
685 	 * Emitted when the physical eject button (if any) of a drive has
686 	 * been pressed.
687 	 */
688 	gulong addOnEjectButton(void delegate(DriveIF) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0)
689 	{
690 		onEjectButtonListeners ~= new OnEjectButtonDelegateWrapper(dlg, 0, connectFlags);
691 		onEjectButtonListeners[onEjectButtonListeners.length - 1].handlerId = Signals.connectData(
692 			this,
693 			"eject-button",
694 			cast(GCallback)&callBackEjectButton,
695 			cast(void*)onEjectButtonListeners[onEjectButtonListeners.length - 1],
696 			cast(GClosureNotify)&callBackEjectButtonDestroy,
697 			connectFlags);
698 		return onEjectButtonListeners[onEjectButtonListeners.length - 1].handlerId;
699 	}
700 	
701 	extern(C) static void callBackEjectButton(GDrive* driveStruct,OnEjectButtonDelegateWrapper wrapper)
702 	{
703 		wrapper.dlg(wrapper.outer);
704 	}
705 	
706 	extern(C) static void callBackEjectButtonDestroy(OnEjectButtonDelegateWrapper wrapper, GClosure* closure)
707 	{
708 		wrapper.outer.internalRemoveOnEjectButton(wrapper);
709 	}
710 
711 	protected void internalRemoveOnEjectButton(OnEjectButtonDelegateWrapper source)
712 	{
713 		foreach(index, wrapper; onEjectButtonListeners)
714 		{
715 			if (wrapper.dlg == source.dlg && wrapper.flags == source.flags && wrapper.handlerId == source.handlerId)
716 			{
717 				onEjectButtonListeners[index] = null;
718 				onEjectButtonListeners = std.algorithm.remove(onEjectButtonListeners, index);
719 				break;
720 			}
721 		}
722 	}
723 	
724 
725 	protected class OnStopButtonDelegateWrapper
726 	{
727 		void delegate(DriveIF) dlg;
728 		gulong handlerId;
729 		ConnectFlags flags;
730 		this(void delegate(DriveIF) dlg, gulong handlerId, ConnectFlags flags)
731 		{
732 			this.dlg = dlg;
733 			this.handlerId = handlerId;
734 			this.flags = flags;
735 		}
736 	}
737 	protected OnStopButtonDelegateWrapper[] onStopButtonListeners;
738 
739 	/**
740 	 * Emitted when the physical stop button (if any) of a drive has
741 	 * been pressed.
742 	 *
743 	 * Since: 2.22
744 	 */
745 	gulong addOnStopButton(void delegate(DriveIF) dlg, ConnectFlags connectFlags=cast(ConnectFlags)0)
746 	{
747 		onStopButtonListeners ~= new OnStopButtonDelegateWrapper(dlg, 0, connectFlags);
748 		onStopButtonListeners[onStopButtonListeners.length - 1].handlerId = Signals.connectData(
749 			this,
750 			"stop-button",
751 			cast(GCallback)&callBackStopButton,
752 			cast(void*)onStopButtonListeners[onStopButtonListeners.length - 1],
753 			cast(GClosureNotify)&callBackStopButtonDestroy,
754 			connectFlags);
755 		return onStopButtonListeners[onStopButtonListeners.length - 1].handlerId;
756 	}
757 	
758 	extern(C) static void callBackStopButton(GDrive* driveStruct,OnStopButtonDelegateWrapper wrapper)
759 	{
760 		wrapper.dlg(wrapper.outer);
761 	}
762 	
763 	extern(C) static void callBackStopButtonDestroy(OnStopButtonDelegateWrapper wrapper, GClosure* closure)
764 	{
765 		wrapper.outer.internalRemoveOnStopButton(wrapper);
766 	}
767 
768 	protected void internalRemoveOnStopButton(OnStopButtonDelegateWrapper source)
769 	{
770 		foreach(index, wrapper; onStopButtonListeners)
771 		{
772 			if (wrapper.dlg == source.dlg && wrapper.flags == source.flags && wrapper.handlerId == source.handlerId)
773 			{
774 				onStopButtonListeners[index] = null;
775 				onStopButtonListeners = std.algorithm.remove(onStopButtonListeners, index);
776 				break;
777 			}
778 		}
779 	}
780 	
781 }