Skip to content

Commit d8a21ac

Browse files
committed
🎊 Add Alongside OS feature to installer
1 parent 30c228f commit d8a21ac

File tree

8 files changed

+580
-15
lines changed

8 files changed

+580
-15
lines changed

src/Dialogs/DecryptDialog.vala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ public class DecryptDialog: Gtk.Dialog {
263263
unowned Distinst.Disks disks = options.borrow_disks ();
264264
foreach (unowned Distinst.Partition partition in disks.get_encrypted_partitions ()) {
265265
string path = Utils.string_from_utf8 (partition.get_device_path ());
266+
266267
bool is_unlocked = options.is_unlocked (path);
267268

268269
var lock_icon_name = is_unlocked ? "emblem-unlocked" : "dialog-password";

src/MainWindow.vala

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
public class Installer.MainWindow : Gtk.Dialog {
2222
private Gtk.Stack stack;
2323

24+
private AlongsideView alongside_view;
2425
private DiskView disk_view;
2526
private EncryptView encrypt_view;
2627
private ErrorView error_view;
@@ -30,6 +31,7 @@ public class Installer.MainWindow : Gtk.Dialog {
3031
private PartitioningView partitioning_view;
3132
private ProgressView progress_view;
3233
private RefreshView refresh_view;
34+
private ResizeView resize_view;
3335
private SuccessView success_view;
3436
private TryInstallView try_install_view;
3537
private bool check_ignored = false;
@@ -132,6 +134,45 @@ public class Installer.MainWindow : Gtk.Dialog {
132134
try_install_view.custom_step.connect (load_partitioning_view);
133135
try_install_view.next_step.connect (load_disk_view);
134136
try_install_view.refresh_step.connect (load_refresh_view);
137+
try_install_view.alongside_step.connect (load_alongside_view);
138+
}
139+
140+
private void load_alongside_view () {
141+
if (alongside_view == null) {
142+
alongside_view = new AlongsideView ();
143+
alongside_view.previous_view = try_install_view;
144+
alongside_view.next_step.connect ((set_scale, os, free, total) => {
145+
if (set_scale) {
146+
load_resize_view (os, free, total);
147+
} else {
148+
load_encrypt_view ();
149+
}
150+
});
151+
152+
alongside_view.cancel.connect (() => {
153+
stack.visible_child = try_install_view;
154+
});
155+
stack.add (alongside_view);
156+
}
157+
158+
stack.visible_child = alongside_view;
159+
alongside_view.update_options ();
160+
}
161+
162+
private void load_resize_view (string? os, uint64 free, uint64 total) {
163+
if (resize_view == null) {
164+
resize_view = new ResizeView (minimum_disk_size);
165+
resize_view.previous_view = alongside_view;
166+
resize_view.next_step.connect (load_encrypt_view);
167+
resize_view.cancel.connect (() => {
168+
stack.visible_child = alongside_view;
169+
});
170+
171+
stack.add (resize_view);
172+
}
173+
174+
stack.visible_child = resize_view;
175+
resize_view.update_options (os, free, total);
135176
}
136177

137178
private void load_refresh_view () {

src/Objects/InstallOptions.vala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ public class InstallOptions : GLib.Object {
2929

3030
private Gee.ArrayList<string> unlocked_devices { get; set; default = new Gee.ArrayList<string> (); }
3131

32+
private unowned Distinst.Disk? install_device;
33+
private string? install_device_path;
34+
3235
// The amount of free space that should be retained when shrinking (20 GiB).
3336
public const uint64 SHRINK_OVERHEAD = 20 * 2 * 1024 * 1024;
3437

@@ -40,6 +43,25 @@ public class InstallOptions : GLib.Object {
4043
return _options_object;
4144
}
4245

46+
public unowned Distinst.Disk? get_install_device () {
47+
if (install_device == null) {
48+
install_device = disks.get_disk_with_mount ("/cdrom");
49+
}
50+
51+
return install_device;
52+
}
53+
54+
public unowned string? get_install_device_path () {
55+
if (install_device_path == null) {
56+
unowned Distinst.Disk? install_device = get_install_device ();
57+
if (install_device != null) {
58+
install_device_path = Utils.string_from_utf8 (install_device.get_device_path ());
59+
}
60+
}
61+
62+
return install_device_path;
63+
}
64+
4365
public void set_minimum_size (uint64 size) {
4466
minimum_size = size;
4567
}
@@ -118,6 +140,8 @@ public class InstallOptions : GLib.Object {
118140
// Transder ownership of the disks to the caller.
119141
public Distinst.Disks take_disks () {
120142
disks_moved = true;
143+
install_device = null;
144+
install_device_path = null;
121145
return (owned) disks;
122146
}
123147

src/Views/AlongsideView.vala

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
/**
2+
* This view is for selecting a location to install alongside an existing operationg system.
3+
*
4+
* Possible install options on this view are:
5+
*
6+
* - Shrinking the largest existing partition on a disk, if possible.
7+
* - Installing to the largest unused region on a disk, if possible.
8+
*/
9+
public class AlongsideView: OptionsView {
10+
public signal void next_step (bool use_scale, string? os, uint64 free, uint64 total);
11+
12+
// Whether to use the resize view for choosing a size or not.
13+
public bool set_scale = false;
14+
// The number of free sectors that the selected install option has.
15+
public uint64 selected_free = 0;
16+
// The number of total sectors that the option has.
17+
public uint64 selected_total = 0;
18+
// The OS that is installed to, or may have ownership of, the option.
19+
public string? selected_os;
20+
21+
// Possible labels that the next button will have, depending on which option is selected.
22+
private string NEXT_LABEL[5];
23+
24+
public AlongsideView () {
25+
Object (
26+
cancellable: true,
27+
artwork: "disks",
28+
title: _("Install Alongside Another OS")
29+
);
30+
}
31+
32+
construct {
33+
NEXT_LABEL = new string[5] {
34+
_("Install"),
35+
_("Resize Partition"),
36+
_("Resize OS"),
37+
_("Install Alongside"),
38+
_("Erase and Install"),
39+
};
40+
41+
next_button.label = NEXT_LABEL[3];
42+
next.connect (() => next_step (set_scale, selected_os, selected_free, selected_total));
43+
show_all ();
44+
}
45+
46+
// Clears existing options in the view, and creates new installation options.
47+
public void update_options () {
48+
base.clear_options ();
49+
50+
var options = InstallOptions.get_default ();
51+
52+
add_alongside_options ();
53+
54+
if (options.get_options ().has_erase_options ()) {
55+
add_erase_options ();
56+
}
57+
58+
base.options.show_all ();
59+
base.select_first_option ();
60+
}
61+
62+
private void add_alongside_options () {
63+
var install_options = InstallOptions.get_default ();
64+
unowned string? install_device = install_options.get_install_device_path ();
65+
66+
foreach (var option in install_options.get_options ().get_alongside_options ()) {
67+
var device = Utils.string_from_utf8 (option.get_device ());
68+
69+
if (install_device != null && install_device == device) {
70+
debug ("skipping %s because it is on the install device\n", device);
71+
continue;
72+
}
73+
74+
string? os = Utils.string_from_utf8 (option.get_os ());
75+
os = os == "none" ? null : os;
76+
77+
var free = option.get_sectors_free ();
78+
var total = option.get_sectors_total ();
79+
var partition = option.get_partition ();
80+
var partition_path = Utils.string_from_utf8 (option.get_path ());
81+
string logo = Utils.get_distribution_logo_from_alongside (option);
82+
83+
string label;
84+
string details;
85+
if (partition == -1) {
86+
label = _("Unused space on %s").printf (device);
87+
details = _("%.1f GiB available").printf ((double) free / SECTORS_AS_GIB);
88+
} else {
89+
label = _("%s on %s").printf (os == null ? _("Partition") : os, device);
90+
details = _("Shrink %s (%.1f GiB free)")
91+
.printf (
92+
partition_path,
93+
(double) free / SECTORS_AS_GIB
94+
);
95+
}
96+
97+
base.add_option (logo, label, details, (button) => {
98+
unowned string next_label;
99+
if (partition == -1) {
100+
next_label = NEXT_LABEL[0];
101+
} else if (os == null) {
102+
next_label = NEXT_LABEL[1];
103+
} else {
104+
next_label = NEXT_LABEL[2];
105+
}
106+
107+
button.key_press_event.connect ((event) => handle_key_press (button, event));
108+
button.notify["active"].connect (() => {
109+
if (button.active) {
110+
base.options.get_children ().foreach ((child) => {
111+
if (child is Gtk.ToggleButton) {
112+
((Gtk.ToggleButton)child).active = child == button;
113+
}
114+
});
115+
116+
install_options.selected_option = new Distinst.InstallOption () {
117+
tag = Distinst.InstallOptionVariant.ALONGSIDE,
118+
option = (void*) option,
119+
encrypt_pass = null,
120+
sectors = (partition == -1) ? 0 : free - 1
121+
};
122+
123+
set_scale = partition != -1;
124+
selected_os = os;
125+
selected_free = free;
126+
selected_total = total;
127+
next_button.label = next_label;
128+
next_button.sensitive = true;
129+
} else {
130+
next_button.label = NEXT_LABEL[3];
131+
next_button.sensitive = false;
132+
}
133+
});
134+
});
135+
}
136+
}
137+
138+
private void add_erase_options () {
139+
var install_options = InstallOptions.get_default ();
140+
unowned Distinst.InstallOptions options = install_options.get_updated_options ();
141+
unowned string? install_device = install_options.get_install_device_path ();
142+
143+
foreach (unowned Distinst.EraseOption disk in options.get_erase_options ()) {
144+
string device_path = Utils.string_from_utf8 (disk.get_device_path ());
145+
146+
if (install_device != null && install_device == device_path && !install_options.has_recovery ()) {
147+
continue;
148+
}
149+
150+
string logo = Utils.string_from_utf8 (disk.get_linux_icon ());
151+
string label = Utils.string_from_utf8 (disk.get_model ());
152+
string details = "Erase %s %.1f GiB".printf (
153+
Utils.string_from_utf8 (disk.get_device_path ()),
154+
(double) disk.get_sectors () / SECTORS_AS_GIB
155+
);
156+
157+
base.add_option(logo, label, details, (button) => {
158+
if (disk.meets_requirements ()) {
159+
button.key_press_event.connect ((event) => handle_key_press (button, event));
160+
button.notify["active"].connect (() => {
161+
if (button.active) {
162+
base.options.get_children ().foreach ((child) => {
163+
if (child is Gtk.ToggleButton) {
164+
((Gtk.ToggleButton)child).active = child == button;
165+
}
166+
});
167+
168+
if (install_options.has_recovery ()) {
169+
var recovery = options.get_recovery_option ();
170+
171+
install_options.selected_option = new Distinst.InstallOption () {
172+
tag = Distinst.InstallOptionVariant.RECOVERY,
173+
option = (void*) recovery,
174+
encrypt_pass = null
175+
};
176+
} else {
177+
install_options.selected_option = new Distinst.InstallOption () {
178+
tag = Distinst.InstallOptionVariant.ERASE,
179+
option = (void*) disk,
180+
encrypt_pass = null
181+
};
182+
}
183+
184+
set_scale = false;
185+
next_button.label = NEXT_LABEL[4];
186+
next_button.sensitive = true;
187+
} else {
188+
next_button.sensitive = false;
189+
next_button.label = NEXT_LABEL[3];
190+
}
191+
});
192+
} else {
193+
button.sensitive = false;
194+
}
195+
});
196+
}
197+
198+
base.sort_sensitive ();
199+
}
200+
201+
private bool handle_key_press (Gtk.Button button, Gdk.EventKey event) {
202+
if (event.keyval == Gdk.Key.Return) {
203+
button.clicked ();
204+
next_button.clicked ();
205+
return true;
206+
}
207+
208+
return false;
209+
}
210+
}

src/Views/DiskView.vala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,15 @@ public class Installer.DiskView : OptionsView {
4444

4545
var install_options = InstallOptions.get_default ();
4646
unowned Distinst.InstallOptions options = install_options.get_updated_options ();
47+
unowned string? install_device = install_options.get_install_device_path ();
4748

4849
foreach (unowned Distinst.EraseOption disk in options.get_erase_options ()) {
50+
string device_path = Utils.string_from_utf8 (disk.get_device_path ());
51+
52+
if (install_device != null && install_device == device_path && !install_options.has_recovery ()) {
53+
continue;
54+
}
55+
4956
string logo = Utils.string_from_utf8 (disk.get_linux_icon ());
5057
string label = Utils.string_from_utf8 (disk.get_model ());
5158
string details = "%s %.1f GiB".printf (

0 commit comments

Comments
 (0)