import logging import os import sys from typing import List, Optional from PyQt6.QtWidgets import * from PyQt6.QtGui import * import constants import urlutils from aboutdialog import AboutDialog from cutesettings import CuteSettings from raven.pluginregistry import PluginRegistry from raven.plugins.domain.menucontribution import MenuContribution from raven.plugins.domain.raction import RAction from raven.plugins.domain.rmenu import RMenu from settingsstore import SettingsStore from highlightingdialog import HighlightingDialog from tabs import Tabs from urlutils import url_is_file from functools import reduce MAX_LINE_LENGTH = 4096 logging.basicConfig(level=logging.INFO) log = logging.getLogger("main") def flat_map(array: List[List]) -> List: return reduce(list.__add__, array) def _action_about(): about_action = RAction( "&About", action=lambda: AboutDialog().exec(), icon_file=constants.raven_icon ) return about_action class MainWindow(QMainWindow): def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.settings = SettingsStore.load() PluginRegistry.execute("set_settings", self.settings) PluginRegistry.execute("set_translator", lambda string: self.tr(string)) self.setWindowTitle(self.tr("RavenLog")) self._restore_window() self.setDockNestingEnabled(True) self.setAcceptDrops(True) self.tabs = Tabs(self.settings) self._menu_recent_files = QMenu(self.tr("Open &Recent"), self) self.setCentralWidget(self.tabs) self.status_bar = QStatusBar(self) self.setStatusBar(self.status_bar) self.setMenuBar(self.create_dynamic_menu_bar()) def create_dynamic_menu_bar(self) -> QMenuBar: menu_bar = QMenuBar() menu_contributions: [MenuContribution] = flat_map(PluginRegistry.execute("get_menu_contributions")) menu_contributions.append(MenuContribution("file", action=self._action_close(), action_id="close app")) menu_contributions.append(MenuContribution("settings", action=self._action_highlighter())) menu_contributions.append(MenuContribution("settings", action=self._action_highlight_search_terms())) menu_contributions.append(MenuContribution("settings", action=self._action_new_tab())) menu_contributions.append(MenuContribution("help", action=_action_about())) known_menus = [ ("file", self.tr("&File")), ("settings", self.tr("&Settings")), ("help", self.tr("&Help")) ] for (menu_id, menu_label) in known_menus: menu = QMenu(menu_label, self) mcs: [MenuContribution] = [mc for mc in menu_contributions if mc.menu_id == menu_id] for menu_contribution in mcs: print("%s %s" % (menu_id, menu_contribution.action_id)) if menu_contribution.action: action = self._raction_to_qaction(menu_contribution.action, menu) menu.addAction(action) if menu_contribution.menu: submenu = QMenu(self.tr(menu_contribution.menu.label), menu_bar) menu_contribution.menu.add_change_listener( lambda qmenu=submenu, rmenu=menu_contribution.menu: self._rmenu_update(qmenu, rmenu)) self._rmenu_update(submenu, menu_contribution.menu) menu.addMenu(submenu) menu_bar.addMenu(menu) return menu_bar def _rmenu_update(self, qmenu: QMenu, rmenu: RMenu): qmenu.clear() for action in rmenu.actions: action = self._raction_to_qaction(action, qmenu) qmenu.addAction(action) def _raction_to_qaction(self, raction: RAction, qmenu: QMenu) -> QAction: action = QAction(self.tr(raction.label), qmenu) if raction.icon_from_theme: action.setIcon(QIcon.fromTheme(raction.icon_from_theme)) if raction.icon_file: action.setIcon(QIcon(raction.icon_file)) if raction.shortcut: action.setShortcut(raction.shortcut) if raction.action: action.triggered.connect(raction.action) if raction.checkable: action.setCheckable(raction.checkable) action.setChecked(raction.checked) return action def _action_open_file(self) -> RAction: open_file = RAction("&Open...", action=self._open_file_dialog, shortcut='Ctrl+O', icon_from_theme="document-open") return open_file def _action_close(self) -> RAction: # Linux: QIcon.fromTheme("exit") icon = "close" if sys.platform == 'win32' or sys.platform == 'cygwin' else "exit" close_action = RAction("E&xit", action=self.destruct, shortcut='Ctrl+X', icon_from_theme=icon) return close_action def create_menu_bar(self) -> QMenuBar: menu_bar = QMenuBar() menu_bar.addMenu(self.file_menu()) menu_bar.addMenu(self.settings_menu()) menu_bar.addMenu(self.help_menu()) return menu_bar def file_menu(self) -> QMenu: file_menu = QMenu(self.tr("&File", "name of the file menu"), self) open_file = self._action_open_file() close_action = self._action_close() self._update_recent_files_menu() file_menu.addAction(open_file) file_menu.addMenu(self._menu_recent_files) file_menu.addAction(close_action) return file_menu def _action_highlighter(self): manage = RAction( "&Highlighter", action=lambda: HighlightingDialog(self.settings).exec(), shortcut='Ctrl+H' ) return manage def _action_highlight_search_terms(self): highlight_search_terms = RAction( "Highlight &Searches", action=lambda checked: self.settings.set_session("general", "highlight_search_term", str(checked)) or self.update() ) highlight_search_terms.set_checkable(True) highlight_search_terms.set_checked(self.settings.session.getboolean("general", "highlight_search_term")) return highlight_search_terms def _action_new_tab(self): new_tab = RAction("Open Tab on Save As File") new_tab.set_checkable(True) new_tab.set_checked(self.settings.session.getboolean("general", "open_tab_on_save_as_file")) new_tab.set_action( lambda checked: self.settings.set_session("general", "open_tab_on_save_as_file", str(checked))) return new_tab def settings_menu(self) -> QMenu: result = QMenu(self.tr("&Settings"), self) result.addAction(self._action_highlighter()) result.addAction(self._action_highlight_search_terms()) result.addAction(self._action_new_tab()) return result def help_menu(self) -> QMenu: help_menu = QMenu(self.tr("&Help", "name of the help menu"), self) help_menu.addAction(_action_about()) return help_menu def _update_recent_files_menu(self): self._menu_recent_files.clear() files = self._get_recent_files() for file in files: action = QAction(os.path.basename(file), self) action.triggered.connect(lambda x, f=file: self.open_file(f)) self._menu_recent_files.addAction(action) def _open_file_dialog(self) -> None: current_file = self.tabs.current_file() directory = os.path.dirname(current_file) if current_file else '' dialog = QFileDialog(self) (selected_file, _filter) = dialog.getOpenFileName( caption=self.tr("Open File"), directory=directory ) if selected_file: self.tabs.create_tab(selected_file) self._remember_recent_file(selected_file) def current_file(self) -> Optional[str]: return self.tabs.current_file() def open_file(self, file: str) -> None: self.tabs.create_tab(file) PluginRegistry.execute("after_open_file", file) # self._remember_recent_file(file) # can_open_plugins = PluginRegistry.execute("can_open_file", file) # if len(can_open_plugins) == 1: # else: def _get_recent_files(self) -> [str]: recent_files = self.settings.session.get('general', 'recent_files', fallback='') files = recent_files.split(os.pathsep) if "" in files: files.remove("") return files # def _remember_recent_file(self, file: str): # files = self._get_recent_files() # if file in files: # files.remove(file) # files.insert(0, file) # recent_files = os.pathsep.join(files[:10]) # self.settings.set_session('general', 'recent_files', recent_files) # self._update_recent_files_menu() def dragEnterEvent(self, e: QDragEnterEvent): if url_is_file(e.mimeData().text()): e.accept() else: e.ignore() def dropEvent(self, e): file = urlutils.url_to_path(e.mimeData().text()) self.open_file(file) def _restore_window(self): qsettings = CuteSettings() geometry_restored = False geometry = qsettings.value("mainWindow/geometry") if geometry: geometry_restored = self.restoreGeometry(geometry) if not geometry_restored: self.setGeometry(0, 0, 800, 600) def closeEvent(self, event): self.destruct() def destruct(self): self.tabs.destruct() self.close() SettingsStore.save(self.settings) CuteSettings().set_value("mainWindow/geometry", self.saveGeometry())