Skip to content

Commit

Permalink
🎊 Add Alongside OS feature to installer
Browse files Browse the repository at this point in the history
  • Loading branch information
mmstick committed Apr 19, 2019
1 parent 30c228f commit b1081ee
Show file tree
Hide file tree
Showing 8 changed files with 578 additions and 15 deletions.
1 change: 1 addition & 0 deletions src/Dialogs/DecryptDialog.vala
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ public class DecryptDialog: Gtk.Dialog {
unowned Distinst.Disks disks = options.borrow_disks ();
foreach (unowned Distinst.Partition partition in disks.get_encrypted_partitions ()) {
string path = Utils.string_from_utf8 (partition.get_device_path ());

bool is_unlocked = options.is_unlocked (path);

var lock_icon_name = is_unlocked ? "emblem-unlocked" : "dialog-password";
Expand Down
41 changes: 41 additions & 0 deletions src/MainWindow.vala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
public class Installer.MainWindow : Gtk.Dialog {
private Gtk.Stack stack;

private AlongsideView alongside_view;
private DiskView disk_view;
private EncryptView encrypt_view;
private ErrorView error_view;
Expand All @@ -30,6 +31,7 @@ public class Installer.MainWindow : Gtk.Dialog {
private PartitioningView partitioning_view;
private ProgressView progress_view;
private RefreshView refresh_view;
private ResizeView resize_view;
private SuccessView success_view;
private TryInstallView try_install_view;
private bool check_ignored = false;
Expand Down Expand Up @@ -132,6 +134,45 @@ public class Installer.MainWindow : Gtk.Dialog {
try_install_view.custom_step.connect (load_partitioning_view);
try_install_view.next_step.connect (load_disk_view);
try_install_view.refresh_step.connect (load_refresh_view);
try_install_view.alongside_step.connect (load_alongside_view);
}

private void load_alongside_view () {
if (alongside_view == null) {
alongside_view = new AlongsideView ();
alongside_view.previous_view = try_install_view;
alongside_view.next_step.connect ((set_scale, os, free, total) => {
if (set_scale) {
load_resize_view (os, free, total);
} else {
load_encrypt_view ();
}
});

alongside_view.cancel.connect (() => {
stack.visible_child = try_install_view;
});
stack.add (alongside_view);
}

stack.visible_child = alongside_view;
alongside_view.update_options ();
}

private void load_resize_view (string? os, uint64 free, uint64 total) {
if (resize_view == null) {
resize_view = new ResizeView (minimum_disk_size);
resize_view.previous_view = alongside_view;
resize_view.next_step.connect (load_encrypt_view);
resize_view.cancel.connect (() => {
stack.visible_child = alongside_view;
});

stack.add (resize_view);
}

stack.visible_child = resize_view;
resize_view.update_options (os, free, total);
}

private void load_refresh_view () {
Expand Down
24 changes: 24 additions & 0 deletions src/Objects/InstallOptions.vala
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ public class InstallOptions : GLib.Object {

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

private unowned Distinst.Disk? install_device;
private string? install_device_path;

// The amount of free space that should be retained when shrinking (20 GiB).
public const uint64 SHRINK_OVERHEAD = 20 * 2 * 1024 * 1024;

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

public unowned Distinst.Disk? get_install_device () {
if (install_device == null) {
install_device = disks.get_disk_with_mount ("/cdrom");
}

return install_device;
}

public unowned string? get_install_device_path () {
if (install_device_path == null) {
unowned Distinst.Disk? install_device = get_install_device ();
if (install_device != null) {
install_device_path = Utils.string_from_utf8 (install_device.get_device_path ());
}
}

return install_device_path;
}

public void set_minimum_size (uint64 size) {
minimum_size = size;
}
Expand Down Expand Up @@ -118,6 +140,8 @@ public class InstallOptions : GLib.Object {
// Transder ownership of the disks to the caller.
public Distinst.Disks take_disks () {
disks_moved = true;
install_device = null;
install_device_path = null;
return (owned) disks;
}

Expand Down
210 changes: 210 additions & 0 deletions src/Views/AlongsideView.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
/**
* This view is for selecting a location to install alongside an existing operationg system.
*
* Possible install options on this view are:
*
* - Shrinking the largest existing partition on a disk, if possible.
* - Installing to the largest unused region on a disk, if possible.
*/
public class AlongsideView: OptionsView {
public signal void next_step (bool use_scale, string? os, uint64 free, uint64 total);

// Whether to use the resize view for choosing a size or not.
public bool set_scale = false;
// The number of free sectors that the selected install option has.
public uint64 selected_free = 0;
// The number of total sectors that the option has.
public uint64 selected_total = 0;
// The OS that is installed to, or may have ownership of, the option.
public string? selected_os;

// Possible labels that the next button will have, depending on which option is selected.
private string NEXT_LABEL[5];

public AlongsideView () {
Object (
cancellable: true,
artwork: "disks",
title: _("Install Alongside Another OS")
);
}

construct {
NEXT_LABEL = new string[5] {
_("Install"),
_("Resize Partition"),
_("Resize OS"),
_("Install Alongside"),
_("Erase and Install"),
};

next_button.label = NEXT_LABEL[3];
next.connect (() => next_step (set_scale, selected_os, selected_free, selected_total));
show_all ();
}

// Clears existing options in the view, and creates new installation options.
public void update_options () {
base.clear_options ();

var options = InstallOptions.get_default ();

add_alongside_options ();

if (options.get_options ().has_erase_options ()) {
add_erase_options ();
}

base.options.show_all ();
base.select_first_option ();
}

private void add_alongside_options () {
var install_options = InstallOptions.get_default ();
unowned string? install_device = install_options.get_install_device_path ();

foreach (var option in install_options.get_options ().get_alongside_options ()) {
var device = Utils.string_from_utf8 (option.get_device ());

if (install_device != null && install_device == device) {
debug ("skipping %s because it is on the install device\n", device);
continue;
}

string? os = Utils.string_from_utf8 (option.get_os ());
os = os == "none" ? null : os;

var free = option.get_sectors_free ();
var total = option.get_sectors_total ();
var partition = option.get_partition ();
var partition_path = Utils.string_from_utf8 (option.get_path ());
string logo = Utils.get_distribution_logo_from_alongside (option);

string label;
string details;
if (partition == -1) {
label = _("Unused space on %s").printf (device);
details = _("%.1f GiB available").printf ((double) free / SECTORS_AS_GIB);
} else {
label = _("%s on %s").printf (os == null ? _("Partition") : os, device);
details = _("Shrink %s (%.1f GiB free)")
.printf (
partition_path,
(double) free / SECTORS_AS_GIB
);
}

base.add_option (logo, label, details, (button) => {
unowned string next_label;
if (partition == -1) {
next_label = NEXT_LABEL[0];
} else if (os == null) {
next_label = NEXT_LABEL[1];
} else {
next_label = NEXT_LABEL[2];
}

button.key_press_event.connect ((event) => handle_key_press (button, event));
button.notify["active"].connect (() => {
if (button.active) {
base.options.get_children ().foreach ((child) => {
if (child is Gtk.ToggleButton) {
((Gtk.ToggleButton)child).active = child == button;
}
});

install_options.selected_option = new Distinst.InstallOption () {
tag = Distinst.InstallOptionVariant.ALONGSIDE,
option = (void*) option,
encrypt_pass = null,
sectors = (partition == -1) ? 0 : free - 1
};

set_scale = partition != -1;
selected_os = os;
selected_free = free;
selected_total = total;
next_button.label = next_label;
next_button.sensitive = true;
} else {
next_button.label = NEXT_LABEL[3];
next_button.sensitive = false;
}
});
});
}
}

private void add_erase_options () {
var install_options = InstallOptions.get_default ();
unowned Distinst.InstallOptions options = install_options.get_updated_options ();
unowned string? install_device = install_options.get_install_device_path ();

foreach (unowned Distinst.EraseOption disk in options.get_erase_options ()) {
string device_path = Utils.string_from_utf8 (disk.get_device_path ());

if (install_device != null && install_device == device_path && !install_options.has_recovery ()) {
continue;
}

string logo = Utils.string_from_utf8 (disk.get_linux_icon ());
string label = Utils.string_from_utf8 (disk.get_model ());
string details = "Erase %s %.1f GiB".printf (
Utils.string_from_utf8 (disk.get_device_path ()),
(double) disk.get_sectors () / SECTORS_AS_GIB
);

base.add_option(logo, label, details, (button) => {
if (disk.meets_requirements ()) {
button.key_press_event.connect ((event) => handle_key_press (button, event));
button.notify["active"].connect (() => {
if (button.active) {
base.options.get_children ().foreach ((child) => {
if (child is Gtk.ToggleButton) {
((Gtk.ToggleButton)child).active = child == button;
}
});

if (install_options.has_recovery ()) {
var recovery = options.get_recovery_option ();

install_options.selected_option = new Distinst.InstallOption () {
tag = Distinst.InstallOptionVariant.RECOVERY,
option = (void*) recovery,
encrypt_pass = null
};
} else {
install_options.selected_option = new Distinst.InstallOption () {
tag = Distinst.InstallOptionVariant.ERASE,
option = (void*) disk,
encrypt_pass = null
};
}

set_scale = false;
next_button.label = NEXT_LABEL[4];
next_button.sensitive = true;
} else {
next_button.sensitive = false;
next_button.label = NEXT_LABEL[3];
}
});
} else {
button.sensitive = false;
}
});
}

base.sort_sensitive ();
}

private bool handle_key_press (Gtk.Button button, Gdk.EventKey event) {
if (event.keyval == Gdk.Key.Return) {
button.clicked ();
next_button.clicked ();
return true;
}

return false;
}
}
7 changes: 7 additions & 0 deletions src/Views/DiskView.vala
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,15 @@ public class Installer.DiskView : OptionsView {

var install_options = InstallOptions.get_default ();
unowned Distinst.InstallOptions options = install_options.get_updated_options ();
unowned string? install_device = install_options.get_install_device_path ();

foreach (unowned Distinst.EraseOption disk in options.get_erase_options ()) {
string device_path = Utils.string_from_utf8 (disk.get_device_path ());

if (install_device != null && install_device == device_path && !install_options.has_recovery ()) {
continue;
}

string logo = Utils.string_from_utf8 (disk.get_linux_icon ());
string label = Utils.string_from_utf8 (disk.get_model ());
string details = "%s %.1f GiB".printf (
Expand Down
Loading

0 comments on commit b1081ee

Please sign in to comment.