pygpt-net 2.4.38__py3-none-any.whl → 2.4.40__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.
Files changed (105) hide show
  1. CHANGELOG.md +19 -0
  2. README.md +25 -2
  3. pygpt_net/CHANGELOG.txt +19 -0
  4. pygpt_net/__init__.py +3 -3
  5. pygpt_net/controller/__init__.py +7 -3
  6. pygpt_net/controller/audio/__init__.py +9 -1
  7. pygpt_net/controller/calendar/__init__.py +3 -1
  8. pygpt_net/controller/chat/input.py +2 -1
  9. pygpt_net/controller/chat/render.py +8 -5
  10. pygpt_net/controller/ctx/__init__.py +33 -25
  11. pygpt_net/controller/ctx/common.py +3 -2
  12. pygpt_net/controller/debug/__init__.py +13 -2
  13. pygpt_net/controller/dialogs/confirm.py +2 -2
  14. pygpt_net/controller/kernel/__init__.py +2 -1
  15. pygpt_net/controller/lang/custom.py +2 -7
  16. pygpt_net/controller/lang/mapping.py +2 -2
  17. pygpt_net/controller/layout.py +2 -2
  18. pygpt_net/controller/notepad.py +14 -10
  19. pygpt_net/controller/theme/nodes.py +2 -5
  20. pygpt_net/controller/tools/__init__.py +37 -1
  21. pygpt_net/controller/ui/__init__.py +1 -5
  22. pygpt_net/controller/ui/tabs.py +295 -60
  23. pygpt_net/core/command.py +3 -1
  24. pygpt_net/core/ctx/__init__.py +16 -2
  25. pygpt_net/core/ctx/container.py +18 -11
  26. pygpt_net/core/ctx/output.py +86 -67
  27. pygpt_net/core/debug/tabs.py +5 -2
  28. pygpt_net/core/filesystem/url.py +7 -3
  29. pygpt_net/core/render/base.py +14 -3
  30. pygpt_net/core/render/markdown/renderer.py +3 -1
  31. pygpt_net/core/render/plain/renderer.py +3 -3
  32. pygpt_net/core/render/web/body.py +10 -4
  33. pygpt_net/core/render/web/renderer.py +213 -41
  34. pygpt_net/core/tabs/__init__.py +268 -98
  35. pygpt_net/core/tabs/tab.py +16 -4
  36. pygpt_net/core/web.py +127 -1
  37. pygpt_net/data/config/config.json +12 -5
  38. pygpt_net/data/config/models.json +3 -3
  39. pygpt_net/data/config/modes.json +3 -3
  40. pygpt_net/data/css/web-blocks.css +18 -0
  41. pygpt_net/data/css/web-blocks.light.css +7 -0
  42. pygpt_net/data/css/web-chatgpt.css +8 -0
  43. pygpt_net/data/css/web-chatgpt_wide.css +8 -0
  44. pygpt_net/data/icons/split_screen.svg +1 -0
  45. pygpt_net/data/locale/locale.de.ini +3 -0
  46. pygpt_net/data/locale/locale.en.ini +6 -1
  47. pygpt_net/data/locale/locale.es.ini +3 -0
  48. pygpt_net/data/locale/locale.fr.ini +3 -0
  49. pygpt_net/data/locale/locale.it.ini +3 -0
  50. pygpt_net/data/locale/locale.pl.ini +4 -1
  51. pygpt_net/data/locale/locale.uk.ini +3 -0
  52. pygpt_net/data/locale/locale.zh.ini +3 -0
  53. pygpt_net/data/locale/plugin.cmd_web.de.ini +2 -0
  54. pygpt_net/data/locale/plugin.cmd_web.en.ini +20 -10
  55. pygpt_net/data/locale/plugin.cmd_web.es.ini +2 -0
  56. pygpt_net/data/locale/plugin.cmd_web.fr.ini +2 -0
  57. pygpt_net/data/locale/plugin.cmd_web.it.ini +2 -0
  58. pygpt_net/data/locale/plugin.cmd_web.pl.ini +2 -0
  59. pygpt_net/data/locale/plugin.cmd_web.uk.ini +2 -0
  60. pygpt_net/data/locale/plugin.cmd_web.zh.ini +2 -0
  61. pygpt_net/icons.qrc +1 -0
  62. pygpt_net/icons_rc.py +165 -136
  63. pygpt_net/item/ctx.py +46 -24
  64. pygpt_net/plugin/audio_input/simple.py +4 -2
  65. pygpt_net/plugin/audio_output/__init__.py +4 -1
  66. pygpt_net/plugin/base/plugin.py +18 -4
  67. pygpt_net/plugin/cmd_code_interpreter/__init__.py +39 -37
  68. pygpt_net/plugin/cmd_code_interpreter/runner.py +25 -12
  69. pygpt_net/plugin/cmd_web/__init__.py +46 -6
  70. pygpt_net/plugin/cmd_web/config.py +74 -48
  71. pygpt_net/plugin/cmd_web/websearch.py +61 -28
  72. pygpt_net/plugin/cmd_web/worker.py +79 -13
  73. pygpt_net/provider/core/config/patch.py +29 -1
  74. pygpt_net/provider/core/ctx/base.py +4 -1
  75. pygpt_net/provider/core/ctx/db_sqlite/__init__.py +10 -1
  76. pygpt_net/provider/core/ctx/db_sqlite/storage.py +22 -1
  77. pygpt_net/tools/__init__.py +9 -1
  78. pygpt_net/tools/base.py +15 -1
  79. pygpt_net/tools/code_interpreter/__init__.py +174 -75
  80. pygpt_net/tools/code_interpreter/ui/dialogs.py +21 -103
  81. pygpt_net/tools/code_interpreter/ui/widgets.py +284 -9
  82. pygpt_net/tools/html_canvas/__init__.py +78 -23
  83. pygpt_net/tools/html_canvas/ui/dialogs.py +46 -62
  84. pygpt_net/tools/html_canvas/ui/widgets.py +96 -3
  85. pygpt_net/ui/base/context_menu.py +2 -2
  86. pygpt_net/ui/layout/chat/input.py +10 -18
  87. pygpt_net/ui/layout/chat/output.py +26 -44
  88. pygpt_net/ui/layout/ctx/ctx_list.py +13 -4
  89. pygpt_net/ui/layout/toolbox/footer.py +18 -2
  90. pygpt_net/ui/main.py +2 -2
  91. pygpt_net/ui/menu/debug.py +11 -1
  92. pygpt_net/ui/widget/filesystem/explorer.py +2 -2
  93. pygpt_net/ui/widget/lists/context.py +26 -5
  94. pygpt_net/ui/widget/tabs/Input.py +2 -2
  95. pygpt_net/ui/widget/tabs/body.py +2 -1
  96. pygpt_net/ui/widget/tabs/layout.py +195 -0
  97. pygpt_net/ui/widget/tabs/output.py +209 -55
  98. pygpt_net/ui/widget/textarea/html.py +11 -1
  99. pygpt_net/ui/widget/textarea/output.py +10 -1
  100. pygpt_net/ui/widget/textarea/web.py +49 -9
  101. {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.40.dist-info}/METADATA +26 -3
  102. {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.40.dist-info}/RECORD +105 -103
  103. {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.40.dist-info}/LICENSE +0 -0
  104. {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.40.dist-info}/WHEEL +0 -0
  105. {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.40.dist-info}/entry_points.txt +0 -0
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.08.22 17:00:00 #
9
+ # Updated Date: 2024.12.12 04:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import datetime
@@ -402,11 +402,32 @@ class ImportantItemDelegate(QtWidgets.QStyledItemDelegate):
402
402
  super(ImportantItemDelegate, self).paint(painter, option, index)
403
403
 
404
404
  # pin (>= 10)
405
- if index.data(QtCore.Qt.ItemDataRole.UserRole) and index.data(QtCore.Qt.ItemDataRole.UserRole) > 0:
406
- label = index.data(QtCore.Qt.ItemDataRole.UserRole)
405
+ if index.data(QtCore.Qt.ItemDataRole.UserRole):
406
+ data = index.data(QtCore.Qt.ItemDataRole.UserRole)
407
+ label = 0
408
+ is_important = False
409
+ is_attachment = False
410
+ if "label" in data:
411
+ label = data["label"]
412
+ if "is_important" in data and data["is_important"]:
413
+ is_important = True
414
+ if "is_attachment" in data and data["is_attachment"]:
415
+ is_attachment = True
416
+
407
417
  painter.save()
408
418
 
409
- if label >= 10:
419
+ if is_attachment:
420
+ icon = QtGui.QIcon(":/icons/attachment.svg")
421
+ icon_size = option.decorationSize or QtCore.QSize(16, 16)
422
+ icon_rect = QtCore.QRect(
423
+ option.rect.right() - icon_size.width(),
424
+ option.rect.top() + (option.rect.height() - icon_size.height()) / 2,
425
+ icon_size.width(),
426
+ icon_size.height()
427
+ )
428
+ icon.paint(painter, icon_rect, QtCore.Qt.AlignCenter)
429
+
430
+ if is_important:
410
431
  color = self.get_color_for_status(3)
411
432
  square_size = 3
412
433
  square_margin = 0
@@ -426,7 +447,7 @@ class ImportantItemDelegate(QtWidgets.QStyledItemDelegate):
426
447
  )
427
448
  painter.drawRect(square_rect)
428
449
 
429
- label = label - 10 # remove pin status
450
+ #label = label - 10 # remove pin status
430
451
 
431
452
  # label (0-9)
432
453
  if label > 0:
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.01.29 14:00:00 #
9
+ # Updated Date: 2024.12.09 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtWidgets import QTabWidget, QMenu
@@ -40,7 +40,7 @@ class InputTabs(QTabWidget):
40
40
 
41
41
  :param global_pos: QPoint
42
42
  """
43
- context_menu = QMenu()
43
+ context_menu = QMenu(self)
44
44
  actions = {}
45
45
  actions['clear'] = QAction(QIcon(":/icons/delete.svg"), trans('attachments.btn.clear'), self)
46
46
  actions['clear'].triggered.connect(
@@ -6,10 +6,11 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.11.05 23:00:00 #
9
+ # Updated Date: 2024.12.09 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtWidgets import QTabWidget
13
+
13
14
  from pygpt_net.core.tabs.tab import Tab
14
15
 
15
16
 
@@ -0,0 +1,195 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # ================================================== #
4
+ # This file is a part of PYGPT package #
5
+ # Website: https://pygpt.net #
6
+ # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
+ # MIT License #
8
+ # Created By : Marcin Szczygliński #
9
+ # Updated Date: 2024.12.09 03:00:00 #
10
+ # ================================================== #
11
+
12
+ from PySide6.QtCore import Qt, QObject, QEvent
13
+ from PySide6.QtWidgets import QTabWidget, QWidget, QVBoxLayout, QSplitter, QSizePolicy
14
+
15
+ from pygpt_net.ui.widget.tabs.output import OutputTabs
16
+
17
+
18
+ class OutputColumn(QWidget):
19
+ def __init__(self, window=None):
20
+ """
21
+ Output column
22
+
23
+ :param window: window instance
24
+ """
25
+ super(OutputColumn, self).__init__(window)
26
+ self.window = window
27
+ self.idx = -1
28
+ self.tabs = OutputTabs(self.window, column=self)
29
+ self.layout = QVBoxLayout()
30
+ self.layout.addWidget(self.tabs)
31
+ self.layout.setContentsMargins(0, 0, 0, 0)
32
+ self.setLayout(self.layout)
33
+ self.filter = FocusEventFilter(self, self.on_focus)
34
+ self.installEventFilter(self.filter)
35
+ self.setFocusPolicy(Qt.StrongFocus)
36
+
37
+ def on_focus(self, widget):
38
+ """
39
+ On widget clicked
40
+
41
+ :param widget: widget
42
+ """
43
+ self.window.controller.ui.tabs.on_column_focus(self.idx)
44
+ widget.setFocus()
45
+
46
+ def set_idx(self, idx: int):
47
+ """
48
+ Set index
49
+
50
+ :param idx: int
51
+ """
52
+ self.idx = idx
53
+
54
+ def get_idx(self) -> int:
55
+ """
56
+ Get index
57
+
58
+ :return: int
59
+ """
60
+ return self.idx
61
+
62
+ def set_tabs(self, tabs: QTabWidget):
63
+ """
64
+ Set tabs widget
65
+
66
+ :param tabs: QTabWidget
67
+ """
68
+ self.tabs = tabs
69
+
70
+ def get_tabs(self) -> OutputTabs:
71
+ """
72
+ Get tabs
73
+
74
+ :return: OutputTabs
75
+ """
76
+ return self.tabs
77
+
78
+
79
+ class OutputLayout(QWidget):
80
+ def __init__(self, window=None):
81
+ """
82
+ Output layout
83
+
84
+ :param window: window instance
85
+ """
86
+ super(OutputLayout, self).__init__(window)
87
+ self.window = window
88
+ self.columns = []
89
+
90
+ column1 = OutputColumn(self.window)
91
+ column2 = OutputColumn(self.window)
92
+ self.add_column(column1)
93
+ self.add_column(column2)
94
+
95
+ self.splitter = QSplitter(Qt.Horizontal)
96
+ self.splitter.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
97
+ for column in self.columns:
98
+ self.splitter.addWidget(column)
99
+
100
+ self.window.ui.splitters['columns'] = self.splitter
101
+
102
+ self.layout = QVBoxLayout()
103
+ self.layout.addWidget(self.splitter, stretch=1)
104
+ self.layout.setContentsMargins(0, 0, 0, 0)
105
+ self.setLayout(self.layout)
106
+
107
+ def get_next_idx(self) -> int:
108
+ """
109
+ Get next index
110
+
111
+ :return: int
112
+ """
113
+ return len(self.columns)
114
+
115
+ def add_column(self, column: OutputColumn):
116
+ """
117
+ Add column
118
+
119
+ :param column: OutputColumn
120
+ """
121
+ idx = self.get_next_idx()
122
+ column.set_idx(idx)
123
+ self.columns.append(column)
124
+
125
+ def get_tabs_by_idx(self, idx: int) -> OutputTabs:
126
+ """
127
+ Get tabs by column index
128
+
129
+ :param idx: int
130
+ :return: OutputTabs
131
+ """
132
+ for column in self.columns:
133
+ if column.idx == idx:
134
+ return column.tabs
135
+ return None
136
+
137
+ def get_active_tabs(self) -> OutputTabs:
138
+ """
139
+ Get active tabs
140
+
141
+ :return: OutputTabs
142
+ """
143
+ current = self.window.controller.ui.tabs.get_current_column_idx()
144
+ for column in self.columns:
145
+ if column.idx == current:
146
+ return column.tabs
147
+
148
+ def get_column_by_idx(self, idx: int) -> OutputColumn:
149
+ """
150
+ Get column by index
151
+
152
+ :param idx: int
153
+ :return: OutputColumn
154
+ """
155
+ for column in self.columns:
156
+ if column.idx == idx:
157
+ return column
158
+ return None
159
+
160
+ def get_active_column(self) -> OutputColumn:
161
+ """
162
+ Get active column
163
+
164
+ :return: OutputColumn
165
+ """
166
+ current = self.window.controller.ui.tabs.get_current_column_idx()
167
+ for column in self.columns:
168
+ if column.idx == current:
169
+ return column
170
+
171
+ class FocusEventFilter(QObject):
172
+ def __init__(self, column, callback):
173
+ """
174
+ Column event filter
175
+
176
+ :param column: parent column
177
+ :param callback: callback
178
+ """
179
+ super().__init__()
180
+ self.column = column
181
+ self.callback = callback
182
+
183
+ def eventFilter(self, obj, event):
184
+ """
185
+ Click event filter
186
+
187
+ :param obj: object
188
+ :param event: event
189
+ """
190
+ if event.type() == QEvent.MouseButtonPress or event.type() == QEvent.FocusIn:
191
+ widget = obj
192
+ if widget is not None:
193
+ self.callback(widget)
194
+ return False
195
+ return False
@@ -6,10 +6,10 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.11.05 23:00:00 #
9
+ # Updated Date: 2024.12.12 01:00:00 #
10
10
  # ================================================== #
11
11
 
12
- from PySide6.QtWidgets import QTabWidget, QMenu
12
+ from PySide6.QtWidgets import QTabWidget, QMenu, QPushButton
13
13
  from PySide6.QtCore import Qt, Slot
14
14
  from PySide6.QtGui import QAction, QIcon
15
15
 
@@ -18,17 +18,119 @@ from pygpt_net.utils import trans
18
18
  import pygpt_net.icons_rc
19
19
 
20
20
 
21
+ class AddButton(QPushButton):
22
+ def __init__(self, window=None, column=None, tabs=None):
23
+ super(AddButton, self).__init__(QIcon(":/icons/add.svg"), "", window)
24
+ self.window = window
25
+ self.column = column
26
+ self.tabs = tabs
27
+ self.setFixedSize(30, 25)
28
+ self.setFlat(True)
29
+ self.clicked.connect(
30
+ lambda: self.window.controller.ui.tabs.new_tab(self.column.get_idx())
31
+ )
32
+ self.setObjectName('tab-add')
33
+ self.setProperty('tabAdd', True)
34
+ self.setToolTip(trans('action.tab.add.chat'))
35
+
36
+ def mousePressEvent(self, event):
37
+ """
38
+ Mouse press event
39
+
40
+ :param event: event
41
+ """
42
+ if event.button() == Qt.RightButton:
43
+ idx = 0
44
+ column_idx = self.column.get_idx()
45
+ self.show_menu(idx, column_idx, event.globalPos())
46
+ super(AddButton, self).mousePressEvent(event)
47
+
48
+ def show_menu(self, index: int, column_idx: int, global_pos):
49
+ """
50
+ Show context menu
51
+
52
+ :param index: index
53
+ :param column_idx: column index
54
+ :param global_pos: global position
55
+ """
56
+ context_menu = self.prepare_menu(index, column_idx)
57
+ context_menu.exec(global_pos)
58
+
59
+ def prepare_menu(self, index: int, column_idx: int) -> QMenu:
60
+ """
61
+ Prepare and return context menu
62
+
63
+ :param index: index
64
+ :param column_idx: column index
65
+ :return: menu
66
+ """
67
+ menu = QMenu(self)
68
+
69
+ actions = {}
70
+ actions['add_chat'] = QAction(QIcon(":/icons/add.svg"), trans('action.tab.add.chat'), self)
71
+ actions['add_chat'].triggered.connect(
72
+ lambda: self.tabs.add_tab(index, column_idx, Tab.TAB_CHAT)
73
+ )
74
+ actions['add_notepad'] = QAction(QIcon(":/icons/add.svg"), trans('action.tab.add.notepad'), self)
75
+ actions['add_notepad'].triggered.connect(
76
+ lambda: self.tabs.add_tab(index, column_idx, Tab.TAB_NOTEPAD)
77
+ )
78
+
79
+ # add chat, add notepad
80
+ menu.addAction(actions['add_chat'])
81
+ menu.addAction(actions['add_notepad'])
82
+
83
+ # add tools submenu
84
+ self.window.controller.tools.append_tab_menu(self, menu, index, column_idx)
85
+
86
+ return menu
87
+
21
88
  class OutputTabs(QTabWidget):
22
- def __init__(self, window=None):
89
+ def __init__(self, window=None, column=None):
23
90
  super(OutputTabs, self).__init__(window)
24
91
  self.window = window
92
+ self.column = column
25
93
  self.setMinimumHeight(1)
26
94
  self.owner = None
27
95
  self.setMovable(True)
96
+ self.init()
97
+
98
+ def init(self):
99
+ """Initialize"""
100
+ # create the [+] button
101
+ add_button = AddButton(self.window, self.column, self)
102
+
103
+ # add the button to the top right corner of the tab bar
104
+ self.setCornerWidget(add_button, corner=Qt.TopRightCorner)
105
+
106
+ # connect signals
107
+ self.currentChanged.connect(
108
+ lambda: self.window.controller.ui.tabs.on_tab_changed(self.currentIndex(), self.column.get_idx())
109
+ )
110
+ self.tabBarClicked.connect(
111
+ lambda: self.window.controller.ui.tabs.on_tab_clicked(self.currentIndex(), self.column.get_idx())
112
+ )
113
+ self.tabBarDoubleClicked.connect(
114
+ lambda: self.window.controller.ui.tabs.on_tab_dbl_clicked(self.currentIndex(), self.column.get_idx())
115
+ )
116
+ self.tabCloseRequested.connect(
117
+ lambda: self.window.controller.ui.tabs.on_tab_closed(self.currentIndex(), self.column.get_idx())
118
+ )
119
+ self.tabBar().tabMoved.connect(
120
+ lambda: self.window.controller.ui.tabs.on_tab_moved(self.currentIndex(), self.column.get_idx())
121
+ )
122
+
123
+ def get_column(self):
124
+ """
125
+ Get column
126
+
127
+ :return: OutputColumn
128
+ """
129
+ return self.column
28
130
 
29
131
  def setOwner(self, owner: Tab):
30
132
  """
31
- Set parent
133
+ Set internal tab instance
32
134
 
33
135
  :param owner: parent tab instance
34
136
  """
@@ -42,158 +144,210 @@ class OutputTabs(QTabWidget):
42
144
  """
43
145
  if event.button() == Qt.RightButton:
44
146
  idx = self.tabBar().tabAt(event.pos())
45
- tab = self.window.core.tabs.get_tab_by_index(idx)
147
+ column_idx = self.column.get_idx()
148
+ tab = self.window.core.tabs.get_tab_by_index(idx, column_idx)
46
149
  if tab is not None:
47
150
  if tab.type == Tab.TAB_NOTEPAD:
48
- self.show_notepad_menu(idx, event.globalPos()) # notepad
151
+ self.show_notepad_menu(idx, column_idx, event.globalPos()) # notepad
49
152
  elif tab.type == Tab.TAB_CHAT:
50
- self.show_chat_menu(idx, event.globalPos()) # chat
153
+ self.show_chat_menu(idx, column_idx, event.globalPos()) # chat
51
154
  elif tab.type == Tab.TAB_FILES:
52
- self.show_files_menu(idx, event.globalPos()) # files
155
+ self.show_files_menu(idx, column_idx, event.globalPos()) # files
156
+ elif tab.type == Tab.TAB_TOOL:
157
+ self.show_tool_menu(idx, column_idx, event.globalPos()) # tool
53
158
  else:
54
- self.show_default_menu(idx, event.globalPos()) # default
159
+ self.show_default_menu(idx, column_idx, event.globalPos()) # default
55
160
  super(OutputTabs, self).mousePressEvent(event)
56
161
 
57
- def get_common_actions(self, index):
162
+ def prepare_menu(self, index: int, column_idx: int) -> QMenu:
58
163
  """
59
- Get common actions
164
+ Prepare and return context menu
60
165
 
61
166
  :param index: index
62
- :return: dict
167
+ :param column_idx: column index
168
+ :return: menu
63
169
  """
170
+ menu = QMenu(self)
171
+
64
172
  actions = {}
65
173
  actions['add_chat'] = QAction(QIcon(":/icons/add.svg"), trans('action.tab.add.chat'), self)
66
174
  actions['add_chat'].triggered.connect(
67
- lambda: self.add_tab(index, Tab.TAB_CHAT)
175
+ lambda: self.add_tab(index, column_idx, Tab.TAB_CHAT)
68
176
  )
69
177
  actions['add_notepad'] = QAction(QIcon(":/icons/add.svg"), trans('action.tab.add.notepad'), self)
70
178
  actions['add_notepad'].triggered.connect(
71
- lambda: self.add_tab(index, Tab.TAB_NOTEPAD)
179
+ lambda: self.add_tab(index, column_idx, Tab.TAB_NOTEPAD)
72
180
  )
73
181
  actions['edit'] = QAction(QIcon(":/icons/edit.svg"), trans('action.rename'), self)
74
182
  actions['edit'].triggered.connect(
75
- lambda: self.rename_tab(index)
183
+ lambda: self.rename_tab(index, column_idx)
184
+ )
185
+ actions['move_right'] = QAction(QIcon(":/icons/forward"), trans('action.tab.move.right'), self)
186
+ actions['move_right'].triggered.connect(
187
+ lambda: self.window.controller.ui.tabs.move_tab(index, column_idx, 1)
188
+ )
189
+ actions['move_left'] = QAction(QIcon(":/icons/back"), trans('action.tab.move.left'), self)
190
+ actions['move_left'].triggered.connect(
191
+ lambda: self.window.controller.ui.tabs.move_tab(index, column_idx, 0)
76
192
  )
77
- return actions
78
193
 
79
- def show_notepad_menu(self, index, global_pos):
194
+ # add chat, add notepad
195
+ menu.addAction(actions['add_chat'])
196
+ menu.addAction(actions['add_notepad'])
197
+
198
+ # add tools submenu
199
+ self.window.controller.tools.append_tab_menu(self, menu, index, column_idx)
200
+
201
+ # rename tab
202
+ menu.addAction(actions['edit'])
203
+
204
+ # move tab left, move tab right
205
+ if column_idx != 0:
206
+ menu.addAction(actions['move_left'])
207
+ if column_idx != 1:
208
+ menu.addAction(actions['move_right'])
209
+
210
+ return menu
211
+
212
+ def show_notepad_menu(self, index: int, column_idx: int, global_pos):
80
213
  """
81
214
  Show notepad menu
82
215
 
83
216
  :param index: index
217
+ :param column_idx: column index
84
218
  :param global_pos: global position
85
219
  """
86
- context_menu = QMenu()
87
- actions = self.get_common_actions(index)
220
+ context_menu = self.prepare_menu(index, column_idx)
221
+ actions = {}
88
222
  actions['close'] = QAction(QIcon(":/icons/close.svg"), trans('action.tab.close'), self)
89
223
  actions['close'].triggered.connect(
90
- lambda: self.close_tab(index)
224
+ lambda: self.close_tab(index, column_idx)
91
225
  )
92
226
  actions['close_all'] = QAction(QIcon(":/icons/close.svg"), trans('action.tab.close_all.notepad'), self)
93
227
  actions['close_all'].triggered.connect(
94
- lambda: self.close_all(Tab.TAB_NOTEPAD)
228
+ lambda: self.close_all(Tab.TAB_NOTEPAD, column_idx)
95
229
  )
96
- context_menu.addAction(actions['add_chat'])
97
- context_menu.addAction(actions['add_notepad'])
98
- context_menu.addAction(actions['edit'])
99
230
  context_menu.addAction(actions['close'])
100
231
 
101
232
  if self.window.core.tabs.count_by_type(Tab.TAB_NOTEPAD) > 1:
102
233
  context_menu.addAction(actions['close_all'])
234
+
103
235
  context_menu.exec(global_pos)
104
236
 
105
- def show_chat_menu(self, index, global_pos):
237
+ def show_chat_menu(self, index: int, column_idx: int, global_pos):
106
238
  """
107
239
  Show chat menu
108
240
 
109
241
  :param index: index
242
+ :param column_idx: column index
110
243
  :param global_pos: global position
111
244
  """
112
- context_menu = QMenu()
113
- actions = self.get_common_actions(index)
245
+ context_menu = self.prepare_menu(index, column_idx)
246
+ actions = {}
114
247
  actions['close'] = QAction(QIcon(":/icons/close.svg"), trans('action.tab.close'), self)
115
248
  actions['close'].triggered.connect(
116
- lambda: self.close_tab(index)
249
+ lambda: self.close_tab(index, column_idx)
117
250
  )
118
251
  actions['close_all'] = QAction(QIcon(":/icons/close.svg"), trans('action.tab.close_all.chat'), self)
119
252
  actions['close_all'].triggered.connect(
120
- lambda: self.close_all(Tab.TAB_CHAT)
253
+ lambda: self.close_all(Tab.TAB_CHAT, column_idx)
121
254
  )
122
- context_menu.addAction(actions['add_chat'])
123
- context_menu.addAction(actions['add_notepad'])
124
- context_menu.addAction(actions['edit'])
125
255
 
126
256
  # at least one chat tab must be open
127
257
  if self.window.core.tabs.count_by_type(Tab.TAB_CHAT) > 1:
128
258
  context_menu.addAction(actions['close'])
129
-
130
- if self.window.core.tabs.count_by_type(Tab.TAB_CHAT) > 1:
131
259
  context_menu.addAction(actions['close_all'])
132
260
 
133
261
  context_menu.exec(global_pos)
134
262
 
135
- def show_files_menu(self, index, global_pos):
263
+ def show_files_menu(self, index: int, column_idx: int, global_pos):
136
264
  """
137
265
  Show files menu
138
266
 
139
267
  :param index: index
268
+ :param column_idx: column index
140
269
  :param global_pos: global position
141
270
  """
142
- context_menu = QMenu()
143
- actions = self.get_common_actions(index)
271
+ context_menu = self.prepare_menu(index, column_idx)
272
+ actions = {}
144
273
  actions['refresh'] = QAction(QIcon(":/icons/reload.svg"), trans('action.refresh'), self)
145
274
  actions['refresh'].triggered.connect(
146
275
  lambda: self.window.controller.files.update_explorer()
147
276
  )
148
- context_menu.addAction(actions['add_chat'])
149
- context_menu.addAction(actions['add_notepad'])
150
277
  context_menu.addAction(actions['refresh'])
151
- context_menu.addAction(actions['edit'])
152
278
  context_menu.exec(global_pos)
153
279
 
154
- def show_default_menu(self, index, global_pos):
280
+ def show_tool_menu(self, index: int, column_idx: int, global_pos):
281
+ """
282
+ Show tool menu
283
+
284
+ :param index: index
285
+ :param column_idx: column index
286
+ :param global_pos: global position
287
+ """
288
+ context_menu = self.prepare_menu(index, column_idx)
289
+ actions = {}
290
+ actions['close'] = QAction(QIcon(":/icons/close.svg"), trans('action.tab.close'), self)
291
+ actions['close'].triggered.connect(
292
+ lambda: self.close_tab(index, column_idx)
293
+ )
294
+ context_menu.addAction(actions['close'])
295
+ context_menu.exec(global_pos)
296
+
297
+ def show_default_menu(self, index: int, column_idx: int, global_pos):
155
298
  """
156
299
  Show default menu
157
300
 
158
301
  :param index: index
302
+ :param column_idx: column index
159
303
  :param global_pos: global position
160
304
  """
161
- context_menu = QMenu()
162
- actions = self.get_common_actions(index)
163
- context_menu.addAction(actions['add_chat'])
164
- context_menu.addAction(actions['add_notepad'])
165
- context_menu.addAction(actions['edit'])
305
+ context_menu = self.prepare_menu(index, column_idx)
166
306
  context_menu.exec(global_pos)
167
307
 
168
308
  @Slot()
169
- def rename_tab(self, index):
309
+ def rename_tab(self, index: int, column_idx: int):
170
310
  """
171
311
  Rename tab
312
+
172
313
  :param index: index
314
+ :param column_idx: column index
173
315
  """
174
- self.window.controller.ui.tabs.rename(index)
316
+ self.window.controller.ui.tabs.rename(index, column_idx)
175
317
 
176
318
  @Slot()
177
- def close_tab(self, index):
319
+ def close_tab(self, index: int, column_idx: int):
178
320
  """
179
321
  Close tab
322
+
180
323
  :param index: index
324
+ :param column_idx: column index
181
325
  """
182
- self.window.controller.ui.tabs.close(index)
326
+ self.window.controller.ui.tabs.close(index, column_idx)
183
327
 
184
328
  @Slot()
185
- def close_all(self, type):
329
+ def close_all(self, type, column_idx: int):
186
330
  """
187
331
  Close all tabs
332
+
188
333
  :param type: type
334
+ :param column_idx: column index
189
335
  """
190
- self.window.controller.ui.tabs.close_all(type)
336
+ self.window.controller.ui.tabs.close_all(type, column_idx)
191
337
 
192
338
  @Slot()
193
- def add_tab(self, index, type):
339
+ def add_tab(self, index: int, column_idx: int, type: int, tool_id: str = None):
194
340
  """
195
- Add tab
341
+ Add a new tab
342
+
196
343
  :param index: index
344
+ :param column_idx: column index
197
345
  :param type: type
346
+ :param tool_id: tool id
198
347
  """
199
- self.window.controller.ui.tabs.append(type, index)
348
+ self.window.controller.ui.tabs.append(
349
+ type=type,
350
+ tool_id=tool_id,
351
+ idx=index,
352
+ column_idx=column_idx,
353
+ )