Merge lp:~abudden/qbzr/tab-width-on-view-menu into lp:qbzr
- tab-width-on-view-menu
- Merge into trunk2a
Status: | Merged |
---|---|
Approved by: | Alexander Belchenko |
Approved revision: | 1375 |
Merge reported by: | Dr Al |
Merged at revision: | not available |
Proposed branch: | lp:~abudden/qbzr/tab-width-on-view-menu |
Merge into: | lp:qbzr |
Diff against target: |
375 lines (+207/-22) 7 files modified
NEWS.txt (+6/-2) lib/annotate.py (+10/-2) lib/config.py (+2/-2) lib/diffwindow.py (+62/-4) lib/util.py (+41/-12) lib/widgets/__init__.py (+1/-0) lib/widgets/tab_width_selector.py (+85/-0) |
To merge this branch: | bzr merge lp:~abudden/qbzr/tab-width-on-view-menu |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alexander Belchenko | Needs Fixing | ||
Review via email: mp+59334@code.launchpad.net |
Commit message
Description of the change
> Hi Alan,
>
> I've just thought that you may want to add support to change Tab Width directly for GUI: via View Options menu on toolbar (in qdiff and qannotate) similar to encoding change, if you like. You don't have to though. That's just a thought.
Implemented in this branch.
Alexander Belchenko (bialix) wrote : | # |
Alexander Belchenko (bialix) : | # |
Dr Al (abudden) wrote : | # |
NEWS moved and branch.conf saving implemented.
Alexander Belchenko (bialix) wrote : | # |
Your both util functions are extended to get aditional parameter tab_width, but their signatures different:
def get_set_
vs
def get_tab_
As I undersstand in both cases you might pass optional tab_width in chars. I think we should use the same order of arguments in both functions to reduce the possible mistakes in usage of that API. I'm asking you for consistency between those 2 functions. Can you fix this please?
Dr Al (abudden) wrote : | # |
Fixed. I've also put more explanation into the docstrings since they're util functions.
Alexander Belchenko (bialix) wrote : | # |
Thanks for descriptive docstring.
Alexander Belchenko (bialix) wrote : | # |
Okay, we need couple of small fixes there.
1) Please put the new file under lib/widgets/ subdirectory (it does not exist yet in trun, so add it). I've long imagined how to improve qbzr internal modules layout. So let's start with it. See https:/
2) It seems the import of from bzrlib.
Otherwise it's fine for me.
Dr Al (abudden) wrote : | # |
Okay, that's done.
Alexander Belchenko (bialix) wrote : | # |
Dr Al пишет:
> Okay, that's done.
You should fix the imports in the modules that requires new TabSelector.
Also, please keep 2 blank lines between NEWS groups for different releases.
Othewrwise, I'm fine with that. Please land.
--
All the dude wanted was his rug back
Alexander Belchenko (bialix) : | # |
Dr Al (abudden) wrote : | # |
Fixed the imports (oops) and re-added blank lines. I can't land this.
Dr Al (abudden) wrote : | # |
Merged.
Preview Diff
1 | === modified file 'NEWS.txt' |
2 | --- NEWS.txt 2011-05-03 09:21:56 +0000 |
3 | +++ NEWS.txt 2011-05-11 11:51:06 +0000 |
4 | @@ -5,6 +5,10 @@ |
5 | * Fixed problem with viewing file from qbrowse. |
6 | (Alexander Belchenko, Bug #776196) |
7 | |
8 | + * qdiff, qannotate: |
9 | + Tab-width can be customised from the view menu. |
10 | + (Bug #490377, A. S. Budden) |
11 | + |
12 | |
13 | 0.21 beta 1 - 2011/05/02 |
14 | ------------------------ |
15 | @@ -63,16 +67,16 @@ |
16 | * qannotate, qdiff: |
17 | * Find text box turns red if no matches are found. |
18 | (A. S. Budden, Bug #772244) |
19 | + |
20 | * qcat, qannotate, qdiff, qconfig: |
21 | * Added ability to customise the tab-stop width (setting in qconfig, |
22 | - affects qcat, qannotate and qdiff). |
23 | + affects qcat, qannotate and qdiff). |
24 | The setting is stored in [DEFAULT] section of bazaar.conf, |
25 | and is named tab_width (it can also be configured with qconfig). Units |
26 | are characters (so 4 means a tab should be displayed with the width of 4 |
27 | spaces). The default value is 8. The setting can also be adjusted in |
28 | branch.conf for specific branches. (Bug #490377, A. S. Budden) |
29 | |
30 | - |
31 | 0.20.1 - 2011/04/26 |
32 | -------------------- |
33 | Maintenance release. |
34 | |
35 | === modified file 'lib/annotate.py' |
36 | --- lib/annotate.py 2011-04-27 14:13:50 +0000 |
37 | +++ lib/annotate.py 2011-05-11 11:51:06 +0000 |
38 | @@ -37,6 +37,7 @@ |
39 | get_icon, |
40 | get_monospace_font, |
41 | get_set_encoding, |
42 | + get_set_tab_width_chars, |
43 | get_tab_width_pixels, |
44 | runs_in_loading_queue, |
45 | ) |
46 | @@ -53,6 +54,7 @@ |
47 | from bzrlib.revisiontree import RevisionTree |
48 | from bzrlib.plugins.qbzr.lib.revisionmessagebrowser import LogListRevisionMessageBrowser |
49 | from bzrlib.plugins.qbzr.lib.encoding_selector import EncodingMenuSelector |
50 | +from bzrlib.plugins.qbzr.lib.widgets.tab_width_selector import TabWidthMenuSelector |
51 | from bzrlib.plugins.qbzr.lib.syntaxhighlighter import highlight_document |
52 | from bzrlib.plugins.qbzr.lib.revtreeview import paint_revno, get_text_color |
53 | from bzrlib.plugins.qbzr.lib import logmodel |
54 | @@ -269,8 +271,6 @@ |
55 | self.text_edit.setLineWrapMode(QtGui.QPlainTextEdit.NoWrap) |
56 | |
57 | self.text_edit.document().setDefaultFont(get_monospace_font()) |
58 | - |
59 | - self.text_edit.setTabStopWidth(get_tab_width_pixels(branch)) |
60 | |
61 | self.annotate_bar = AnnotateBar(self.text_edit, self, self.get_revno) |
62 | annotate_spliter = QtGui.QSplitter(QtCore.Qt.Horizontal, self) |
63 | @@ -337,7 +337,15 @@ |
64 | self.connect(word_wrap, |
65 | QtCore.SIGNAL("toggled (bool)"), |
66 | self.word_wrap_toggle) |
67 | + |
68 | + def setTabStopWidth(tw): |
69 | + self.text_edit.setTabStopWidth(get_tab_width_pixels(tab_width_chars=tw)) |
70 | + get_set_tab_width_chars(branch=self.branch,tab_width_chars=tw) |
71 | + self.tab_width_selector = TabWidthMenuSelector(get_set_tab_width_chars(branch=branch), |
72 | + gettext("Tab Width"), |
73 | + setTabStopWidth) |
74 | |
75 | + view_menu.addMenu(self.tab_width_selector) |
76 | view_menu.addMenu(self.encoding_selector) |
77 | view_menu.addAction(word_wrap) |
78 | |
79 | |
80 | === modified file 'lib/config.py' |
81 | --- lib/config.py 2011-04-26 08:26:28 +0000 |
82 | +++ lib/config.py 2011-05-11 11:51:06 +0000 |
83 | @@ -35,7 +35,7 @@ |
84 | extract_name, |
85 | get_qbzr_config, |
86 | get_global_config, |
87 | - get_tab_width_chars, |
88 | + get_set_tab_width_chars, |
89 | ) |
90 | |
91 | |
92 | @@ -305,7 +305,7 @@ |
93 | self.emailClientCombo.setCurrentIndex(index) |
94 | |
95 | # Tab-width |
96 | - self.tabWidthSpinner.setValue(get_tab_width_chars()) |
97 | + self.tabWidthSpinner.setValue(get_set_tab_width_chars()) |
98 | |
99 | # Spellcheck language |
100 | spellcheck_language = config.get_user_option('spellcheck_language') or 'en' |
101 | |
102 | === modified file 'lib/diffwindow.py' |
103 | --- lib/diffwindow.py 2011-04-29 10:12:04 +0000 |
104 | +++ lib/diffwindow.py 2011-05-11 11:51:06 +0000 |
105 | @@ -54,6 +54,7 @@ |
106 | ToolBarThrobberWidget, |
107 | get_icon, |
108 | get_set_encoding, |
109 | + get_set_tab_width_chars, |
110 | get_tab_width_pixels, |
111 | is_binary_content, |
112 | run_in_loading_queue, |
113 | @@ -63,6 +64,7 @@ |
114 | from bzrlib.plugins.qbzr.lib.uifactory import ui_current_widget |
115 | from bzrlib.plugins.qbzr.lib.trace import reports_exception |
116 | from bzrlib.plugins.qbzr.lib.encoding_selector import EncodingMenuSelector |
117 | +from bzrlib.plugins.qbzr.lib.widgets.tab_width_selector import TabWidthMenuSelector |
118 | |
119 | try: |
120 | from bzrlib.errors import FileTimestampUnavailable |
121 | @@ -154,6 +156,11 @@ |
122 | vbox = QtGui.QVBoxLayout(self.centralwidget) |
123 | vbox.addWidget(self.stack) |
124 | |
125 | + # Don't use a custom tab width by default |
126 | + # Indices are left side, right side and unidiff |
127 | + # respectively |
128 | + self.custom_tab_widths = [-1,-1,-1] |
129 | + |
130 | for browser in self.diffview.browsers: |
131 | browser.installEventFilter(self) |
132 | |
133 | @@ -273,6 +280,42 @@ |
134 | self.ignore_whitespace_action = self.create_ignore_ws_action() |
135 | view_menu.addAction(self.ignore_whitespace_action) |
136 | |
137 | + def on_unidiff_tab_width_changed(tabwidth): |
138 | + if self.branches: |
139 | + get_set_tab_width_chars(branch=self.branches[0],tab_width_chars=tabwidth) |
140 | + self.custom_tab_widths[2] = tabwidth |
141 | + self.setup_tab_width() |
142 | + self.tab_width_selector_unidiff = TabWidthMenuSelector( |
143 | + label_text=gettext("Tab width"), |
144 | + onChanged=on_unidiff_tab_width_changed) |
145 | + view_menu.addMenu(self.tab_width_selector_unidiff) |
146 | + |
147 | + def on_left_tab_width_changed(tabwidth): |
148 | + if self.branches: |
149 | + get_set_tab_width_chars(branch=self.branches[0],tab_width_chars=tabwidth) |
150 | + self.custom_tab_widths[0] = tabwidth |
151 | + self.setup_tab_width() |
152 | + self.tab_width_selector_left = TabWidthMenuSelector( |
153 | + label_text=gettext("Left side tab width"), |
154 | + onChanged=on_left_tab_width_changed) |
155 | + view_menu.addMenu(self.tab_width_selector_left) |
156 | + |
157 | + def on_right_tab_width_changed(tabwidth): |
158 | + if self.branches: |
159 | + get_set_tab_width_chars(branch=self.branches[1],tab_width_chars=tabwidth) |
160 | + self.custom_tab_widths[1] = tabwidth |
161 | + self.setup_tab_width() |
162 | + self.tab_width_selector_right = TabWidthMenuSelector( |
163 | + label_text=gettext("Right side tab width"), |
164 | + onChanged=on_right_tab_width_changed) |
165 | + view_menu.addMenu(self.tab_width_selector_right) |
166 | + |
167 | + if self.stack.currentWidget() == self.diffview: |
168 | + self.tab_width_selector_unidiff.menuAction().setVisible(False) |
169 | + else: |
170 | + self.tab_width_selector_left.menuAction().setVisible(False) |
171 | + self.tab_width_selector_right.menuAction().setVisible(False) |
172 | + |
173 | def on_left_encoding_changed(encoding): |
174 | if self.branches: |
175 | get_set_encoding(encoding, self.branches[0]) |
176 | @@ -371,10 +414,19 @@ |
177 | self.processEvents() |
178 | |
179 | def setup_tab_width(self): |
180 | - tabWidths = (get_tab_width_pixels(self.branches[0]), |
181 | - get_tab_width_pixels(self.branches[1])) |
182 | - self.diffview.setTabStopWidths(tabWidths) |
183 | - self.sdiffview.setTabStopWidth(tabWidths[0]) |
184 | + tabWidths = self.custom_tab_widths |
185 | + if tabWidths[0] < 0: |
186 | + tabWidths[0] = get_set_tab_width_chars(branch=self.branches[0]) |
187 | + self.tab_width_selector_left.setTabWidth(tabWidths[0]) |
188 | + if tabWidths[1] < 0: |
189 | + tabWidths[1] = get_set_tab_width_chars(branch=self.branches[1]) |
190 | + self.tab_width_selector_right.setTabWidth(tabWidths[1]) |
191 | + if tabWidths[2] < 0: |
192 | + tabWidths[2] = get_set_tab_width_chars(branch=self.branches[0]) |
193 | + self.tab_width_selector_unidiff.setTabWidth(tabWidths[2]) |
194 | + tabWidthsPixels = [get_tab_width_pixels(tab_width_chars=i) for i in tabWidths] |
195 | + self.diffview.setTabStopWidths(tabWidthsPixels) |
196 | + self.sdiffview.setTabStopWidth(tabWidthsPixels[2]) |
197 | |
198 | def load_diff(self): |
199 | self.view_refresh.setEnabled(False) |
200 | @@ -532,9 +584,15 @@ |
201 | if checked: |
202 | view = self.sdiffview |
203 | self.find_toolbar.text_edit = view |
204 | + self.tab_width_selector_left.menuAction().setVisible(False) |
205 | + self.tab_width_selector_right.menuAction().setVisible(False) |
206 | + self.tab_width_selector_unidiff.menuAction().setVisible(True) |
207 | else: |
208 | view = self.diffview |
209 | self.find_toolbar.text_edit = view.browsers[0] |
210 | + self.tab_width_selector_left.menuAction().setVisible(True) |
211 | + self.tab_width_selector_right.menuAction().setVisible(True) |
212 | + self.tab_width_selector_unidiff.menuAction().setVisible(False) |
213 | view.rewind() |
214 | index = self.stack.indexOf(view) |
215 | self.stack.setCurrentIndex(index) |
216 | |
217 | === modified file 'lib/util.py' |
218 | --- lib/util.py 2011-05-07 15:50:46 +0000 |
219 | +++ lib/util.py 2011-05-11 11:51:06 +0000 |
220 | @@ -1232,17 +1232,46 @@ |
221 | font.setFixedPitch(True) |
222 | return font |
223 | |
224 | -def get_tab_width_chars(branch=None): |
225 | - """Function to get the tab width in characters from the configuration.""" |
226 | - config = get_branch_config(branch) |
227 | - try: |
228 | - tabWidth = int(config.get_user_option('tab_width')) |
229 | - except TypeError: |
230 | - tabWidth = 8 |
231 | - return tabWidth |
232 | - |
233 | -def get_tab_width_pixels(branch=None): |
234 | - """Function to get the tab width in pixels based on a monospaced font.""" |
235 | +def get_set_tab_width_chars(branch=None, tab_width_chars=None): |
236 | + """Function to get the tab width in characters from the configuration. |
237 | + |
238 | + @param branch: Use branch.conf as well as bazaar.conf if this is provided. |
239 | + @param tab_width_chars: Number of characters to use as tab width: if branch |
240 | + is provided, the tab width will be stored in branch.conf |
241 | + |
242 | + Both arguments are optional, but if tab_width_chars is provided and branch is |
243 | + not, nothing will be done. |
244 | + |
245 | + @return: Tab width, in characters. |
246 | + """ |
247 | + if tab_width_chars is None: |
248 | + config = get_branch_config(branch) |
249 | + try: |
250 | + tab_width_chars = int(config.get_user_option('tab_width')) |
251 | + if tab_width_chars < 0: |
252 | + raise TypeError("Invalid tab width") |
253 | + except TypeError: |
254 | + tab_width_chars = 8 |
255 | + else: |
256 | + if branch: |
257 | + branch.get_config().set_user_option("tab_width", tab_width_chars) |
258 | + |
259 | + return tab_width_chars |
260 | + |
261 | +def get_tab_width_pixels(branch=None, tab_width_chars=None): |
262 | + """Function to get the tab width in pixels based on a monospaced font. |
263 | + |
264 | + If tab_width_chars is provided, it is simply converted to a value in pixels. If |
265 | + it is not provided, the configuration is retrieved from bazaar.conf. If branch is |
266 | + provided (and tab_width_chars is not), branch.conf is also checked. |
267 | + |
268 | + @param tab_width_chars: Number of characters of tab width to convert to pixels. |
269 | + @param branch: Branch to use when retrieving tab width from configuration. |
270 | + |
271 | + @return: Tab width, in pixels. |
272 | + """ |
273 | monospacedFont = get_monospace_font() |
274 | char_width = QtGui.QFontMetrics(monospacedFont).width(" ") |
275 | - return char_width*get_tab_width_chars(branch) |
276 | + if tab_width_chars is None: |
277 | + tab_width_chars = get_set_tab_width_chars(branch=branch) |
278 | + return char_width*tab_width_chars |
279 | |
280 | === added directory 'lib/widgets' |
281 | === added file 'lib/widgets/__init__.py' |
282 | --- lib/widgets/__init__.py 1970-01-01 00:00:00 +0000 |
283 | +++ lib/widgets/__init__.py 2011-05-11 11:51:06 +0000 |
284 | @@ -0,0 +1,1 @@ |
285 | +"" |
286 | |
287 | === added file 'lib/widgets/tab_width_selector.py' |
288 | --- lib/widgets/tab_width_selector.py 1970-01-01 00:00:00 +0000 |
289 | +++ lib/widgets/tab_width_selector.py 2011-05-11 11:51:06 +0000 |
290 | @@ -0,0 +1,85 @@ |
291 | +# -*- coding: utf-8 -*- |
292 | +# |
293 | +# QBzr - Qt frontend to Bazaar commands |
294 | +# |
295 | +# This program is free software; you can redistribute it and/or |
296 | +# modify it under the terms of the GNU General Public License |
297 | +# as published by the Free Software Foundation; either version 2 |
298 | +# of the License, or (at your option) any later version. |
299 | +# |
300 | +# This program is distributed in the hope that it will be useful, |
301 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
302 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
303 | +# GNU General Public License for more details. |
304 | +# |
305 | +# You should have received a copy of the GNU General Public License |
306 | +# along with this program; if not, write to the Free Software |
307 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
308 | + |
309 | +from PyQt4 import QtGui, QtCore |
310 | + |
311 | +# Range of tab widths to display by default on the menu (if others are |
312 | +# specified in either bazaar.conf or branch.conf they'll be added after |
313 | +# a separator). |
314 | +MIN_TAB_WIDTH = 1 |
315 | +MAX_TAB_WIDTH = 12 |
316 | + |
317 | +class TabWidthMenuSelector(QtGui.QMenu): |
318 | + """Menu to control tab width.""" |
319 | + def __init__(self, initial_tab_width=None, label_text=None, onChanged=None, *args): |
320 | + """Create tab width menu. |
321 | + @param label_text: text for label. |
322 | + @param onChanged: callback to processing tab width change. |
323 | + """ |
324 | + QtGui.QMenu.__init__(self, *args) |
325 | + self.onChanged = onChanged |
326 | + if onChanged is None: |
327 | + self.onChanged = lambda settabwidth: None |
328 | + |
329 | + self.setTitle(label_text) |
330 | + |
331 | + self.action_group = QtGui.QActionGroup(self) |
332 | + |
333 | + self.tabwidth_actions = {} |
334 | + for tabwidth in range(MIN_TAB_WIDTH, MAX_TAB_WIDTH+1): |
335 | + action = QtGui.QAction(str(tabwidth), self.action_group) |
336 | + action.setCheckable(True) |
337 | + action.setData(QtCore.QVariant(tabwidth)) |
338 | + self.addAction(action) |
339 | + self.tabwidth_actions[tabwidth] = action |
340 | + |
341 | + self._tabwidth = None |
342 | + self._has_separator = False |
343 | + self.connect(self, QtCore.SIGNAL("triggered(QAction *)"), |
344 | + self.triggered) |
345 | + |
346 | + if initial_tab_width is not None: |
347 | + self.setTabWidth(initial_tab_width) |
348 | + self.triggered(self.tabwidth_actions[initial_tab_width]) |
349 | + |
350 | + def triggered(self, action): |
351 | + tw, success = action.data().toInt() |
352 | + if success and tw != self._tabwidth: |
353 | + self._tabwidth = tw |
354 | + self.onChanged(tw) |
355 | + |
356 | + def setTabWidth(self, width): |
357 | + if width not in self.tabwidth_actions: |
358 | + action = QtGui.QAction(str(width), self.action_group) |
359 | + action.setCheckable(True) |
360 | + action.setData(QtCore.QVariant(width)) |
361 | + # Find the next highest tab width currently in the menu |
362 | + for tw in sorted(self.tabwidth_actions.keys()): |
363 | + if tw > width: |
364 | + self.insertAction(action, self.tabwidth_actions[tw]) |
365 | + break |
366 | + else: |
367 | + # Not found |
368 | + if not self._has_separator: |
369 | + self.addSeparator() |
370 | + self._has_separator = True |
371 | + self.addAction(action) |
372 | + |
373 | + self.tabwidth_actions[width] = action |
374 | + |
375 | + self.tabwidth_actions[width].setChecked(True) |
You need to update your NEWS changes to 0.20b2 cycle now.
Also, I wonder why don't you want to save the changed value in corresponding branch.conf as encoding selector does? See get_set_encoding() function in util.py as reference (but please use the latest revision from lp:qbzr).