diff --git a/data/layout-files-examples/2-3-grid.json b/data/layout-files-examples/2-3-grid.json new file mode 100644 index 00000000..5d6c9d21 --- /dev/null +++ b/data/layout-files-examples/2-3-grid.json @@ -0,0 +1,36 @@ +{ + "layout": { + "tab1": [ + { + "children": [ + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + } + ] + }, + { + "children": [ + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + } + ] + } + ] + }, + "profile": { + "background_color": "#170717", + "foreground_color": "#f5c0b7" + } +} diff --git a/data/layout-files-examples/2-columns.json b/data/layout-files-examples/2-columns.json new file mode 100644 index 00000000..1524f19e --- /dev/null +++ b/data/layout-files-examples/2-columns.json @@ -0,0 +1,13 @@ +{ + "layout": { + "vertical": false, + "tab1": [ + { + "command": "bash" + }, + { + "command": "bash" + } + ] + } +} diff --git a/data/layout-files-examples/3-rows.json b/data/layout-files-examples/3-rows.json new file mode 100644 index 00000000..aaf4fd91 --- /dev/null +++ b/data/layout-files-examples/3-rows.json @@ -0,0 +1,15 @@ +{ + "layout": { + "tab1": [ + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + } + ] + } +} diff --git a/data/layout-files-examples/3-tabs-2-rows.json b/data/layout-files-examples/3-tabs-2-rows.json new file mode 100644 index 00000000..e0d2d2c8 --- /dev/null +++ b/data/layout-files-examples/3-tabs-2-rows.json @@ -0,0 +1,28 @@ +{ + "layout": { + "tab1": [ + { + "command": "bash" + }, + { + "command": "bash" + } + ], + "tab2": [ + { + "command": "bash" + }, + { + "command": "bash" + } + ], + "tab3": [ + { + "command": "bash" + }, + { + "command": "bash" + } + ] + } +} diff --git a/data/layout-files-examples/3-tabs-3-columns.json b/data/layout-files-examples/3-tabs-3-columns.json new file mode 100644 index 00000000..86792074 --- /dev/null +++ b/data/layout-files-examples/3-tabs-3-columns.json @@ -0,0 +1,38 @@ +{ + "layout": { + "vertical": false, + "tab1": [ + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + } + ], + "tab2": [ + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + } + ], + "tab3": [ + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + } + ] + } +} diff --git a/data/layout-files-examples/4-4-grid.json b/data/layout-files-examples/4-4-grid.json new file mode 100644 index 00000000..789c0810 --- /dev/null +++ b/data/layout-files-examples/4-4-grid.json @@ -0,0 +1,75 @@ +{ + "layout": { + "tab1": [ + { + "children": [ + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + } + ] + }, + { + "children": [ + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + } + ] + }, + { + "children": [ + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + } + ] + }, + { + "children": [ + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + } + ] + } + ] + }, + "profile": { + "background_color": "#070717", + "foreground_color": "#f5c0b7", + "font": "Monospace 16" + } +} diff --git a/data/layout-files-examples/README.md b/data/layout-files-examples/README.md new file mode 100644 index 00000000..98ce6592 --- /dev/null +++ b/data/layout-files-examples/README.md @@ -0,0 +1,8 @@ +The JSONs files in this directory are example config files used by `--config-json` option. + +Once this feature would be documented officialy this directoy can be removed. + +Example: +``` +./terminator --config-json data/layout-files-examples/2-3-grid.json +``` diff --git a/data/layout-files-examples/complex.json b/data/layout-files-examples/complex.json new file mode 100644 index 00000000..f92e83d3 --- /dev/null +++ b/data/layout-files-examples/complex.json @@ -0,0 +1,224 @@ +{ + "layout":{ + "columns": [ + { + "children": [ + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + } + ] + } + ], + "rows": [ + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + } + ], + "grid": [ + { + "children": [ + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + } + ] + }, + { + "children": [ + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + } + ] + }, + { + "children": [ + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + } + ] + }, + { + "children": [ + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + } + ] + } + ], + "border": [ + { + "command": "bash", + "ratio": 0.2 + }, + { + "ratio": 0.8, + "children": [ + { + "command": "bash", + "ratio": 0.2 + }, + { + "command": "bash", + "ratio": 0.8 + }, + { + "command": "bash" + } + ] + }, + { + "command": "bash" + } + ], + "nested": [ + { + "children": [ + { + "children": [ + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + }, + { + "command": "bash" + } + ] + }, + { + "command": "bash" + } + ] + }, + { + "children": [ + { + "children": [ + { + "children": [ + { + "command": "bash" + }, + { + "command": "bash" + } + ] + }, + { + "children": [ + { + "command": "bash" + }, + { + "command": "bash" + } + ] + } + ] + }, + { + "children": [ + { + "children": [ + { + "command": "bash" + }, + { + "command": "bash" + } + ] + }, + { + "children": [ + { + "children": [ + { + "command": "bash" + }, + { + "command": "bash" + } + ] + }, + { + "children": [ + { + "command": "bash" + }, + { + "command": "bash" + } + ] + } + ] + } + ] + } + ] + } + ] + }, + "profile":{ + "background_color": "#170717", + "foreground_color": "#f5c0b7", + "font": "Monospace 16", + "scrollback_infinite":"True" + } +} \ No newline at end of file diff --git a/terminator b/terminator index 9727a9ed..808888c7 100755 --- a/terminator +++ b/terminator @@ -51,6 +51,7 @@ from terminatorlib.factory import Factory from terminatorlib.version import APP_NAME, APP_VERSION from terminatorlib.util import dbg, err from terminatorlib.layoutlauncher import LayoutLauncher +from terminatorlib.configjson import ConfigJson if __name__ == '__main__': # Workaround for IBus intefering with broadcast when using dead keys @@ -68,6 +69,14 @@ if __name__ == '__main__': OPTIONS,dbus_options = terminatorlib.optionparse.parse_options() + if OPTIONS.configjson: + configjson = ConfigJson() + layoutname = configjson.extend_config(OPTIONS.configjson) + if layoutname and ((not OPTIONS.layout) or OPTIONS.layout == 'default'): + OPTIONS.layout = layoutname + if not OPTIONS.profile: + OPTIONS.profile = configjson.get_profile_to_use() + TERMINATOR = Terminator() TERMINATOR.set_origcwd(ORIGCWD) diff --git a/terminatorlib/config.py b/terminatorlib/config.py index ffbbfdb6..8083791a 100644 --- a/terminatorlib/config.py +++ b/terminatorlib/config.py @@ -700,14 +700,20 @@ class ConfigBase(Borg): section = getattr(self, section_name) parser[section_name] = dict_diff(DEFAULTS[section_name], section) + from .configjson import JSON_PROFILE_NAME, JSON_LAYOUT_NAME + parser['profiles'] = {} for profile in self.profiles: + if profile == JSON_PROFILE_NAME: + continue dbg('ConfigBase::save: Processing profile: %s' % profile) parser['profiles'][profile] = dict_diff( DEFAULTS['profiles']['default'], self.profiles[profile]) parser['layouts'] = {} for layout in self.layouts: + if layout == JSON_LAYOUT_NAME: + continue dbg('ConfigBase::save: Processing layout: %s' % layout) parser['layouts'][layout] = self.layouts[layout] diff --git a/terminatorlib/configjson.py b/terminatorlib/configjson.py new file mode 100644 index 00000000..48c6ab5a --- /dev/null +++ b/terminatorlib/configjson.py @@ -0,0 +1,173 @@ +from .util import dbg, err +from os import path +import sys +import json +import copy +from .config import Config + +JSON_PROFILE_NAME = "__internal_json_profile__" +JSON_LAYOUT_NAME = "__internal_json_layout__" + +class ConfigJson(object): + profile_to_use = 'default' + + def get_profile_to_use(self): + return self.profile_to_use + + def build_single_tab_layout(self, layoutjson, vertical): + dbg ('Budiling a single tab layout from json: %s ' % layoutjson) + + result = { + 'root': { + 'type': 'Window' + } + } + + self.build_container_layout(layoutjson, result, 'root', 0, vertical) + + return result + + def build_multi_tab_layout(self, layoutjson, vertical): + dbg ('Budiling multi tabs layout from json: %s ' % layoutjson) + + tabs = { + 'type': 'Notebook', + 'parent': 'root', + 'labels': [] + } + + result = { + 'root': { + 'type': 'Window' + }, + 'tabs': tabs + } + + counter = 0 + + for tab in layoutjson: + tabs['labels'].append(tab) + self.build_container_layout(layoutjson[tab], result, 'tabs', counter, vertical) + counter += 1 + + return result + + def build_terminal_layout(self, layoutjson, children, parent, order): + dbg ('Building a terminal from json: %s' % layoutjson) + + children[parent + "." + str(order)] = { + 'type': 'Terminal', + 'order': order, + 'parent': parent, + 'profile': self.profile_to_use, + 'command': layoutjson['command'] + } + + def build_container_layout(self, layoutjson, children, parent, order, vertical): + if len(layoutjson) == 1: + layoutjson = layoutjson[0] + + if 'children' in layoutjson: + self.build_container_layout(layoutjson['children'], children, parent, order, False if vertical else True) + else: + self.build_terminal_layout(layoutjson, children, parent, order) + return + + dbg ('Building %s layout from json: %s' % ("vertical" if vertical else "horizental", layoutjson)) + + counter = 0 + actualparent = parent + + for pane in layoutjson: + if counter < (len(layoutjson) - 1): + containername = parent + "." + str(order) + "." + str(counter) + ratio = (100 / (len(layoutjson) - counter)) / 100 + if 'ratio' in pane: + ratio = pane['ratio'] + children[containername] = { + 'type': 'VPaned' if vertical else 'HPaned', + 'order': order + counter, + 'ratio': ratio, + 'parent': actualparent + } + actualparent = containername + + if 'children' in pane: + self.build_container_layout(pane['children'], children, containername, counter, False if vertical else True) + else: + self.build_terminal_layout(pane, children, containername, counter) + + counter += 1 + + def get_layout(self, layoutjson): + try: + vertical = True + if "vertical" in layoutjson: + vertical = layoutjson["vertical"] + del layoutjson["vertical"] + + result = None + + if len(layoutjson) == 1: + firstitem = next(iter(layoutjson.values())) + result = self.build_single_tab_layout(firstitem, vertical) + else: + result = self.build_multi_tab_layout(layoutjson, vertical) + + dbg('Json layout is: %s' % result) + return result + except Exception as ex: + err('Error building a layout from file %s' % ex) + return None + + def get_profile(self, profilejson, baseprofile): + try: + result = copy.deepcopy(baseprofile) + + result.update(profilejson) + + dbg('Json profile is: %s' % result) + return result + except Exception as ex: + err('Error building a profile from json file %s' % ex) + return None + + def read_config(self, jsonfile): + if not path.exists(jsonfile): + dbg("Json config file is missing %s" % jsonfile) + return None + + dbg('Loading config json from a file: %s' % jsonfile) + + layoutjson = None + + try: + with open(jsonfile) as json_file: + layoutjson = json.load(json_file) + except Exception as ex: + err('Error loading config json file %s (%s)' % (jsonfile, ex)) + return None + + return layoutjson + + def extend_config(self, jsonfile): + configjson = self.read_config(jsonfile) + + if not configjson: + return None + + config = Config() + + if 'profile' in configjson: + profile = self.get_profile(configjson['profile'], config.base.profiles['default']) + if profile: + config.base.profiles[JSON_PROFILE_NAME] = profile + self.profile_to_use = JSON_PROFILE_NAME + + if 'layout' in configjson: + layout = self.get_layout(configjson['layout']) + if layout: + config.base.layouts[JSON_LAYOUT_NAME] = layout + return JSON_LAYOUT_NAME + + return None diff --git a/terminatorlib/optionparse.py b/terminatorlib/optionparse.py index 63a6f625..8efd242b 100644 --- a/terminatorlib/optionparse.py +++ b/terminatorlib/optionparse.py @@ -74,6 +74,8 @@ def parse_options(): 'execute inside the terminal, and its arguments')) parser.add_option('-g', '--config', dest='config', help=_('Specify a config file')) + parser.add_option('-j', '--config-json', dest='configjson', + help=_('Specify a partial config json file')) parser.add_option('-x', '--execute', dest='execute', action='callback', callback=execute_cb, help=_('Use the rest of the command line as a command to execute ' diff --git a/terminatorlib/terminator.py b/terminatorlib/terminator.py index c634f98e..d8d76b4d 100644 --- a/terminatorlib/terminator.py +++ b/terminatorlib/terminator.py @@ -631,4 +631,4 @@ class Terminator(Borg): return(layout) -# vim: set expandtab ts=4 sw=4: +# vim: set expandtab ts=4 sw=4: \ No newline at end of file