Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion qubes_menu/app_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@

logger = logging.getLogger("qubes-appmenu")

DISP_TEXT = "new Disposable Qube from "
DISP_TEXT = "New disposable qube from "


class AppEntry(Gtk.ListBoxRow):
Expand Down
4 changes: 3 additions & 1 deletion qubes_menu/application_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,9 @@ def _selection_changed(self, _widget, row: Optional[VMRow]):
self.selected_vm_entry = row
self._set_right_visibility(True)
self.network_indicator.set_network_state(row.vm_entry.has_network)
self.control_list.update_visibility(row.vm_entry.power_state)
self.control_list.update_visibility(
row.vm_entry, self.toggle_buttons.apps_toggle.get_active()
)
self.control_list.unselect_all()
self.app_list.ephemeral_vm = bool(self.selected_vm_entry.vm_entry.parent_vm)
self.app_list.invalidate_filter()
Expand Down
58 changes: 38 additions & 20 deletions qubes_menu/custom_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,9 @@ def get_appinfo(self) -> ApplicationInfo:
vm_entry.settings_desktop_file_name
)

def update_state(self, state): # pylint: disable=unused-argument
def update_state(self, vm_entry: VMEntry, apps_tab: bool = False):
"""Update state: should be always visible."""
# pylint: disable=unused-argument
self.show_all()

def show_menu(self, _widget, event):
Expand Down Expand Up @@ -449,7 +450,7 @@ def __init__(self):
self.show_all()
self.command = None

def update_state(self, state):
def update_state(self, vm_entry: VMEntry):
"""
Update own state (visibility/text/sensitivity) based on provided VM
state.
Expand Down Expand Up @@ -497,35 +498,49 @@ def show_menu(self, _widget, event):
if event.button == 3:
self.menu.popup_at_pointer(None) # None means current event

def update_state(self, state):
def update_state(self, vm_entry: VMEntry, apps_tab: bool = False):
"""
Update own state (visibility/text/sensitivity) based on provided VM
state.
"""
self.state = state
if state == "Running":
self.row_label.set_label("Shutdown qube")
self.command = "qvm-shutdown"
vm_name = vm_entry.vm_name
is_dispvm_template = vm_entry.is_dispvm_template
self.state = vm_entry.power_state

if (
vm_name == "dom0"
or (apps_tab and is_dispvm_template and self.state != "Running")
):
self.row_label.set_label(" ")
self.set_sensitive(False)
self.command = None
self.icon.hide()
return

self.set_sensitive(True)
self.icon.show()
if self.state == "Halted" and not (is_dispvm_template and apps_tab):
self.row_label.set_label("Start qube")
self.command = "qvm-start"
self.icon.set_from_pixbuf(
load_icon("qappmenu-shutdown", size=None, pixel_size=15)
load_icon("qappmenu-start", size=None, pixel_size=15)
)
return
if state == "Transient":
if self.state == "Transient":
self.row_label.set_label("Kill qube")
self.command = "qvm-kill"
self.icon.set_from_pixbuf(
load_icon("qappmenu-shutdown", size=None, pixel_size=15)
)
return
if state == "Halted":
self.row_label.set_label("Start qube")
self.command = "qvm-start"
if self.state == "Running":
self.row_label.set_label("Shutdown qube")
self.command = "qvm-shutdown"
self.icon.set_from_pixbuf(
load_icon("qappmenu-start", size=None, pixel_size=15)
load_icon("qappmenu-shutdown", size=None, pixel_size=15)
)

return
if state == "Paused":
if self.state == "Paused":
self.row_label.set_label("Unpause qube")
self.command = "qvm-unpause"
self.icon.set_from_pixbuf(
Expand All @@ -544,13 +559,16 @@ def __init__(self):
self.icon.set_from_pixbuf(load_icon("qappmenu-pause", size=None, pixel_size=15))
self.state = None

def update_state(self, state):
def update_state(self, vm_entry: VMEntry, apps_tab: bool = False):
"""
Update own state (visibility/text/sensitivity) based on provided VM
state.
"""
self.state = state
if state == "Running":
# pylint: disable=unused-argument
vm_name = vm_entry.vm_name
self.state = vm_entry.power_state

if self.state == "Running" and vm_name != "dom0":
self.row_label.set_label("Pause qube")
self.set_sensitive(True)
self.command = "qvm-pause"
Expand Down Expand Up @@ -581,12 +599,12 @@ def __init__(self, app_page):
self.add(self.start_item)
self.add(self.pause_item)

def update_visibility(self, state):
def update_visibility(self, vm_entry: VMEntry, apps_tab: bool = False):
"""
Update children's state based on provided VM state.
"""
for row in self.get_children():
row.update_state(state)
row.update_state(vm_entry, apps_tab)


class KeynavController:
Expand Down
2 changes: 1 addition & 1 deletion qubes_menu/search_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -511,7 +511,7 @@ def _selection_changed(self, _widget, row: Optional[SearchVMRow]):
else:
self.selected_vm_row = row
self.control_list.show()
self.control_list.update_visibility(row.vm_entry.power_state)
self.control_list.update_visibility(row.vm_entry, apps_tab=False)
self.control_list.unselect_all()
self.app_list.invalidate_filter()
self.app_list.select_row(None)
Expand Down
117 changes: 90 additions & 27 deletions qubes_menu/tests/test_app_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,43 +30,104 @@ def test_app_page_vm_state(test_desktop_file_path, test_qapp, test_builder):
dispatcher = MockDispatcher(test_qapp)
vm_manager = VMManager(test_qapp, dispatcher)

with mock.patch.object(DesktopFileManager, 'desktop_dirs',
[test_desktop_file_path]):
with mock.patch.object(
DesktopFileManager, "desktop_dirs", [test_desktop_file_path]
):
desktop_file_manager = DesktopFileManager(test_qapp)

app_page = AppPage(vm_manager, test_builder, desktop_file_manager)

# select a turned off vm
app_page.vm_list.select_row([row for row in app_page.vm_list.get_children()
if row.vm_name == 'test-red'][0])
# For some reason it defaults to the system tab.
app_page.toggle_buttons.apps_toggle.set_active(True)

assert app_page.control_list.start_item.row_label.get_label() == \
"Start qube"
assert app_page.control_list.pause_item.row_label.get_label() == \
" "
# select dom0
app_page.vm_list.select_row(
[
row
for row in app_page.vm_list.get_children()
if row.vm_name == "dom0"
][0]
)
assert app_page.control_list.start_item.row_label.get_label() == " "
assert app_page.control_list.pause_item.row_label.get_label() == " "

# select a turned on vm
app_page.vm_list.select_row([row for row in app_page.vm_list.get_children()
if row.vm_name == 'sys-usb'][0])
# select a turned off vm
app_page.vm_list.select_row(
[
row
for row in app_page.vm_list.get_children()
if row.vm_name == "test-red"
][0]
)

assert (
app_page.control_list.start_item.row_label.get_label() == "Start qube"
)
assert app_page.control_list.pause_item.row_label.get_label() == " "

assert app_page.control_list.start_item.row_label.get_label() == \
"Shutdown qube"
assert app_page.control_list.pause_item.row_label.get_label() == \
"Pause qube"
# select a turned on vm
app_page.vm_list.select_row(
[
row
for row in app_page.vm_list.get_children()
if row.vm_name == "sys-usb"
][0]
)

assert (
app_page.control_list.start_item.row_label.get_label()
== "Shutdown qube"
)
assert (
app_page.control_list.pause_item.row_label.get_label() == "Pause qube"
)

# select a turned off disposable template
app_page.vm_list.select_row(
[
row
for row in app_page.vm_list.get_children()
if row.vm_name == "test-alt-dvm"
][0]
)
assert app_page.control_list.start_item.row_label.get_label() == " "
assert app_page.control_list.pause_item.row_label.get_label() == " "

# select a turned on disposable template
app_page.vm_list.select_row(
[
row
for row in app_page.vm_list.get_children()
if row.vm_name == "test-alt-dvm-running"
][0]
)
assert (
app_page.control_list.start_item.row_label.get_label()
== "Shutdown qube"
)
assert (
app_page.control_list.pause_item.row_label.get_label() == "Pause qube"
)


def test_dispvm_parent_sorting(test_desktop_file_path, test_qapp, test_builder):
# check if dispvm child is sorted after the parent
test_qapp._qubes['disp1233'] = MockQube(
name="disp1233", qapp=test_qapp, klass='DispVM',
template_for_dispvms='True', template='default-dvm', auto_cleanup=True)
test_qapp._qubes["disp1233"] = MockQube(
name="disp1233",
qapp=test_qapp,
klass="DispVM",
template_for_dispvms="True",
template="default-dvm",
auto_cleanup=True,
)
test_qapp.update_vm_calls()

dispatcher = MockDispatcher(test_qapp)
vm_manager = VMManager(test_qapp, dispatcher)

with mock.patch.object(DesktopFileManager, 'desktop_dirs',
[test_desktop_file_path]):
with mock.patch.object(
DesktopFileManager, "desktop_dirs", [test_desktop_file_path]
):
desktop_file_manager = DesktopFileManager(test_qapp)

app_page = AppPage(vm_manager, test_builder, desktop_file_manager)
Expand All @@ -75,11 +136,11 @@ def test_dispvm_parent_sorting(test_desktop_file_path, test_qapp, test_builder):

for row in app_page.vm_list.get_children():
if found_dvm:
if row.vm_name == 'disp1233' and row.vm_entry.parent_vm:
if row.vm_name == "disp1233" and row.vm_entry.parent_vm:
break
found_dvm = False
continue
if row.vm_name == 'default-dvm' and row.vm_entry._is_dispvm_template:
if row.vm_entry.is_dispvm_template:
found_dvm = True
continue
found_dvm = False
Expand All @@ -92,12 +153,14 @@ def test_settings_app_page(test_desktop_file_path, test_qapp, test_builder):
dispatcher = MockDispatcher(test_qapp)
vm_manager = VMManager(test_qapp, dispatcher)

with mock.patch.object(DesktopFileManager, 'desktop_dirs',
[test_desktop_file_path]):
with mock.patch.object(
DesktopFileManager, "desktop_dirs", [test_desktop_file_path]
):
desktop_file_manager = DesktopFileManager(test_qapp)

settings_page = SettingsPage(test_qapp, test_builder,
desktop_file_manager, dispatcher)
settings_page = SettingsPage(
test_qapp, test_builder, desktop_file_manager, dispatcher
)

for row in settings_page.app_list.get_children():
assert not row.app_info.vm