gitpane 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gitpane/__init__.py +0 -0
- gitpane/account/__init__.py +0 -0
- gitpane/account/account_choice.py +297 -0
- gitpane/account/address_owner.py +67 -0
- gitpane/account/authentication.py +1811 -0
- gitpane/account/authentication_worker.py +836 -0
- gitpane/account/credential_manager.py +480 -0
- gitpane/account/credential_manager_install.py +171 -0
- gitpane/account/keyring_reader.py +135 -0
- gitpane/account/keyring_state.py +158 -0
- gitpane/account/ssh.py +312 -0
- gitpane/account/ssh_askpass.py +216 -0
- gitpane/account/ssh_prompt.py +222 -0
- gitpane/application.py +684 -0
- gitpane/commit/__init__.py +0 -0
- gitpane/commit/commit_metadata.py +88 -0
- gitpane/commit/commit_push.py +276 -0
- gitpane/commit/commit_write.py +830 -0
- gitpane/config.py +1066 -0
- gitpane/controller/__init__.py +0 -0
- gitpane/controller/account_controller.py +677 -0
- gitpane/controller/clone_controller.py +776 -0
- gitpane/controller/close_readiness.py +212 -0
- gitpane/controller/commit_timing_controller.py +566 -0
- gitpane/controller/conflict_controller.py +885 -0
- gitpane/controller/console_registry.py +38 -0
- gitpane/controller/emoji_window_controller.py +95 -0
- gitpane/controller/file_action_controller.py +479 -0
- gitpane/controller/history_read_controller.py +671 -0
- gitpane/controller/history_rewrite_controller.py +739 -0
- gitpane/controller/navigation_controller.py +658 -0
- gitpane/controller/opening_controller.py +375 -0
- gitpane/controller/operation_policy.py +76 -0
- gitpane/controller/rebase_controller.py +797 -0
- gitpane/controller/rebase_editor_controller.py +309 -0
- gitpane/controller/reference_form_controller.py +1677 -0
- gitpane/controller/reference_read_controller.py +242 -0
- gitpane/controller/refresh_controller.py +477 -0
- gitpane/controller/replay_controller.py +805 -0
- gitpane/controller/session_registry.py +337 -0
- gitpane/controller/setting_controller.py +592 -0
- gitpane/controller/shutdown_controller.py +345 -0
- gitpane/controller/staging_controller.py +199 -0
- gitpane/controller/staging_session_registry.py +47 -0
- gitpane/controller/stash_controller.py +367 -0
- gitpane/controller/transport_controller.py +813 -0
- gitpane/controller/welcome_controller.py +102 -0
- gitpane/controller/window_activation.py +116 -0
- gitpane/controller/worktree_state_controller.py +169 -0
- gitpane/emoji/__init__.py +0 -0
- gitpane/emoji/emoji_catalogue.py +156 -0
- gitpane/git_config/__init__.py +0 -0
- gitpane/git_config/git_setting.py +441 -0
- gitpane/history/__init__.py +0 -0
- gitpane/history/avatar.py +241 -0
- gitpane/history/file_history.py +359 -0
- gitpane/history/graph.py +145 -0
- gitpane/history/history_filter.py +31 -0
- gitpane/history/history_parse.py +605 -0
- gitpane/history/repository.py +1600 -0
- gitpane/history/repository_value.py +341 -0
- gitpane/history/revision.py +180 -0
- gitpane/history/revision_file.py +182 -0
- gitpane/history_rewrite/__init__.py +0 -0
- gitpane/history_rewrite/graph.py +210 -0
- gitpane/history_rewrite/guard.py +288 -0
- gitpane/history_rewrite/index.py +125 -0
- gitpane/history_rewrite/prepare.py +378 -0
- gitpane/history_rewrite/protocol.py +320 -0
- gitpane/history_rewrite/publish.py +444 -0
- gitpane/history_rewrite/recovery.py +481 -0
- gitpane/history_rewrite/value.py +229 -0
- gitpane/operation/__init__.py +0 -0
- gitpane/operation/console_line.py +313 -0
- gitpane/operation/console_tone.py +76 -0
- gitpane/operation/diagnostic.py +954 -0
- gitpane/operation/launch_signature.py +254 -0
- gitpane/operation/operation_status.py +87 -0
- gitpane/operation/operation_trace.py +766 -0
- gitpane/operation/repository_operation.py +781 -0
- gitpane/preference/__init__.py +0 -0
- gitpane/preference/preference_location.py +40 -0
- gitpane/preference/preference_store.py +590 -0
- gitpane/process/__init__.py +0 -0
- gitpane/process/exact_arguments.py +77 -0
- gitpane/process/git_output_text.py +20 -0
- gitpane/process/process_group.py +851 -0
- gitpane/process/process_log.py +114 -0
- gitpane/process/process_runner.py +1033 -0
- gitpane/process/released_child_watch.py +138 -0
- gitpane/recent/__init__.py +0 -0
- gitpane/recent/mount_table.py +422 -0
- gitpane/recent/recent_availability.py +414 -0
- gitpane/recent/recent_name.py +11 -0
- gitpane/recent/recent_search.py +114 -0
- gitpane/reference/__init__.py +0 -0
- gitpane/reference/reference_edit.py +2030 -0
- gitpane/reference/reference_snapshot.py +196 -0
- gitpane/reference/stash.py +730 -0
- gitpane/reference/stash_list.py +164 -0
- gitpane/sequencer/__init__.py +0 -0
- gitpane/sequencer/commit_message_rule.py +154 -0
- gitpane/sequencer/conflict.py +506 -0
- gitpane/sequencer/merge_recovery.py +163 -0
- gitpane/sequencer/merge_state.py +166 -0
- gitpane/sequencer/mergetool.py +856 -0
- gitpane/sequencer/rebase.py +750 -0
- gitpane/sequencer/rebase_edit_file.py +224 -0
- gitpane/sequencer/rebase_state.py +523 -0
- gitpane/sequencer/replay.py +1435 -0
- gitpane/sequencer/replay_legacy.py +253 -0
- gitpane/sequencer/replay_state.py +747 -0
- gitpane/submodule/__init__.py +0 -0
- gitpane/submodule/checkout_refusal.py +102 -0
- gitpane/submodule/checkout_refusal_notice.py +111 -0
- gitpane/submodule/cloned_without_checkout.py +65 -0
- gitpane/submodule/submodule_discovery.py +1180 -0
- gitpane/submodule/submodule_loss.py +218 -0
- gitpane/system/__init__.py +0 -0
- gitpane/system/appearance_portal.py +125 -0
- gitpane/system/application_identity.py +48 -0
- gitpane/system/command_line.py +36 -0
- gitpane/system/desktop_icon_fact.py +128 -0
- gitpane/system/desktop_icon_support.py +270 -0
- gitpane/system/desktop_shortcut.py +536 -0
- gitpane/system/icns_image.py +35 -0
- gitpane/system/installed_launcher.py +136 -0
- gitpane/system/linux_distribution.py +151 -0
- gitpane/system/network_folder.py +46 -0
- gitpane/system/path_identity.py +95 -0
- gitpane/system/private_tree_removal.py +47 -0
- gitpane/system/process_program.py +63 -0
- gitpane/system/qt_folder.py +38 -0
- gitpane/system/ready_channel_watch.py +67 -0
- gitpane/system/startup_activation.py +65 -0
- gitpane/system/terminal_detach.py +384 -0
- gitpane/system/xdg_session.py +85 -0
- gitpane/text/__init__.py +0 -0
- gitpane/text/text_query.py +97 -0
- gitpane/text/typed_path.py +75 -0
- gitpane/text/typed_text.py +17 -0
- gitpane/thread/__init__.py +0 -0
- gitpane/thread/weak_signal.py +93 -0
- gitpane/tool/__init__.py +0 -0
- gitpane/tool/app_bundle.py +164 -0
- gitpane/tool/desktop_text_editor.py +156 -0
- gitpane/tool/difftool.py +429 -0
- gitpane/tool/editor.py +445 -0
- gitpane/tool/editor_bridge.py +193 -0
- gitpane/tool/editor_recipe.py +481 -0
- gitpane/tool/file_reveal.py +94 -0
- gitpane/tool/git_executable.py +264 -0
- gitpane/tool/git_version.py +62 -0
- gitpane/tool/kdiff3.py +41 -0
- gitpane/tool/kdiff3_install.py +162 -0
- gitpane/tool/launch_services_text_editor.py +104 -0
- gitpane/tool/mergetool_recipe.py +84 -0
- gitpane/tool/system_editor_default.py +80 -0
- gitpane/tool/system_text_editor.py +44 -0
- gitpane/tool/system_tool.py +816 -0
- gitpane/tool/terminal_recipe.py +390 -0
- gitpane/transport/__init__.py +0 -0
- gitpane/transport/branch_exchange.py +1690 -0
- gitpane/transport/clone.py +1273 -0
- gitpane/transport/credential_read_retry.py +35 -0
- gitpane/transport/git_refusal.py +207 -0
- gitpane/transport/push_tracking.py +121 -0
- gitpane/transport/refusal_notice.py +225 -0
- gitpane/transport/remote.py +747 -0
- gitpane/transport/remote_config.py +544 -0
- gitpane/ui/__init__.py +0 -0
- gitpane/ui/console/__init__.py +0 -0
- gitpane/ui/console/error_dialog.py +332 -0
- gitpane/ui/console/operation_dialog.py +408 -0
- gitpane/ui/console/operation_output.py +91 -0
- gitpane/ui/console/operation_output_view.py +373 -0
- gitpane/ui/emoji/__init__.py +0 -0
- gitpane/ui/emoji/emoji_font.py +257 -0
- gitpane/ui/emoji/emoji_grid.py +125 -0
- gitpane/ui/emoji/emoji_window.py +154 -0
- gitpane/ui/emoji/file_action_icon.py +131 -0
- gitpane/ui/emoji/opentype_family_name.py +182 -0
- gitpane/ui/history_rewrite/__init__.py +0 -0
- gitpane/ui/history_rewrite/details.py +78 -0
- gitpane/ui/history_rewrite/preview.py +565 -0
- gitpane/ui/history_rewrite/preview_model.py +291 -0
- gitpane/ui/history_rewrite/question.py +87 -0
- gitpane/ui/history_rewrite/recovery_dialog.py +262 -0
- gitpane/ui/look/__init__.py +0 -0
- gitpane/ui/look/action_icon.py +504 -0
- gitpane/ui/look/application_icon.py +78 -0
- gitpane/ui/look/application_style.py +317 -0
- gitpane/ui/look/avatar_label.py +53 -0
- gitpane/ui/look/color_contrast.py +43 -0
- gitpane/ui/look/desktop_appearance_follower.py +118 -0
- gitpane/ui/look/fixed_font.py +21 -0
- gitpane/ui/look/gtk_settings.py +116 -0
- gitpane/ui/look/icon_density.py +16 -0
- gitpane/ui/look/reference_font.py +19 -0
- gitpane/ui/look/theme_color.py +63 -0
- gitpane/ui/look/theme_look.py +95 -0
- gitpane/ui/look/theme_palette.py +173 -0
- gitpane/ui/look/warning_icon.py +86 -0
- gitpane/ui/look/windows_control_look.py +250 -0
- gitpane/ui/reader/__init__.py +0 -0
- gitpane/ui/reader/commit_message.py +129 -0
- gitpane/ui/reader/commit_panel.py +378 -0
- gitpane/ui/reader/diff_panel.py +626 -0
- gitpane/ui/reader/file_action.py +138 -0
- gitpane/ui/reader/file_tree_panel.py +235 -0
- gitpane/ui/reader/reader_toolbar.py +56 -0
- gitpane/ui/reader/revision_tree.py +80 -0
- gitpane/ui/reader/search_result.py +165 -0
- gitpane/ui/reader/source_text.py +134 -0
- gitpane/ui/reader/source_view.py +151 -0
- gitpane/ui/reader/syntax_span.py +67 -0
- gitpane/ui/reference/__init__.py +0 -0
- gitpane/ui/reference/branch_selector.py +106 -0
- gitpane/ui/reference/checkout_dialog.py +419 -0
- gitpane/ui/reference/checkout_revision_dialog.py +182 -0
- gitpane/ui/reference/commit_summary.py +339 -0
- gitpane/ui/reference/create_branch_dialog.py +346 -0
- gitpane/ui/reference/create_tag_dialog.py +283 -0
- gitpane/ui/reference/delete_local_branch_dialog.py +323 -0
- gitpane/ui/reference/delete_tag_dialog.py +394 -0
- gitpane/ui/reference/form_opening.py +53 -0
- gitpane/ui/reference/reference_dialog.py +417 -0
- gitpane/ui/reference/reference_feedback.py +62 -0
- gitpane/ui/reference/reference_form.py +139 -0
- gitpane/ui/reference/rename_branch_dialog.py +179 -0
- gitpane/ui/reference/reset_branch_dialog.py +430 -0
- gitpane/ui/reference/reset_dialog.py +263 -0
- gitpane/ui/reference/revision_dialog.py +121 -0
- gitpane/ui/reference/revision_picker.py +227 -0
- gitpane/ui/reference/select_branches_dialog.py +199 -0
- gitpane/ui/reference/stash_dialog.py +171 -0
- gitpane/ui/repository/__init__.py +0 -0
- gitpane/ui/repository/graph_delegate.py +298 -0
- gitpane/ui/repository/history_avatar.py +130 -0
- gitpane/ui/repository/history_model.py +179 -0
- gitpane/ui/repository/repository_list_popup.py +319 -0
- gitpane/ui/repository/repository_window.py +2580 -0
- gitpane/ui/resource/ArrowDown.png +0 -0
- gitpane/ui/resource/ArrowUp.png +0 -0
- gitpane/ui/resource/BranchCheckout.png +0 -0
- gitpane/ui/resource/BranchCreate.png +0 -0
- gitpane/ui/resource/BranchDelete.png +0 -0
- gitpane/ui/resource/CopyToClipboard.png +0 -0
- gitpane/ui/resource/GitPane-16.png +0 -0
- gitpane/ui/resource/GitPane-24.png +0 -0
- gitpane/ui/resource/GitPane-256.png +0 -0
- gitpane/ui/resource/GitPane-32.png +0 -0
- gitpane/ui/resource/GitPane-48.png +0 -0
- gitpane/ui/resource/GitPane-64.png +0 -0
- gitpane/ui/resource/GitPane.ico +0 -0
- gitpane/ui/resource/NotoColorEmoji-OFL.txt +92 -0
- gitpane/ui/resource/NotoColorEmoji_WindowsCompatible.ttf +0 -0
- gitpane/ui/resource/Renamed.png +0 -0
- gitpane/ui/resource/ResetAnotherBranchToHere.png +0 -0
- gitpane/ui/resource/ResetCurrentBranchToHere.png +0 -0
- gitpane/ui/resource/Stash.png +0 -0
- gitpane/ui/resource/Tag.png +0 -0
- gitpane/ui/resource/TagCreate.png +0 -0
- gitpane/ui/resource/TagDelete.png +0 -0
- gitpane/ui/resource/Unmerged.png +0 -0
- gitpane/ui/resource/checkout.png +0 -0
- gitpane/ui/sequencer/__init__.py +0 -0
- gitpane/ui/sequencer/conflict_dialog.py +532 -0
- gitpane/ui/sequencer/merge_recovery_dialog.py +67 -0
- gitpane/ui/sequencer/rebase_dialog.py +461 -0
- gitpane/ui/sequencer/rebase_editor_window.py +220 -0
- gitpane/ui/sequencer/replay_conflict_prompt.py +105 -0
- gitpane/ui/sequencer/replay_dialog.py +501 -0
- gitpane/ui/setting/__init__.py +0 -0
- gitpane/ui/setting/account_page.py +724 -0
- gitpane/ui/setting/add_account_dialog.py +1005 -0
- gitpane/ui/setting/credential_manager_dialog.py +151 -0
- gitpane/ui/setting/git_setting_widget.py +388 -0
- gitpane/ui/setting/setting_block.py +19 -0
- gitpane/ui/setting/setting_dialog.py +750 -0
- gitpane/ui/setting/ssh_widget.py +232 -0
- gitpane/ui/setting/utility_page.py +240 -0
- gitpane/ui/staging/__init__.py +0 -0
- gitpane/ui/staging/staging_action.py +356 -0
- gitpane/ui/staging/staging_window.py +745 -0
- gitpane/ui/transport/__init__.py +0 -0
- gitpane/ui/transport/clone_dialog.py +879 -0
- gitpane/ui/transport/push_dialog.py +580 -0
- gitpane/ui/transport/push_rejection_prompt.py +100 -0
- gitpane/ui/transport/push_tracking_question.py +88 -0
- gitpane/ui/transport/remote_dialog.py +943 -0
- gitpane/ui/welcome/__init__.py +0 -0
- gitpane/ui/welcome/invalid_repository_prompt.py +114 -0
- gitpane/ui/welcome/welcome_window.py +1137 -0
- gitpane/ui/widget/__init__.py +0 -0
- gitpane/ui/widget/busy_indicator.py +106 -0
- gitpane/ui/widget/commit_date_text.py +55 -0
- gitpane/ui/widget/content_growth.py +262 -0
- gitpane/ui/widget/content_measure.py +99 -0
- gitpane/ui/widget/dialog_layout.py +668 -0
- gitpane/ui/widget/drop_down_wheel_filter.py +32 -0
- gitpane/ui/widget/flow_layout.py +110 -0
- gitpane/ui/widget/long_text.py +542 -0
- gitpane/ui/widget/mnemonic.py +13 -0
- gitpane/ui/widget/numbered_text_edit.py +127 -0
- gitpane/ui/widget/plain_label.py +15 -0
- gitpane/ui/widget/plain_tooltip.py +19 -0
- gitpane/ui/widget/refresh_advice.py +25 -0
- gitpane/ui/widget/result_label.py +227 -0
- gitpane/ui/widget/result_status_bar.py +53 -0
- gitpane/ui/widget/scrolled_text.py +69 -0
- gitpane/ui/widget/shortcut_text.py +31 -0
- gitpane/ui/widget/standard_icon_label.py +55 -0
- gitpane/ui/widget/system_shortcut.py +75 -0
- gitpane/ui/widget/unavailable_reason.py +29 -0
- gitpane/ui/widget/wheel_safe_drop_down.py +24 -0
- gitpane/ui/window_control/__init__.py +0 -0
- gitpane/ui/window_control/button_focus.py +42 -0
- gitpane/ui/window_control/compositor_answer.py +152 -0
- gitpane/ui/window_control/compositor_growth_cap.py +73 -0
- gitpane/ui/window_control/desktop_activation.py +203 -0
- gitpane/ui/window_control/dialog_modality.py +122 -0
- gitpane/ui/window_control/item_activation.py +42 -0
- gitpane/ui/window_control/modal_focus_relay.py +124 -0
- gitpane/ui/window_control/wayland_activation.py +695 -0
- gitpane/ui/window_control/window_placement.py +305 -0
- gitpane/vendor/DISTLIB-LICENSE.txt +284 -0
- gitpane/vendor/w32.exe +0 -0
- gitpane/vendor/w64-arm.exe +0 -0
- gitpane/vendor/w64.exe +0 -0
- gitpane/worktree/__init__.py +0 -0
- gitpane/worktree/command_sequence.py +110 -0
- gitpane/worktree/file_action_target.py +61 -0
- gitpane/worktree/file_menu_action.py +52 -0
- gitpane/worktree/historical_restore.py +361 -0
- gitpane/worktree/ignore_rule.py +266 -0
- gitpane/worktree/mutation.py +1539 -0
- gitpane/worktree/partial_patch.py +435 -0
- gitpane/worktree/staged_reset.py +447 -0
- gitpane/worktree/staging.py +1418 -0
- gitpane/worktree/trash.py +302 -0
- gitpane/worktree/worktree_file.py +406 -0
- gitpane/worktree/worktree_service.py +584 -0
- gitpane/worktree/worktree_snapshot.py +630 -0
- gitpane-1.0.0.dist-info/METADATA +141 -0
- gitpane-1.0.0.dist-info/RECORD +353 -0
- gitpane-1.0.0.dist-info/WHEEL +5 -0
- gitpane-1.0.0.dist-info/entry_points.txt +2 -0
- gitpane-1.0.0.dist-info/licenses/LICENSE +21 -0
- gitpane-1.0.0.dist-info/licenses/THIRD_PARTY_NOTICES.md +109 -0
- gitpane-1.0.0.dist-info/licenses/src/gitpane/ui/resource/NotoColorEmoji-OFL.txt +92 -0
- gitpane-1.0.0.dist-info/licenses/src/gitpane/vendor/DISTLIB-LICENSE.txt +284 -0
- gitpane-1.0.0.dist-info/top_level.txt +1 -0
gitpane/application.py
ADDED
|
@@ -0,0 +1,684 @@
|
|
|
1
|
+
"""Wire the services of GitPane to its controllers, then run its event loop."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import functools
|
|
6
|
+
import os
|
|
7
|
+
import signal
|
|
8
|
+
import sys
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
from PySide6.QtCore import QObject, QSettings, QStandardPaths, QTimer
|
|
12
|
+
from PySide6.QtGui import QGuiApplication
|
|
13
|
+
from PySide6.QtWidgets import QApplication
|
|
14
|
+
|
|
15
|
+
from gitpane.account.authentication import AuthenticationService
|
|
16
|
+
from gitpane.account.credential_manager import (
|
|
17
|
+
CredentialManagerLocator,
|
|
18
|
+
ignore_line,
|
|
19
|
+
)
|
|
20
|
+
from gitpane.account.credential_manager_install import session_gcm_archive_architecture
|
|
21
|
+
from gitpane.account.keyring_reader import read_session_keyring_state
|
|
22
|
+
from gitpane.account.ssh import SshClientFinder, SshService
|
|
23
|
+
from gitpane.commit.commit_push import CommitPushService
|
|
24
|
+
from gitpane.config import (
|
|
25
|
+
APPLICATION_NAME,
|
|
26
|
+
APPLICATION_VERSION,
|
|
27
|
+
APPLICATION_VERSION_LINE,
|
|
28
|
+
SharedText,
|
|
29
|
+
)
|
|
30
|
+
from gitpane.controller.account_controller import AccountController
|
|
31
|
+
from gitpane.controller.clone_controller import CloneController
|
|
32
|
+
from gitpane.controller.close_readiness import CloseReadiness
|
|
33
|
+
from gitpane.controller.commit_timing_controller import CommitTimingController
|
|
34
|
+
from gitpane.controller.conflict_controller import ConflictController
|
|
35
|
+
from gitpane.controller.console_registry import ConsoleRegistry
|
|
36
|
+
from gitpane.controller.emoji_window_controller import EmojiWindowController
|
|
37
|
+
from gitpane.controller.file_action_controller import (
|
|
38
|
+
STAGING_FOLDER_TOOL,
|
|
39
|
+
STAGING_TEXT_EDITOR_TOOL,
|
|
40
|
+
FileActionController,
|
|
41
|
+
)
|
|
42
|
+
from gitpane.controller.history_read_controller import HistoryReadController
|
|
43
|
+
from gitpane.controller.history_rewrite_controller import HistoryRewriteController
|
|
44
|
+
from gitpane.controller.navigation_controller import (
|
|
45
|
+
NavigationController,
|
|
46
|
+
)
|
|
47
|
+
from gitpane.controller.opening_controller import OpeningController
|
|
48
|
+
from gitpane.controller.operation_policy import OperationPolicy
|
|
49
|
+
from gitpane.controller.rebase_controller import RebaseController
|
|
50
|
+
from gitpane.controller.rebase_editor_controller import RebaseEditorController
|
|
51
|
+
from gitpane.controller.reference_form_controller import (
|
|
52
|
+
ReferenceFormController,
|
|
53
|
+
)
|
|
54
|
+
from gitpane.controller.reference_read_controller import ReferenceReadController
|
|
55
|
+
from gitpane.controller.refresh_controller import RefreshController
|
|
56
|
+
from gitpane.controller.replay_controller import ReplayController
|
|
57
|
+
from gitpane.controller.session_registry import RepositorySession, SessionRegistry
|
|
58
|
+
from gitpane.controller.setting_controller import SettingController
|
|
59
|
+
from gitpane.controller.shutdown_controller import ShutdownController
|
|
60
|
+
from gitpane.controller.staging_controller import StagingController
|
|
61
|
+
from gitpane.controller.staging_session_registry import StagingSessionRegistry
|
|
62
|
+
from gitpane.controller.stash_controller import (
|
|
63
|
+
StashController,
|
|
64
|
+
)
|
|
65
|
+
from gitpane.controller.transport_controller import TransportController
|
|
66
|
+
from gitpane.controller.welcome_controller import WelcomeController
|
|
67
|
+
from gitpane.controller.window_activation import WindowActivationFilter
|
|
68
|
+
from gitpane.controller.worktree_state_controller import WorktreeStateController
|
|
69
|
+
from gitpane.git_config.git_setting import GitSettingService
|
|
70
|
+
from gitpane.history.avatar import AvatarService
|
|
71
|
+
from gitpane.history.file_history import FileHistoryAnchor, WindowCapability
|
|
72
|
+
from gitpane.history.repository import GitRepository
|
|
73
|
+
from gitpane.history.repository_value import (
|
|
74
|
+
RepositoryContext,
|
|
75
|
+
RequestIdentity,
|
|
76
|
+
)
|
|
77
|
+
from gitpane.history_rewrite.recovery import HISTORY_REWRITE_STORAGE_BASENAME
|
|
78
|
+
from gitpane.operation.diagnostic import DiagnosticLog
|
|
79
|
+
from gitpane.operation.repository_operation import (
|
|
80
|
+
RepositoryOperationService,
|
|
81
|
+
)
|
|
82
|
+
from gitpane.preference.preference_location import PREFERENCE_FILE_LOG_TEXT, user_preference_filename
|
|
83
|
+
from gitpane.preference.preference_store import (
|
|
84
|
+
PreferenceStore,
|
|
85
|
+
)
|
|
86
|
+
from gitpane.process.process_log import ProcessLogWriter
|
|
87
|
+
from gitpane.reference.reference_edit import (
|
|
88
|
+
BranchMode,
|
|
89
|
+
ReferenceService,
|
|
90
|
+
)
|
|
91
|
+
from gitpane.reference.stash import StashService
|
|
92
|
+
from gitpane.sequencer.conflict import (
|
|
93
|
+
ConflictService,
|
|
94
|
+
)
|
|
95
|
+
from gitpane.sequencer.mergetool import MergetoolService, TemporaryMergeStore
|
|
96
|
+
from gitpane.sequencer.rebase import RebaseService
|
|
97
|
+
from gitpane.sequencer.replay import ReplayService
|
|
98
|
+
from gitpane.submodule.submodule_discovery import SubmoduleService
|
|
99
|
+
from gitpane.system.application_identity import declare_application_identity
|
|
100
|
+
from gitpane.system.command_line import command_line_parser
|
|
101
|
+
from gitpane.system.desktop_shortcut import create_installed_desktop_shortcut
|
|
102
|
+
from gitpane.system.linux_distribution import session_distribution_family, session_distribution_release
|
|
103
|
+
from gitpane.system.qt_folder import writable_folder
|
|
104
|
+
from gitpane.system.ready_channel_watch import ReadyChannelWatch
|
|
105
|
+
from gitpane.system.terminal_detach import (
|
|
106
|
+
detached_child_command,
|
|
107
|
+
detaches_from_terminal,
|
|
108
|
+
interrupt_stops_gitpane,
|
|
109
|
+
relaunch_detached,
|
|
110
|
+
running_interpreter_options,
|
|
111
|
+
take_ready_channel,
|
|
112
|
+
)
|
|
113
|
+
from gitpane.tool.difftool import DifftoolService
|
|
114
|
+
from gitpane.tool.editor import EditorService
|
|
115
|
+
from gitpane.tool.git_executable import GitExecutableValidator, terminal_git_filename
|
|
116
|
+
from gitpane.tool.system_editor_default import SystemEditorDefault
|
|
117
|
+
from gitpane.tool.system_text_editor import session_system_text_editor_id
|
|
118
|
+
from gitpane.tool.system_tool import SystemToolService
|
|
119
|
+
from gitpane.tool.terminal_recipe import TerminalRegistry, detect_terminal
|
|
120
|
+
from gitpane.transport.branch_exchange import TransportService
|
|
121
|
+
from gitpane.transport.clone import CloneService
|
|
122
|
+
from gitpane.transport.remote import RemoteService
|
|
123
|
+
from gitpane.ui.look.application_icon import install_application_icon
|
|
124
|
+
from gitpane.ui.look.application_style import configure_system_style
|
|
125
|
+
from gitpane.ui.look.desktop_appearance_follower import install_desktop_appearance_follower
|
|
126
|
+
from gitpane.ui.look.theme_palette import install_light_field_palette
|
|
127
|
+
from gitpane.ui.repository.repository_window import RepositoryWindow
|
|
128
|
+
from gitpane.ui.staging.staging_window import StagingWindow
|
|
129
|
+
from gitpane.ui.widget.drop_down_wheel_filter import DropDownWheelFilter
|
|
130
|
+
from gitpane.ui.widget.refresh_advice import refresh_advice_text
|
|
131
|
+
from gitpane.ui.window_control.desktop_activation import desktop_activation
|
|
132
|
+
from gitpane.ui.window_control.modal_focus_relay import ModalFocusRelay, modal_window_activator
|
|
133
|
+
from gitpane.ui.window_control.window_placement import NewWindowPlacement
|
|
134
|
+
from gitpane.worktree.historical_restore import HistoricalRestoreService
|
|
135
|
+
from gitpane.worktree.mutation import MutationService
|
|
136
|
+
from gitpane.worktree.staging import StagingSession
|
|
137
|
+
from gitpane.worktree.worktree_service import WorktreeService
|
|
138
|
+
from gitpane.worktree.worktree_snapshot import WorktreeKey
|
|
139
|
+
|
|
140
|
+
if TYPE_CHECKING:
|
|
141
|
+
from collections.abc import Callable
|
|
142
|
+
|
|
143
|
+
from gitpane.sequencer.merge_recovery import MergeRecoveryStore
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class ApplicationWiring(QObject):
|
|
147
|
+
"""Build the services of GitPane, then connect them to its windows."""
|
|
148
|
+
|
|
149
|
+
def __init__(self, application: QApplication, preference: PreferenceStore, repository: GitRepository, diagnostic_log: DiagnosticLog | None = None,
|
|
150
|
+
avatar_service: AvatarService | None = None, system_tool: SystemToolService | None = None, terminal_registry: TerminalRegistry | None = None,
|
|
151
|
+
difftool: DifftoolService | None = None, merge_recovery: MergeRecoveryStore | None = None, rebase: RebaseService | None = None,
|
|
152
|
+
leave_event_loop: Callable[[], None] | None = None, system_editor_default: SystemEditorDefault | None = None) -> None:
|
|
153
|
+
"""Inject external services and keep window lifetimes explicit.
|
|
154
|
+
|
|
155
|
+
system_editor_default keeps the text editor the system names for plain text; by default it reads the session's
|
|
156
|
+
files of this system, only when asked, so building the wiring reads nothing, and writes an unexpected error of its read to the --log journal.
|
|
157
|
+
|
|
158
|
+
leave_event_loop ends the application once everything has drained. Only the caller that runs the event loop knows
|
|
159
|
+
that a loop runs to be left, so it gives QApplication.exit there; the default, QApplication.quit, does nothing
|
|
160
|
+
while no loop runs, which keeps a wiring driven without one, as in the tests, from stopping later loops.
|
|
161
|
+
"""
|
|
162
|
+
super().__init__(application)
|
|
163
|
+
self.session_registry_controller = SessionRegistry()
|
|
164
|
+
self.application = application
|
|
165
|
+
self.leave_event_loop = leave_event_loop if leave_event_loop is not None else application.quit
|
|
166
|
+
self.preference = preference
|
|
167
|
+
self.repository = repository
|
|
168
|
+
# The distribution is read once here and handed to everything that says how to install a tool on it.
|
|
169
|
+
distribution_family = session_distribution_family()
|
|
170
|
+
distribution_release = session_distribution_release()
|
|
171
|
+
self.difftool = difftool if difftool is not None else DifftoolService(repository, parent=self, activation=desktop_activation, distribution_family=distribution_family,
|
|
172
|
+
distribution_release=distribution_release)
|
|
173
|
+
self.worktree_service = WorktreeService(repository.runner, self)
|
|
174
|
+
self.editor = EditorService(self)
|
|
175
|
+
# The operation policy reads this service, so it is built after it: the lambdas read the policy when a write is submitted.
|
|
176
|
+
self.repository_operation = RepositoryOperationService(self.worktree_service, self, allows=lambda intent: self.operation_policy_controller.operation_allowed(intent),
|
|
177
|
+
explains=lambda intent: self.operation_policy_controller.operation_refusal(intent))
|
|
178
|
+
self.rebase = rebase if rebase is not None else RebaseService(self.worktree_service, self, repository_operation=self.repository_operation, editor=self.editor)
|
|
179
|
+
self.operation_policy_controller = OperationPolicy(session_registry_controller=self.session_registry_controller, repository=self.repository, repository_operation=self.repository_operation, rebase=self.rebase)
|
|
180
|
+
self.replay = ReplayService(self.worktree_service, self, repository_operation=self.repository_operation)
|
|
181
|
+
# Connected before the merge tool service is built: it connects itself to this signal while it is built, and Qt
|
|
182
|
+
# calls the slots of a signal in connection order, so these three keep running before its own. The rebase, replay
|
|
183
|
+
# and worktree state controllers are built after that service, so their slots are read by lambdas when the signal comes.
|
|
184
|
+
self.repository_operation.changed.connect(lambda: self.rebase_controller.rebase_actions_changed())
|
|
185
|
+
self.repository_operation.changed.connect(lambda: self.replay_controller.replay_actions_changed())
|
|
186
|
+
self.repository_operation.changed.connect(lambda: self.worktree_state_controller.merge_banner_actions_changed())
|
|
187
|
+
self.reference = ReferenceService(self.worktree_service, self.repository_operation, refresh_advice=refresh_advice_text)
|
|
188
|
+
self.stash = StashService(self.worktree_service, self.repository_operation)
|
|
189
|
+
self.remote = RemoteService(self.worktree_service, self.repository_operation)
|
|
190
|
+
self.mutation = MutationService(repository.runner, self.worktree_service, self, repository_operation=self.repository_operation)
|
|
191
|
+
self.historical_restore = HistoricalRestoreService(self.mutation, self)
|
|
192
|
+
self.git_setting_service = GitSettingService(repository.runner, self.mutation, self)
|
|
193
|
+
# The search of the credential manager writes its --log lines from the background task of each operation.
|
|
194
|
+
log_line = diagnostic_log.write_line if diagnostic_log is not None else ignore_line
|
|
195
|
+
credential_manager = CredentialManagerLocator(sys.platform, os.environ, log_line=log_line, distribution_family=distribution_family,
|
|
196
|
+
architecture=session_gcm_archive_architecture())
|
|
197
|
+
self.authentication = AuthenticationService(preference, self, credential_manager=credential_manager,
|
|
198
|
+
current_git_filename=lambda: terminal_git_filename(self.repository.git_filename))
|
|
199
|
+
self.ssh_client_finder = SshClientFinder(lambda: terminal_git_filename(self.repository.git_filename), sys.platform, os.environ, log_line=log_line)
|
|
200
|
+
self.ssh = SshService(preference, repository.runner, self, repository_operation=self.repository_operation, detect_client=self.ssh_client_finder.find)
|
|
201
|
+
self.transport = TransportService(self.remote, self.authentication, self.ssh, self.repository_operation, repository.runner,
|
|
202
|
+
self, is_current=self.operation_policy_controller.operation_allowed, log=diagnostic_log)
|
|
203
|
+
# Connected before the commit-and-push service is built: it connects itself to this signal while it is built, and
|
|
204
|
+
# Qt calls the slots of a signal in connection order, so these two keep running before its own. The end of a remote
|
|
205
|
+
# deletion comes first, as it did when the end of a transfer called it first; it ignores any other transfer, whose
|
|
206
|
+
# operation is never one a deletion form submitted. The reference form and transport controllers are built after
|
|
207
|
+
# that service, so their slots are read by lambdas when the signal comes.
|
|
208
|
+
self.transport.finished.connect(lambda outcome: self.reference_form_controller.delete_finished(outcome))
|
|
209
|
+
self.transport.finished.connect(lambda outcome: self.transport_controller.transport_finished(outcome))
|
|
210
|
+
self.commit_push = CommitPushService(self.transport, current_operation=self.operation_policy_controller.commit_push_operation, parent=self)
|
|
211
|
+
self.clone = CloneService(repository.runner, self.repository_operation, self.authentication, self.ssh, self, is_current=self.operation_policy_controller.clone_is_current, log=diagnostic_log)
|
|
212
|
+
self.staging_session_registry_controller = StagingSessionRegistry(repository=self.repository, worktree_service=self.worktree_service, rebase=self.rebase, mutation=self.mutation,
|
|
213
|
+
wire_staging_session=self.wire_staging_session, window_factory=StagingWindow, parent=self)
|
|
214
|
+
self.window_activation_controller = WindowActivationFilter(session_registry_controller=self.session_registry_controller, operation_policy_controller=self.operation_policy_controller,
|
|
215
|
+
staging_session_registry_controller=self.staging_session_registry_controller, application=self.application,
|
|
216
|
+
preference=self.preference, parent=self)
|
|
217
|
+
self.console_registry_controller = ConsoleRegistry()
|
|
218
|
+
self.conflict = ConflictService()
|
|
219
|
+
self.mergetool = MergetoolService(self.conflict, self.mutation, self, temporary_store=TemporaryMergeStore(merge_recovery), activation=desktop_activation,
|
|
220
|
+
distribution_family=distribution_family, distribution_release=distribution_release)
|
|
221
|
+
self.diagnostic_log = diagnostic_log
|
|
222
|
+
self.reference_read_controller = ReferenceReadController(session_registry_controller=self.session_registry_controller, operation_policy_controller=self.operation_policy_controller, repository=self.repository, reference=self.reference,
|
|
223
|
+
diagnostic_log=self.diagnostic_log)
|
|
224
|
+
self.process_log_writer: ProcessLogWriter | None = None
|
|
225
|
+
# The condition is on the open file, not on the option: without a log to write into, no subscriber must be
|
|
226
|
+
# added to the path every single process goes through. The attribute keeps the writer alive with this owner.
|
|
227
|
+
if diagnostic_log is not None and diagnostic_log.stream is not None:
|
|
228
|
+
self.process_log_writer = ProcessLogWriter(diagnostic_log, [repository.runner, self.authentication.runner])
|
|
229
|
+
self.avatar_service = avatar_service if avatar_service is not None else AvatarService(parent=self)
|
|
230
|
+
self.system_tool = system_tool if system_tool is not None else SystemToolService(parent=self, activation=desktop_activation)
|
|
231
|
+
# Git Bash is searched from the Git and the utilities folder GitPane uses, both read at each search.
|
|
232
|
+
self.terminal_registry = terminal_registry if terminal_registry is not None else TerminalRegistry(
|
|
233
|
+
preference, lambda: detect_terminal(git_filename=terminal_git_filename(self.repository.git_filename), git_utility_dir=str(self.preference.value_by_key["git_utility_dir"])),
|
|
234
|
+
parent=self)
|
|
235
|
+
self.validator = GitExecutableValidator(repository.runner)
|
|
236
|
+
self.submodule = SubmoduleService(self.worktree_service, self.repository_operation, self.authentication, self.ssh, diagnostic_log)
|
|
237
|
+
self.refresh_controller = RefreshController(session_registry_controller=self.session_registry_controller, operation_policy_controller=self.operation_policy_controller,
|
|
238
|
+
staging_session_registry_controller=self.staging_session_registry_controller, reference_read_controller=self.reference_read_controller, repository=self.repository, submodule=self.submodule,
|
|
239
|
+
parent=self)
|
|
240
|
+
self.history_rewrite_controller = HistoryRewriteController(session_registry_controller=self.session_registry_controller, operation_policy_controller=self.operation_policy_controller,
|
|
241
|
+
reference_read_controller=self.reference_read_controller, console_registry_controller=self.console_registry_controller,
|
|
242
|
+
refresh_controller=self.refresh_controller, repository=self.repository, worktree_service=self.worktree_service,
|
|
243
|
+
repository_operation=self.repository_operation,
|
|
244
|
+
storage_root_dir=os.path.join(writable_folder(QStandardPaths.StandardLocation.AppLocalDataLocation, sys.platform), HISTORY_REWRITE_STORAGE_BASENAME),
|
|
245
|
+
preference=self.preference, diagnostic_log=self.diagnostic_log, parent=self)
|
|
246
|
+
self.worktree_state_controller = WorktreeStateController(session_registry_controller=self.session_registry_controller, staging_session_registry_controller=self.staging_session_registry_controller,
|
|
247
|
+
reference_read_controller=self.reference_read_controller, refresh_controller=self.refresh_controller, repository_operation=self.repository_operation, conflict=self.conflict,
|
|
248
|
+
mergetool=self.mergetool, parent=self)
|
|
249
|
+
self.history_read_controller = HistoryReadController(session_registry_controller=self.session_registry_controller, operation_policy_controller=self.operation_policy_controller, reference_read_controller=self.reference_read_controller,
|
|
250
|
+
refresh_controller=self.refresh_controller, preference=self.preference, repository=self.repository, clone=self.clone, diagnostic_log=self.diagnostic_log, system_tool=self.system_tool,
|
|
251
|
+
parent=self)
|
|
252
|
+
self.commit_timing_controller = CommitTimingController(session_registry_controller=self.session_registry_controller, staging_session_registry_controller=self.staging_session_registry_controller,
|
|
253
|
+
worktree_state_controller=self.worktree_state_controller, history_read_controller=self.history_read_controller, refresh_controller=self.refresh_controller,
|
|
254
|
+
reference_read_controller=self.reference_read_controller, repository=self.repository, authentication=self.authentication,
|
|
255
|
+
worktree_service=self.worktree_service, repository_operation=self.repository_operation, diagnostic_log=self.diagnostic_log, parent=self)
|
|
256
|
+
self.system_editor_default = system_editor_default if system_editor_default is not None else SystemEditorDefault(session_system_text_editor_id, parent=self, log_line=log_line)
|
|
257
|
+
self.setting_controller = SettingController(session_registry_controller=self.session_registry_controller, operation_policy_controller=self.operation_policy_controller,
|
|
258
|
+
staging_session_registry_controller=self.staging_session_registry_controller, window_activation_controller=self.window_activation_controller, history_read_controller=self.history_read_controller,
|
|
259
|
+
preference=self.preference, repository=self.repository, git_setting_service=self.git_setting_service, ssh=self.ssh, mergetool=self.mergetool, diagnostic_log=self.diagnostic_log,
|
|
260
|
+
terminal_registry=self.terminal_registry, validator=self.validator, system_editor_default=self.system_editor_default,
|
|
261
|
+
distribution_family=distribution_family, distribution_release=distribution_release,
|
|
262
|
+
desktop_shortcut=functools.partial(create_installed_desktop_shortcut, application_module_name=application_module_name()), parent=self)
|
|
263
|
+
self.account_controller = AccountController(session_registry_controller=self.session_registry_controller, operation_policy_controller=self.operation_policy_controller, setting_controller=self.setting_controller, repository=self.repository,
|
|
264
|
+
worktree_service=self.worktree_service, remote=self.remote, authentication=self.authentication, diagnostic_log=self.diagnostic_log,
|
|
265
|
+
read_keyring=read_session_keyring_state)
|
|
266
|
+
self.clone_controller = CloneController(session_registry_controller=self.session_registry_controller, operation_policy_controller=self.operation_policy_controller, account_controller=self.account_controller, application=self.application,
|
|
267
|
+
preference=self.preference, repository=self.repository, authentication=self.authentication, clone=self.clone, diagnostic_log=self.diagnostic_log, parent=self)
|
|
268
|
+
self.opening_controller = OpeningController(session_registry_controller=self.session_registry_controller, operation_policy_controller=self.operation_policy_controller,
|
|
269
|
+
staging_session_registry_controller=self.staging_session_registry_controller, window_activation_controller=self.window_activation_controller,
|
|
270
|
+
reference_read_controller=self.reference_read_controller, refresh_controller=self.refresh_controller, history_read_controller=self.history_read_controller,
|
|
271
|
+
setting_controller=self.setting_controller, clone_controller=self.clone_controller, preference=self.preference, repository=self.repository, authentication=self.authentication, clone=self.clone,
|
|
272
|
+
diagnostic_log=self.diagnostic_log, wire_repository_window=self.wire_repository_window, parent=self)
|
|
273
|
+
self.welcome_controller = WelcomeController(session_registry_controller=self.session_registry_controller, operation_policy_controller=self.operation_policy_controller, window_activation_controller=self.window_activation_controller,
|
|
274
|
+
setting_controller=self.setting_controller, clone_controller=self.clone_controller, opening_controller=self.opening_controller, preference=self.preference, repository=self.repository,
|
|
275
|
+
diagnostic_log=self.diagnostic_log)
|
|
276
|
+
self.emoji_window_controller = EmojiWindowController(session_registry_controller=self.session_registry_controller, operation_policy_controller=self.operation_policy_controller, application=self.application, preference=self.preference,
|
|
277
|
+
diagnostic_log=self.diagnostic_log)
|
|
278
|
+
self.file_action_controller = FileActionController(session_registry_controller=self.session_registry_controller, operation_policy_controller=self.operation_policy_controller,
|
|
279
|
+
staging_session_registry_controller=self.staging_session_registry_controller, refresh_controller=self.refresh_controller, history_read_controller=self.history_read_controller,
|
|
280
|
+
opening_controller=self.opening_controller, preference=self.preference, repository=self.repository, difftool=self.difftool, worktree_service=self.worktree_service,
|
|
281
|
+
historical_restore=self.historical_restore, diagnostic_log=self.diagnostic_log, system_tool=self.system_tool,
|
|
282
|
+
system_editor_default=self.system_editor_default)
|
|
283
|
+
self.staging_controller = StagingController(session_registry_controller=self.session_registry_controller, operation_policy_controller=self.operation_policy_controller,
|
|
284
|
+
staging_session_registry_controller=self.staging_session_registry_controller, window_activation_controller=self.window_activation_controller, refresh_controller=self.refresh_controller,
|
|
285
|
+
worktree_state_controller=self.worktree_state_controller, history_read_controller=self.history_read_controller, opening_controller=self.opening_controller,
|
|
286
|
+
emoji_window_controller=self.emoji_window_controller, file_action_controller=self.file_action_controller, preference=self.preference, repository=self.repository, diagnostic_log=self.diagnostic_log)
|
|
287
|
+
self.transport_controller = TransportController(session_registry_controller=self.session_registry_controller, operation_policy_controller=self.operation_policy_controller,
|
|
288
|
+
staging_session_registry_controller=self.staging_session_registry_controller, console_registry_controller=self.console_registry_controller,
|
|
289
|
+
reference_read_controller=self.reference_read_controller, refresh_controller=self.refresh_controller, account_controller=self.account_controller, clone_controller=self.clone_controller,
|
|
290
|
+
preference=self.preference, repository=self.repository, worktree_service=self.worktree_service, repository_operation=self.repository_operation, remote=self.remote,
|
|
291
|
+
authentication=self.authentication, transport=self.transport, commit_push=self.commit_push, diagnostic_log=self.diagnostic_log, parent=self)
|
|
292
|
+
self.reference_form_controller = ReferenceFormController(session_registry_controller=self.session_registry_controller, operation_policy_controller=self.operation_policy_controller,
|
|
293
|
+
console_registry_controller=self.console_registry_controller, reference_read_controller=self.reference_read_controller, refresh_controller=self.refresh_controller,
|
|
294
|
+
history_read_controller=self.history_read_controller, staging_controller=self.staging_controller, transport_controller=self.transport_controller, preference=self.preference,
|
|
295
|
+
repository=self.repository, worktree_service=self.worktree_service, reference=self.reference, remote=self.remote, transport=self.transport, diagnostic_log=self.diagnostic_log)
|
|
296
|
+
self.replay_controller = ReplayController(session_registry_controller=self.session_registry_controller, operation_policy_controller=self.operation_policy_controller,
|
|
297
|
+
staging_session_registry_controller=self.staging_session_registry_controller, console_registry_controller=self.console_registry_controller, reference_read_controller=self.reference_read_controller,
|
|
298
|
+
refresh_controller=self.refresh_controller, worktree_state_controller=self.worktree_state_controller, history_read_controller=self.history_read_controller,
|
|
299
|
+
reference_form_controller=self.reference_form_controller, repository=self.repository, repository_operation=self.repository_operation, replay=self.replay, mergetool=self.mergetool,
|
|
300
|
+
diagnostic_log=self.diagnostic_log, parent=self)
|
|
301
|
+
self.rebase_editor_controller = RebaseEditorController(editor=self.editor, worktree_service=self.worktree_service, parent=self)
|
|
302
|
+
self.rebase_controller = RebaseController(session_registry_controller=self.session_registry_controller, operation_policy_controller=self.operation_policy_controller,
|
|
303
|
+
staging_session_registry_controller=self.staging_session_registry_controller, window_activation_controller=self.window_activation_controller,
|
|
304
|
+
console_registry_controller=self.console_registry_controller, reference_read_controller=self.reference_read_controller, refresh_controller=self.refresh_controller,
|
|
305
|
+
worktree_state_controller=self.worktree_state_controller, history_read_controller=self.history_read_controller, staging_controller=self.staging_controller,
|
|
306
|
+
replay_controller=self.replay_controller, rebase_editor_controller=self.rebase_editor_controller, preference=self.preference, repository=self.repository,
|
|
307
|
+
worktree_service=self.worktree_service, repository_operation=self.repository_operation, rebase=self.rebase, mergetool=self.mergetool, diagnostic_log=self.diagnostic_log, parent=self)
|
|
308
|
+
self.stash_controller = StashController(session_registry_controller=self.session_registry_controller, operation_policy_controller=self.operation_policy_controller, staging_session_registry_controller=self.staging_session_registry_controller,
|
|
309
|
+
window_activation_controller=self.window_activation_controller, refresh_controller=self.refresh_controller,
|
|
310
|
+
replay_controller=self.replay_controller, preference=self.preference, repository=self.repository, worktree_service=self.worktree_service, stash=self.stash, diagnostic_log=self.diagnostic_log,
|
|
311
|
+
parent=self)
|
|
312
|
+
self.conflict_controller = ConflictController(session_registry_controller=self.session_registry_controller, operation_policy_controller=self.operation_policy_controller,
|
|
313
|
+
staging_session_registry_controller=self.staging_session_registry_controller, window_activation_controller=self.window_activation_controller, refresh_controller=self.refresh_controller,
|
|
314
|
+
worktree_state_controller=self.worktree_state_controller, setting_controller=self.setting_controller,
|
|
315
|
+
reference_form_controller=self.reference_form_controller, replay_controller=self.replay_controller, rebase_controller=self.rebase_controller, stash_controller=self.stash_controller,
|
|
316
|
+
preference=self.preference, repository=self.repository, worktree_service=self.worktree_service, repository_operation=self.repository_operation, rebase=self.rebase, reference=self.reference,
|
|
317
|
+
stash=self.stash, mutation=self.mutation, conflict=self.conflict, mergetool=self.mergetool, diagnostic_log=self.diagnostic_log, parent=self)
|
|
318
|
+
self.close_readiness_controller = CloseReadiness(session_registry_controller=self.session_registry_controller, console_registry_controller=self.console_registry_controller, reference_form_controller=self.reference_form_controller,
|
|
319
|
+
replay_controller=self.replay_controller, rebase_controller=self.rebase_controller, rebase_editor_controller=self.rebase_editor_controller, editor=self.editor,
|
|
320
|
+
repository_operation=self.repository_operation, rebase=self.rebase, replay=self.replay,
|
|
321
|
+
mutation=self.mutation, mergetool=self.mergetool, parent=self)
|
|
322
|
+
self.navigation_controller = NavigationController(session_registry_controller=self.session_registry_controller, operation_policy_controller=self.operation_policy_controller,
|
|
323
|
+
staging_session_registry_controller=self.staging_session_registry_controller, reference_read_controller=self.reference_read_controller, refresh_controller=self.refresh_controller,
|
|
324
|
+
history_read_controller=self.history_read_controller, account_controller=self.account_controller, opening_controller=self.opening_controller, transport_controller=self.transport_controller,
|
|
325
|
+
replay_controller=self.replay_controller, rebase_controller=self.rebase_controller, close_readiness_controller=self.close_readiness_controller, preference=self.preference,
|
|
326
|
+
repository=self.repository, reference=self.reference, authentication=self.authentication, diagnostic_log=self.diagnostic_log, submodule=self.submodule, parent=self)
|
|
327
|
+
self.shutdown_controller = ShutdownController(session_registry_controller=self.session_registry_controller, staging_session_registry_controller=self.staging_session_registry_controller,
|
|
328
|
+
window_activation_controller=self.window_activation_controller, reference_read_controller=self.reference_read_controller, refresh_controller=self.refresh_controller,
|
|
329
|
+
history_read_controller=self.history_read_controller, account_controller=self.account_controller,
|
|
330
|
+
emoji_window_controller=self.emoji_window_controller, transport_controller=self.transport_controller, reference_form_controller=self.reference_form_controller,
|
|
331
|
+
replay_controller=self.replay_controller, rebase_controller=self.rebase_controller, stash_controller=self.stash_controller, conflict_controller=self.conflict_controller,
|
|
332
|
+
close_readiness_controller=self.close_readiness_controller, navigation_controller=self.navigation_controller, application=self.application,
|
|
333
|
+
leave_event_loop=self.leave_event_loop, repository=self.repository,
|
|
334
|
+
difftool=self.difftool, worktree_service=self.worktree_service, editor=self.editor, repository_operation=self.repository_operation, rebase=self.rebase, replay=self.replay, mutation=self.mutation,
|
|
335
|
+
authentication=self.authentication, ssh=self.ssh, transport=self.transport, clone=self.clone, mergetool=self.mergetool, system_tool=self.system_tool, submodule=self.submodule, parent=self)
|
|
336
|
+
# Everything above is built before anything is connected, and the connections below keep the order they had: Qt calls
|
|
337
|
+
# the slots of a signal in connection order, and every object a connection reaches must already exist. The few
|
|
338
|
+
# connections made above stay ahead of a slot that a service adds to the same signal while it is built.
|
|
339
|
+
self.rebase.changed.connect(self.worktree_state_controller.relay_rebase_state)
|
|
340
|
+
self.rebase.changed.connect(self.rebase_controller.follow_rebase_ended_outside)
|
|
341
|
+
self.repository_operation.finished.connect(self.replay_controller.replay_finished)
|
|
342
|
+
self.replay.changed.connect(self.worktree_state_controller.relay_replay_state)
|
|
343
|
+
self.repository_operation.finished.connect(self.stash_controller.stash_finished)
|
|
344
|
+
self.repository_operation.finished.connect(self.stash_controller.stash_pop_finished)
|
|
345
|
+
self.repository_operation.finished.connect(self.stash_controller.stash_drop_finished)
|
|
346
|
+
self.repository_operation.finished.connect(self.reference_form_controller.reset_finished)
|
|
347
|
+
self.repository_operation.finished.connect(self.reference_form_controller.checkout_finished)
|
|
348
|
+
self.repository_operation.finished.connect(self.reference_form_controller.branch_finished)
|
|
349
|
+
self.repository_operation.finished.connect(self.reference_form_controller.delete_finished)
|
|
350
|
+
self.repository_operation.finished.connect(self.reference_form_controller.branch_deletion_finished)
|
|
351
|
+
self.repository_operation.finished.connect(self.reference_form_controller.tag_finished)
|
|
352
|
+
self.repository_operation.finished.connect(self.conflict_controller.merge_abort_finished)
|
|
353
|
+
self.mutation.finished.connect(self.file_action_controller.historical_restore_finished)
|
|
354
|
+
self.repository_operation.finished.connect(self.history_rewrite_controller.operation_finished)
|
|
355
|
+
self.history_rewrite_controller.recovery_requested.connect(self.history_rewrite_controller.present_recovery)
|
|
356
|
+
self.opening_controller.pause_observation_requested.connect(self.history_rewrite_controller.check_pending_recovery)
|
|
357
|
+
self.refresh_controller.pause_observation_requested.connect(lambda session_id, *_: self.history_rewrite_controller.check_pending_recovery(session_id))
|
|
358
|
+
self.refresh_controller.refresh_started.connect(self.history_rewrite_controller.observe_refresh)
|
|
359
|
+
self.worktree_service.idle.connect(self.history_rewrite_controller.readers_changed)
|
|
360
|
+
self.repository.runner.idle.connect(self.history_rewrite_controller.readers_changed)
|
|
361
|
+
self.git_setting_service.changed.connect(self.staging_controller.git_setting_changed)
|
|
362
|
+
self.commit_push.push_started.connect(self.transport_controller.commit_push_started)
|
|
363
|
+
self.commit_push.tracking_decision_requested.connect(self.transport_controller.commit_push_decide_tracking)
|
|
364
|
+
self.commit_push.choose_requested.connect(self.transport_controller.commit_push_choose)
|
|
365
|
+
self.application.aboutToQuit.connect(self.transport.close)
|
|
366
|
+
self.clone.finished.connect(self.clone_controller.clone_finished)
|
|
367
|
+
self.ssh.question_requested.connect(self.account_controller.show_ssh_question)
|
|
368
|
+
self.ssh.question_closed.connect(self.account_controller.close_ssh_question)
|
|
369
|
+
self.application.aboutToQuit.connect(self.ssh.close)
|
|
370
|
+
self.authentication.idle.connect(self.shutdown_controller.maybe_quit)
|
|
371
|
+
self.application.aboutToQuit.connect(self.authentication.close)
|
|
372
|
+
self.mutation.finished.connect(self.staging_controller.log_mutation_result)
|
|
373
|
+
self.mergetool.finished.connect(self.conflict_controller.mergetool_finished)
|
|
374
|
+
self.mergetool.series_changed.connect(self.conflict_controller.mergetool_series_changed)
|
|
375
|
+
self.mergetool.series_finished.connect(self.conflict_controller.mergetool_series_finished)
|
|
376
|
+
self.mergetool.confirmation_requested.connect(self.conflict_controller.confirm_mergetool)
|
|
377
|
+
self.mergetool.confirmation_closed.connect(self.conflict_controller.close_merge_confirmation)
|
|
378
|
+
self.mergetool.idle.connect(self.shutdown_controller.maybe_quit)
|
|
379
|
+
self.application.aboutToQuit.connect(self.mergetool.close)
|
|
380
|
+
self.mutation.finished.connect(self.conflict_controller.conflict_resolution_finished)
|
|
381
|
+
# A drop waiting for the merge tool or another write to release the repository starts as soon as it is free.
|
|
382
|
+
self.repository_operation.changed.connect(self.stash_controller.continue_stash_drops_after_resolution)
|
|
383
|
+
self.difftool.failed.connect(self.file_action_controller.difftool_failed)
|
|
384
|
+
self.difftool.started.connect(self.file_action_controller.difftool_started)
|
|
385
|
+
self.difftool.tool_exited.connect(self.file_action_controller.difftool_closed)
|
|
386
|
+
self.application.aboutToQuit.connect(self.difftool.close)
|
|
387
|
+
self.application.aboutToQuit.connect(self.avatar_service.close)
|
|
388
|
+
self.repository_operation.finished.connect(self.rebase_controller.rebase_finished)
|
|
389
|
+
self.editor.waiting.connect(self.rebase_controller.rebase_editor_waiting)
|
|
390
|
+
# Only a wait reads the native list again, so a reword shows the edited instructions; closing the editor
|
|
391
|
+
# changes the progress text alone, and the command's own result follows.
|
|
392
|
+
self.editor.finished.connect(lambda result: self.rebase_controller.rebase_progress(result.request.operation_id, SharedText.REBASE_APPLYING_COMMITS.value))
|
|
393
|
+
# A request whose bridge was lost ends without an answer, and Git goes on to its own result the same way.
|
|
394
|
+
self.editor.edit_cancelled.connect(lambda request: self.rebase_controller.rebase_progress(request.operation_id, SharedText.REBASE_APPLYING_COMMITS.value))
|
|
395
|
+
self.editor.idle.connect(self.shutdown_controller.maybe_quit)
|
|
396
|
+
self.system_tool.started.connect(self.file_action_controller.tool_started)
|
|
397
|
+
self.system_tool.finished.connect(self.file_action_controller.tool_finished)
|
|
398
|
+
self.system_tool.export_finished.connect(self.file_action_controller.export_finished)
|
|
399
|
+
self.application.aboutToQuit.connect(self.system_tool.close)
|
|
400
|
+
self.validator.validated.connect(self.setting_controller.git_validated)
|
|
401
|
+
self.validator.tools_folder_checked.connect(self.setting_controller.git_tools_folder_checked)
|
|
402
|
+
# Settings applies a change first, then an open accounts page and the clone forms show the accounts it touched, as before.
|
|
403
|
+
self.preference.changed.connect(self.setting_controller.preference_changed)
|
|
404
|
+
self.preference.changed.connect(self.account_controller.preference_changed_for_accounts)
|
|
405
|
+
self.preference.changed.connect(self.clone_controller.preference_changed_for_clone_forms)
|
|
406
|
+
# A read a navigation owns goes to its own slot, connected first; the opening's and the history read's slots that follow ignore it.
|
|
407
|
+
self.repository.opened.connect(self.navigation_controller.navigation_opened)
|
|
408
|
+
self.repository.opened.connect(self.opening_controller.opened)
|
|
409
|
+
self.repository.failed.connect(self.navigation_controller.navigation_read_failed)
|
|
410
|
+
self.repository.failed.connect(self.history_read_controller.failed)
|
|
411
|
+
self.repository.snapshot_read.connect(self.navigation_controller.navigation_snapshot_read)
|
|
412
|
+
self.repository.snapshot_read.connect(self.history_read_controller.snapshot_read)
|
|
413
|
+
self.repository.commit_batch_read.connect(self.navigation_controller.navigation_commit_batch_read)
|
|
414
|
+
self.repository.commit_batch_read.connect(self.history_read_controller.commit_batch_read)
|
|
415
|
+
self.repository.commit_read.connect(self.history_read_controller.commit_read)
|
|
416
|
+
self.repository.matching_read.connect(self.navigation_controller.navigation_matching_read)
|
|
417
|
+
self.repository.matching_read.connect(self.history_read_controller.matching_read)
|
|
418
|
+
self.repository.relation_read.connect(self.history_read_controller.relation_read)
|
|
419
|
+
self.repository.changes_read.connect(self.history_read_controller.changes_read)
|
|
420
|
+
self.repository.diff_read.connect(self.history_read_controller.diff_read)
|
|
421
|
+
self.repository.tree_read.connect(self.history_read_controller.tree_read)
|
|
422
|
+
self.repository.blob_read.connect(self.history_read_controller.blob_read)
|
|
423
|
+
self.repository.file_failed.connect(self.history_read_controller.file_failed)
|
|
424
|
+
self.application.aboutToQuit.connect(self.repository.close)
|
|
425
|
+
self.close_readiness_controller.close_timer.timeout.connect(self.shutdown_controller.resume_pending_close)
|
|
426
|
+
self.repository.runner.idle.connect(self.shutdown_controller.maybe_quit)
|
|
427
|
+
self.repository_operation.idle.connect(self.shutdown_controller.maybe_quit)
|
|
428
|
+
self.system_tool.export_idle.connect(self.shutdown_controller.maybe_quit)
|
|
429
|
+
self.difftool.idle.connect(self.shutdown_controller.maybe_quit)
|
|
430
|
+
self.mutation.finished.connect(self.shutdown_controller.maybe_quit)
|
|
431
|
+
self.worktree_service.idle.connect(self.shutdown_controller.maybe_quit)
|
|
432
|
+
if diagnostic_log is not None:
|
|
433
|
+
self.application.aboutToQuit.connect(diagnostic_log.close)
|
|
434
|
+
# A controller tells a higher one through a signal connected here. The lambda reads the slot when the signal comes,
|
|
435
|
+
# so a slot replaced on its owner after construction is the one called, and passes the options it only takes by name.
|
|
436
|
+
self.refresh_controller.pause_observation_requested.connect(
|
|
437
|
+
lambda session_id, replay_publication, rebase_publication: self.rebase_controller.observe_rebase(session_id, open_paused=True, replay_publication=replay_publication,
|
|
438
|
+
rebase_publication=rebase_publication))
|
|
439
|
+
self.worktree_state_controller.automatic_observation_requested.connect(lambda session_id: self.rebase_controller.observe_rebase(session_id, open_paused=True, automatic=True))
|
|
440
|
+
self.worktree_state_controller.window_conflict_state_shown.connect(lambda session_id, key, conflict_state: self.conflict_controller.show_window_conflict_state(session_id, key, conflict_state))
|
|
441
|
+
# The stash kept for its conflicts is settled before the conflicts a Stash pop left are read again, as before.
|
|
442
|
+
self.worktree_state_controller.worktree_conflict_state_read.connect(lambda key: self.stash_controller.continue_stash_drop_after_resolution(key))
|
|
443
|
+
self.worktree_state_controller.worktree_conflict_state_read.connect(lambda key: self.conflict_controller.read_conflict_retention(key))
|
|
444
|
+
# A published history ends the replay refresh waiting for it before a finished rebase shows its final commit, as before.
|
|
445
|
+
self.history_read_controller.history_published.connect(lambda session_id, generation: self.replay_controller.finish_replay_refresh(session_id, published_generation=generation))
|
|
446
|
+
self.history_read_controller.history_published.connect(lambda session_id, generation: self.history_rewrite_controller.readers_changed())
|
|
447
|
+
self.history_read_controller.history_published.connect(lambda session_id, generation: self.rebase_controller.reveal_rebase_result(session_id))
|
|
448
|
+
self.history_read_controller.history_status_written.connect(lambda session_id, snapshot: self.rebase_controller.show_rebase_end_notice(session_id, snapshot, after_history=True))
|
|
449
|
+
self.history_read_controller.refresh_failed.connect(lambda session_id, diagnostic: self.replay_controller.finish_replay_refresh(session_id, diagnostic))
|
|
450
|
+
self.history_read_controller.refresh_failed.connect(lambda session_id, diagnostic: self.rebase_controller.forget_rebase_end_notice(session_id))
|
|
451
|
+
self.setting_controller.setting_opened.connect(
|
|
452
|
+
lambda dialog, account_page, session_id, context: self.account_controller.bind_setting_account_page(dialog, account_page, session_id, context))
|
|
453
|
+
self.setting_controller.git_missing.connect(lambda: self.welcome_controller.show_missing_git())
|
|
454
|
+
self.setting_controller.waiting_opening_released.connect(lambda waiting: self.opening_controller.resume_waiting_opening(waiting))
|
|
455
|
+
self.clone_controller.open_requested.connect(lambda session_id, repository_dir: self.opening_controller.open(session_id, repository_dir))
|
|
456
|
+
self.clone_controller.open_cancel_requested.connect(lambda session_id: self.opening_controller.cancel_open(session_id))
|
|
457
|
+
self.opening_controller.pause_observation_requested.connect(lambda session_id: self.rebase_controller.observe_rebase(session_id, open_paused=True))
|
|
458
|
+
self.replay_controller.conflict_write_availability_changed.connect(lambda session_id, locked: self.conflict_controller.show_conflict_write_availability(session_id, locked))
|
|
459
|
+
self.rebase_controller.close_wait_requested.connect(lambda session_id: self.close_readiness_controller.show_close_wait(session_id))
|
|
460
|
+
self.rebase_controller.rebase_state_shown.connect(lambda session_id, state: self.conflict_controller.show_rebase_state_in_conflict_dialog(session_id, state))
|
|
461
|
+
self.rebase_controller.conflict_list_requested.connect(lambda session_id: self.conflict_controller.open_conflict_dialog(session_id))
|
|
462
|
+
self.stash_controller.conflict_retention_requested.connect(lambda key, observation: self.conflict_controller.track_conflict_retention(key, observation))
|
|
463
|
+
# The session a published change of repository leaves behind closes at once, its window and the navigation it publishes kept.
|
|
464
|
+
self.navigation_controller.repository_close_requested.connect(lambda owner_id: self.shutdown_controller.close_repository(owner_id, keep_navigation=True))
|
|
465
|
+
# Qt calls the filters of the application from the last installed to the first: the placement filter still sees a
|
|
466
|
+
# window's first show before the activation filter, and the event types of the other filters do not overlap; the
|
|
467
|
+
# modal relay alone follows WindowBlocked and WindowUnblocked.
|
|
468
|
+
application.installEventFilter(self.shutdown_controller)
|
|
469
|
+
self.drop_down_wheel_filter = DropDownWheelFilter(application, self)
|
|
470
|
+
application.installEventFilter(self.window_activation_controller)
|
|
471
|
+
application.applicationStateChanged.connect(self.window_activation_controller.schedule_activation_update)
|
|
472
|
+
self.window_placement = NewWindowPlacement(application, self)
|
|
473
|
+
self.modal_focus_relay: ModalFocusRelay | None = None
|
|
474
|
+
activate_modal_window = modal_window_activator(QGuiApplication.platformName())
|
|
475
|
+
if activate_modal_window is not None:
|
|
476
|
+
self.modal_focus_relay = ModalFocusRelay(application, activate_modal_window, self)
|
|
477
|
+
|
|
478
|
+
def wire_staging_session(self, key: WorktreeKey, staging: StagingSession) -> None:
|
|
479
|
+
"""Connect a new preparation session to the controllers that follow what it does.
|
|
480
|
+
|
|
481
|
+
The registry that creates the session sits below these controllers and knows none of them, so its connections are
|
|
482
|
+
made here, together, in the order Qt calls them. The commit timing hears a commit's result after the controllers
|
|
483
|
+
that start its refresh, so the refresh it waits for is already asked.
|
|
484
|
+
"""
|
|
485
|
+
staging.changed.connect(lambda: self.worktree_state_controller.show_worktree_state(key))
|
|
486
|
+
staging.changed.connect(lambda: self.history_rewrite_controller.observe_worktree(staging))
|
|
487
|
+
staging.head_moved.connect(lambda: self.worktree_state_controller.follow_moved_head(key))
|
|
488
|
+
staging.pause_observation_requested.connect(lambda: self.worktree_state_controller.observe_worktree_pause(key))
|
|
489
|
+
staging.window_opened.connect(self.staging_controller.staging_window_opened)
|
|
490
|
+
staging.commit_finished.connect(lambda outcome: self.staging_controller.commit_finished(staging, outcome))
|
|
491
|
+
staging.commit_finished.connect(self.commit_push.commit_finished)
|
|
492
|
+
staging.commit_submitted.connect(lambda request: self.commit_timing_controller.commit_clicked(staging, request))
|
|
493
|
+
staging.commit_finished.connect(self.commit_timing_controller.commit_finished)
|
|
494
|
+
staging.commit_push_requested.connect(self.transport_controller.commit_push_requested)
|
|
495
|
+
# The session behind this service has just read its worktree on the same result, so only the others read.
|
|
496
|
+
staging.ignore_service.changed.connect(lambda context: self.staging_controller.ignore_changed(context, refreshed_key=staging.key))
|
|
497
|
+
staging.ignore_service.finished.connect(self.staging_controller.log_mutation_result)
|
|
498
|
+
staging.history_requested.connect(lambda anchor: self.staging_controller.open_staging_history(staging, anchor))
|
|
499
|
+
staging.folder_requested.connect(lambda path: self.system_tool.show_file_location((key.git_dir, STAGING_FOLDER_TOOL), staging.context, path, allow_missing=True))
|
|
500
|
+
staging.text_editor_requested.connect(lambda path: self.file_action_controller.launch_text_editor((key.git_dir, STAGING_TEXT_EDITOR_TOOL), staging.context, path))
|
|
501
|
+
# A brand new session has never been told which window is active, and no activation event is on its way.
|
|
502
|
+
self.window_activation_controller.schedule_activation_update()
|
|
503
|
+
|
|
504
|
+
def wire_repository_window(self, identity: RequestIdentity, session: RepositorySession, window: RepositoryWindow, context: RepositoryContext, file_history_anchor: FileHistoryAnchor | None) -> None:
|
|
505
|
+
"""Connect a new repository window and its session to the controllers that answer what the user does in it.
|
|
506
|
+
|
|
507
|
+
The opening that builds the window sits below most of these controllers and knows none of them, so its
|
|
508
|
+
connections are made here, together, in the order Qt calls them, before the window is shown.
|
|
509
|
+
"""
|
|
510
|
+
window.file_history_requested.connect(lambda entry: self.file_action_controller.open_file_history(identity.session_id, entry))
|
|
511
|
+
window.remove_history_requested.connect(lambda selection: self.history_rewrite_controller.request(identity.session_id, selection))
|
|
512
|
+
window.remove_history_reason = lambda selection: self.history_rewrite_controller.remove_history_reason(identity.session_id, selection)
|
|
513
|
+
window.file_folder_requested.connect(lambda selection: self.file_action_controller.show_file_folder(identity.session_id, selection))
|
|
514
|
+
window.file_text_editor_requested.connect(lambda selection: self.file_action_controller.show_file_in_text_editor(identity.session_id, selection))
|
|
515
|
+
window.text_editor_reason = lambda selection: self.file_action_controller.text_editor_reason(identity.session_id, selection)
|
|
516
|
+
if file_history_anchor is None:
|
|
517
|
+
self.file_action_controller.read_git_editor_setting(context)
|
|
518
|
+
window.file_save_requested.connect(lambda selection: self.file_action_controller.save_file_as(identity.session_id, selection))
|
|
519
|
+
window.file_restore_requested.connect(lambda selection, target: self.file_action_controller.restore_historical_file(identity.session_id, selection, target))
|
|
520
|
+
window.difftool_requested.connect(lambda: self.file_action_controller.compare_external(identity.session_id))
|
|
521
|
+
window.file_history_resume_requested.connect(lambda: self.history_read_controller.resume_file_history(identity.session_id))
|
|
522
|
+
session.selection_changed.connect(lambda oid: window.diff_panel.clear_selection("Loading diff…"))
|
|
523
|
+
session.selection_changed.connect(lambda oid: window.file_tree_panel.clear_selection("Loading tree…"))
|
|
524
|
+
session.closed.connect(window.file_tree_panel.clear_selection)
|
|
525
|
+
window.file_tree_panel.selection_requested.connect(lambda entry: self.history_read_controller.read_blob(identity.session_id, entry))
|
|
526
|
+
window.file_tree_panel.retry_requested.connect(lambda kind: self.history_read_controller.retry_file_tree(identity.session_id, kind))
|
|
527
|
+
window.detail_tabs.currentChanged.connect(lambda _index: self.history_read_controller.read_visible_tree(identity.session_id))
|
|
528
|
+
window.diff_panel.entry_selected.connect(lambda entry: self.history_read_controller.read_diff(identity.session_id, entry))
|
|
529
|
+
window.diff_panel.retry_requested.connect(lambda: self.history_read_controller.retry_diff(identity.session_id))
|
|
530
|
+
window.diff_panel.full_context_requested.connect(lambda checked: self.history_read_controller.save_reading_preference(identity.session_id, {"diff_show_entire_file": checked}))
|
|
531
|
+
window.reading_preference_requested.connect(lambda values: self.history_read_controller.save_reading_preference(identity.session_id, values))
|
|
532
|
+
window.set_avatar_service(self.avatar_service)
|
|
533
|
+
window.explorer_requested.connect(lambda: self.file_action_controller.open_explorer(identity.session_id))
|
|
534
|
+
if WindowCapability.MANAGE in session.capabilities:
|
|
535
|
+
window.bind_terminal_registry(self.terminal_registry)
|
|
536
|
+
window.terminal_requested.connect(lambda descriptor: self.file_action_controller.launch_terminal(identity.session_id, descriptor))
|
|
537
|
+
window.commit_selected.connect(lambda oid: self.session_registry_controller.session_by_id[identity.session_id].select_commit(oid))
|
|
538
|
+
window.commit_selected.connect(lambda oid: self.history_read_controller.read_selection(identity.session_id, oid))
|
|
539
|
+
window.detail_retry_requested.connect(lambda oid: self.history_read_controller.retry_selection(identity.session_id, oid))
|
|
540
|
+
window.navigation_requested.connect(lambda oid: self.session_registry_controller.session_by_id[identity.session_id].navigate_to_commit(oid))
|
|
541
|
+
window.repository_activated.connect(lambda directory: self.navigation_controller.navigate_repository(identity.session_id, directory))
|
|
542
|
+
window.repository_switch_requested.connect(lambda directory: self.navigation_controller.switch_repository(identity.session_id, directory))
|
|
543
|
+
session.navigation_requested.connect(lambda oid: self.history_read_controller.read_navigation(identity.session_id, oid))
|
|
544
|
+
window.setting_requested.connect(lambda: self.setting_controller.show_setting(identity.session_id))
|
|
545
|
+
window.installEventFilter(self.window_activation_controller)
|
|
546
|
+
window.commit_requested.connect(lambda: self.staging_controller.open_staging(identity.session_id))
|
|
547
|
+
window.checkout_requested.connect(lambda full_ref: self.reference_form_controller.open_checkout(identity.session_id, full_ref))
|
|
548
|
+
window.create_branch_requested.connect(lambda oid, snapshot: self.reference_form_controller.open_branch(identity.session_id, BranchMode.CREATE, oid, expected_snapshot=snapshot))
|
|
549
|
+
window.create_tag_requested.connect(lambda oid, snapshot: self.reference_form_controller.open_tag(identity.session_id, oid, expected_snapshot=snapshot))
|
|
550
|
+
window.remote_requested.connect(lambda: self.transport_controller.open_remote(identity.session_id))
|
|
551
|
+
window.emoji_keyboard_requested.connect(lambda: self.emoji_window_controller.open_emoji_window(identity.session_id))
|
|
552
|
+
window.fetch_requested.connect(lambda: self.transport_controller.fetch(identity.session_id))
|
|
553
|
+
window.push_requested.connect(lambda: self.transport_controller.open_push(identity.session_id))
|
|
554
|
+
window.stash_requested.connect(lambda: self.stash_controller.open_stash(identity.session_id))
|
|
555
|
+
window.resolve_requested.connect(lambda state: self.conflict_controller.open_conflict_dialog(identity.session_id, state))
|
|
556
|
+
# Continue merge works the way Git Extensions does: it opens Prepare changes, where Git's merge message is offered,
|
|
557
|
+
# and the user makes the merge commit with Commit; Git's own merge --continue is never run.
|
|
558
|
+
window.merge_continue_requested.connect(lambda: self.staging_controller.open_staging(identity.session_id))
|
|
559
|
+
window.merge_abort_requested.connect(lambda: self.conflict_controller.abort_merge(identity.session_id))
|
|
560
|
+
window.rebase_requested.connect(lambda: self.rebase_controller.open_rebase_dialog(identity.session_id))
|
|
561
|
+
window.replay_refresh_requested.connect(lambda: self.replay_controller.observe_replay(identity.session_id))
|
|
562
|
+
window.replay_resolve_requested.connect(lambda state: self.replay_controller.resolve_replay(identity.session_id, state))
|
|
563
|
+
window.replay_continue_requested.connect(lambda state: self.replay_controller.continue_replay(identity.session_id, state))
|
|
564
|
+
window.replay_abort_requested.connect(lambda state: self.replay_controller.abort_replay(identity.session_id, state))
|
|
565
|
+
window.replay_requested.connect(lambda oid, kind, snapshot: self.replay_controller.open_replay(identity.session_id, oid, kind, snapshot))
|
|
566
|
+
window.rebase_start_requested.connect(lambda oid, snapshot: self.rebase_controller.start_rebase(identity.session_id, oid, snapshot))
|
|
567
|
+
window.merge_recovery_requested.connect(lambda: self.conflict_controller.open_merge_recovery(identity.session_id))
|
|
568
|
+
window.stash_pop_requested.connect(lambda: self.stash_controller.stash_pop(identity.session_id))
|
|
569
|
+
window.stash_drop_requested.connect(lambda entry: self.stash_controller.drop_stash(identity.session_id, entry))
|
|
570
|
+
window.fetch_selection_requested.connect(lambda choice: self.transport_controller.fetch(identity.session_id, choice))
|
|
571
|
+
window.fetch_menu_requested.connect(lambda: self.transport_controller.read_fetch_choice(identity.session_id))
|
|
572
|
+
window.fetch_default_requested.connect(lambda mode: self.transport_controller.save_fetch_default(identity.session_id, mode))
|
|
573
|
+
window.reset_requested.connect(lambda oid, snapshot: self.reference_form_controller.open_reset(identity.session_id, oid, expected_snapshot=snapshot))
|
|
574
|
+
window.move_branch_requested.connect(lambda oid, snapshot: self.reference_form_controller.open_branch(identity.session_id, BranchMode.MOVE, oid, expected_snapshot=snapshot))
|
|
575
|
+
window.rename_branch_requested.connect(lambda full_ref, oid, snapshot: self.reference_form_controller.open_branch(identity.session_id, BranchMode.RENAME, oid, expected_snapshot=snapshot, full_ref=full_ref))
|
|
576
|
+
window.delete_branch_requested.connect(lambda full_ref, oid, snapshot: self.reference_form_controller.open_delete_branch(identity.session_id, full_ref, oid, expected_snapshot=snapshot))
|
|
577
|
+
window.delete_tag_requested.connect(lambda full_ref, oid, snapshot: self.reference_form_controller.open_delete_tag(identity.session_id, full_ref, oid, expected_snapshot=snapshot))
|
|
578
|
+
window.checkout_commit_requested.connect(lambda oid, snapshot: self.reference_form_controller.open_checkout_revision(identity.session_id, oid, expected_snapshot=snapshot))
|
|
579
|
+
window.checkout_branch_requested.connect(lambda full_ref, oid, snapshot: self.reference_form_controller.open_checkout(identity.session_id, full_ref, target_oid=oid, expected_snapshot=snapshot))
|
|
580
|
+
window.open_requested.connect(lambda: self.welcome_controller.reopen_welcome(identity.session_id))
|
|
581
|
+
window.closed.connect(lambda: self.shutdown_controller.close_repository(identity.session_id))
|
|
582
|
+
window.refresh_requested.connect(lambda: self.refresh_controller.refresh(identity.session_id))
|
|
583
|
+
window.filter_changed.connect(lambda text: self.history_read_controller.change_filter(identity.session_id, text))
|
|
584
|
+
window.search_requested.connect(lambda: self.history_read_controller.start_search(identity.session_id))
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
def application_module_name() -> str:
|
|
588
|
+
"""Answer the name under which the import system loaded this module, which a desktop shortcut passes to ``python -m``.
|
|
589
|
+
|
|
590
|
+
It is read from the module's spec, so it stays right when this module runs itself through -m, where __name__ is
|
|
591
|
+
__main__, and a move of the module cannot leave a stale name in the shortcut. A module loaded without a spec has no
|
|
592
|
+
name to give, and the text is empty.
|
|
593
|
+
"""
|
|
594
|
+
if __spec__ is None:
|
|
595
|
+
return ""
|
|
596
|
+
return __spec__.name
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
def run(argv: list[str], *, preference_filename: str = "") -> int:
|
|
600
|
+
"""Parse command-line options and start the installed Qt application.
|
|
601
|
+
|
|
602
|
+
Preferences live in one INI file: ``preference_filename`` when given (test launchers pass a temporary one), otherwise
|
|
603
|
+
the per-user gitpane.ini. The registry is no longer read or written.
|
|
604
|
+
|
|
605
|
+
A GitPane started detached by the gitpane command finds the ready channel in its environment: it is taken before any
|
|
606
|
+
program is started, so none inherits it, and the ready signal is sent once the first window is shown and Git is set
|
|
607
|
+
up, which gives the terminal back. When the command is gone before that (its wait ran out, Ctrl+C, the terminal was
|
|
608
|
+
closed), the standard streams leave its pipes as soon as the event loop runs. This function never detaches by
|
|
609
|
+
itself: only main() decides that.
|
|
610
|
+
"""
|
|
611
|
+
ready_channel = take_ready_channel(os.environ)
|
|
612
|
+
option = command_line_parser().parse_args(argv)
|
|
613
|
+
declare_application_identity()
|
|
614
|
+
# The desktop finds a running application by this identifier, which Qt hands to each window when it creates it, so
|
|
615
|
+
# it is set before the application and its first window. Under Wayland it is the application id GNOME reads to name
|
|
616
|
+
# the window and to find the GitPane.desktop entry that carries the icon; under X11 the window class keeps matching
|
|
617
|
+
# the entry's StartupWMClass. Without it, a window started from Python is named after the interpreter.
|
|
618
|
+
QGuiApplication.setDesktopFileName(APPLICATION_NAME)
|
|
619
|
+
application = QApplication([APPLICATION_NAME])
|
|
620
|
+
# Here: the first point where an event loop can watch the channel, so a command gone before the first window is
|
|
621
|
+
# handled as soon as the loop runs, not only once GitPane is ready.
|
|
622
|
+
ready_watch = ReadyChannelWatch(ready_channel, application) if ready_channel is not None else None
|
|
623
|
+
configure_system_style(application)
|
|
624
|
+
# Here: the application just loaded GTK and subscribed Qt's portal listeners, and a dark desktop palette must exist before the Git Extensions colours are decided on it.
|
|
625
|
+
install_desktop_appearance_follower(application)
|
|
626
|
+
install_light_field_palette(application)
|
|
627
|
+
install_application_icon(application)
|
|
628
|
+
application.setOrganizationName(APPLICATION_NAME)
|
|
629
|
+
application.setApplicationName(APPLICATION_NAME)
|
|
630
|
+
application.setApplicationVersion(APPLICATION_VERSION)
|
|
631
|
+
preference_filename = preference_filename or user_preference_filename(sys.platform, os.environ, os.path.expanduser("~"))
|
|
632
|
+
preference = PreferenceStore(QSettings(preference_filename, QSettings.Format.IniFormat))
|
|
633
|
+
git_filename = str(preference.read("git_filename"))
|
|
634
|
+
repository = GitRepository(git_filename)
|
|
635
|
+
diagnostic_log = DiagnosticLog(option.log, writable_folder(QStandardPaths.StandardLocation.AppLocalDataLocation, sys.platform))
|
|
636
|
+
diagnostic_log.write_line(APPLICATION_VERSION_LINE)
|
|
637
|
+
diagnostic_log.write_line(PREFERENCE_FILE_LOG_TEXT.format(filename=preference_filename))
|
|
638
|
+
if preference.warning:
|
|
639
|
+
diagnostic_log.write_line(preference.warning)
|
|
640
|
+
for line in preference.foreign_dir_lines:
|
|
641
|
+
diagnostic_log.write_line(line)
|
|
642
|
+
# Workaround: on macOS, QApplication.quit() asks AppKit to terminate the application. Asked while GitPane answers
|
|
643
|
+
# AppKit's own terminate request (Cmd+Q when nothing runs), AppKit calls the C exit() in the middle of a Python call:
|
|
644
|
+
# exec() never returns and Shiboken's static destructors crash the interpreter. exit() only leaves the event loop,
|
|
645
|
+
# on every system, so exec() returns here and the process ends normally. It is given here because only this function
|
|
646
|
+
# runs the loop: called while no loop runs, exit() would make every later event loop return at once.
|
|
647
|
+
coordinator = ApplicationWiring(application, preference, repository, diagnostic_log, leave_event_loop=application.exit)
|
|
648
|
+
# The startup read blocks nothing: Show in text editor and Settings use the desktop's editor as soon as it arrives.
|
|
649
|
+
coordinator.system_editor_default.request_read()
|
|
650
|
+
coordinator.welcome_controller.show_welcome()
|
|
651
|
+
coordinator.setting_controller.initialize_git()
|
|
652
|
+
if ready_watch is not None:
|
|
653
|
+
# Sent from the event loop, so the first window has had its show handled before the terminal is given back.
|
|
654
|
+
QTimer.singleShot(0, functools.partial(ready_watch.send_ready, diagnostic_log.log_filename or ""))
|
|
655
|
+
application.setQuitOnLastWindowClosed(False)
|
|
656
|
+
application.lastWindowClosed.connect(coordinator.shutdown_controller.request_shutdown)
|
|
657
|
+
try:
|
|
658
|
+
return application.exec()
|
|
659
|
+
finally:
|
|
660
|
+
diagnostic_log.close()
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
def main(stream_is_terminal: Callable[[int], bool] = os.isatty) -> int:
|
|
664
|
+
"""Start GitPane with the command-line arguments of the process, detached from the terminal it was typed in.
|
|
665
|
+
|
|
666
|
+
The installed gitpane command, main.py and python -m gitpane.application call it without argument;
|
|
667
|
+
``stream_is_terminal`` lets tests decide whether the standard streams are a terminal. The options are read first, so
|
|
668
|
+
--version, --help and an unknown option answer in the terminal. Typed in a terminal on Linux or macOS without
|
|
669
|
+
--attach, GitPane is started again in a session of its own, under the interpreter options of this command, and this
|
|
670
|
+
command returns once its first window is shown; otherwise GitPane runs in this process, and with --attach Ctrl+C
|
|
671
|
+
stops it at once.
|
|
672
|
+
"""
|
|
673
|
+
argv = sys.argv[1:]
|
|
674
|
+
option = command_line_parser().parse_args(argv)
|
|
675
|
+
module_name = application_module_name()
|
|
676
|
+
if module_name and detaches_from_terminal(sys.platform, stream_is_terminal, option.attach, os.environ):
|
|
677
|
+
return relaunch_detached(detached_child_command(argv, sys.executable, module_name, running_interpreter_options()), os.environ).exit_code
|
|
678
|
+
if interrupt_stops_gitpane(sys.platform, option.attach):
|
|
679
|
+
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
|
680
|
+
return run(argv)
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
if __name__ == "__main__":
|
|
684
|
+
raise SystemExit(main())
|