Merge lp:~beutlin/zim/table-plugin into lp:~jaap.karssenberg/zim/pyzim
- table-plugin
- Merge into pyzim
Status: | Merged |
---|---|
Merged at revision: | 758 |
Proposed branch: | lp:~beutlin/zim/table-plugin |
Merge into: | lp:~jaap.karssenberg/zim/pyzim |
Diff against target: |
5325 lines (+2737/-441) 27 files modified
data/manual/Plugins.txt (+1/-0) data/manual/Plugins/Journal.txt (+1/-1) data/manual/Plugins/Table_Editor.txt (+21/-0) data/manual/Plugins/Table_Editor/Basic_Table_Operations.txt (+41/-0) data/manual/Plugins/Table_Editor/Superior_Table_Operations.txt (+46/-0) tests/__init__.py (+1/-1) tests/data/formats/export.html (+25/-0) tests/data/formats/export.markdown (+8/-0) tests/data/formats/export.rst (+12/-0) tests/data/formats/export.tex (+16/-0) tests/data/formats/parsetree.xml (+2/-0) tests/data/formats/plain.txt (+12/-0) tests/data/formats/wiki.txt (+7/-0) tests/objectmanager.py (+1/-1) tests/tableeditor.py (+84/-0) translations/zim.pot (+603/-415) zim/formats/__init__.py (+167/-1) zim/formats/html.py (+45/-0) zim/formats/latex.py (+24/-0) zim/formats/markdown.py (+28/-1) zim/formats/plain.py (+33/-0) zim/formats/rst.py (+33/-0) zim/formats/wiki.py (+125/-3) zim/gui/objectmanager.py (+1/-1) zim/gui/pageview.py (+233/-13) zim/objectmanager.py (+8/-4) zim/plugins/tableeditor.py (+1159/-0) |
To merge this branch: | bzr merge lp:~beutlin/zim/table-plugin |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tobias Haupenthal (community) | Needs Resubmitting | ||
Jaap Karssenberg | Needs Information | ||
Review via email: mp+251673@code.launchpad.net |
Commit message
Description of the change
Hello,
currently I've created a table plugin for zim and wish to get this into the main branch.
Please have a look at it.
Tobias Haupenthal
Murat Güven (murat-gueven) wrote : | # |
Jaap Karssenberg (jaap.karssenberg) wrote : | # |
Hi Tobias,
Just checked out your branch, and my first reaction is that I'm highly impressed with the work you put into this - it really looks very polished.
I have a few minor change requests that I will come back on soon. In the mean time I will start testing and reviewing the code.
I feel this feature alone is reason enough to publish a new release asap.
Regards,
Jaap
Jaap Karssenberg (jaap.karssenberg) wrote : | # |
Hi Tobias,
After studying the code in more detail I think it can be merged right away.
My only point of discussion is the format of tables in the default wiki format. I wonder of the choice of using the "{{{table ... }}}" object syntax is the right choice. Especially sine you build the table format more as a core structure into the parser and only the widget is in the plugin.
My proposal then would be to support tables in the native wiki syntax without the "{{{ ... }}}" object wrapper. I would propose to use the same format as in markdown [1] and possibly also support the wiki creole syntax [2]. Fine to allow reading the object syntax, but when writing we should just use the native format.
Wanted to make the change myself and then merge. But I don't know what to do with the "wraps" attribute. What would be a logical syntax to add that in a markdown like table format ?
Regards,
Jaap
[1] https:/
[2] http://
Tobias Haupenthal (beutlin) wrote : | # |
Hi Jaap,
thank you for your feedback.
One of the most important aspects is indeed the syntax for the table.
A futural feature might be including external files directly. With the object-oriented form I could use {{{table file=".
Do you have any good idea to circumvent that issue? Maybe using a different plugin?
Wrapping mode can be implemented with two colons, whereas normal mode is one colon.
center - left - right alignment
| h1 | h2 | h3 |
|::-:|::--|--::|
vs.
| h1 | h2 | h3 |
|:--:|:---|---:|
Regards,
Tobias
Jaap Karssenberg (jaap.karssenberg) wrote : | # |
Hi Tobias,
Using markdown syntax now for inline tables will not prevent you from using
object syntax later for extensions line including CSV files. In fact, I
would highly encourage going that way :)
In my vision, at some point of time there will be two table widgets. One
for "data", and one for "grid" tables. The widget you wrote based on the
gtk Treeview is a very good start for a "data" table. It would be perfect
for rendering CSV, and you could extend it by e.g. defining data types per
column (boolean checkboxes, integers with spinner, etc.).
The "grid" table will be really text driven and could allow things like
row-span and col-span which a "data" table will not have. I think the
inline markdown format is really a "grid" table. However since we only have
your widget for now, we use that for all tables. (Later on it could still
be a user preference what table rendering to use by default.)
So I would support inline markdown tables and use that as the default for
"all text" tables. For CSV and similar data, definitely the object syntax
is the right thing to do. I would recommend to use the object type
"data-table" instead of "table", so we can differentiate later. I can even
imagine:
{{{data-tabel format=csv
head1,head2,head3
..., ..., ...
}}}
The plugin could just define both "table" and "data-table" and write back
in a different format depending on the object type originally defined.
For the wrapping I was thinking to annotate the header line, that keeps the
format backward compatible with markdown rendering.
E.g.
| h1 < | h2 | h3 |
|:-:|:--|--:|
Where the "<" after a heading means wrap. Maybe you even want to go with
"<10" for wrapping at (approximately) 10 characters width.
My rationale for this is that I have plans to support Markdown as native
format as well, so I try to do stuff in a more or less compatible way. With
this syntax a markdown parser will still render the table, be it with the
annotation still visible in the heading.
Does that make sense - or am I making things to complicated?
Regards,
Jaap
On Mon, Mar 23, 2015 at 7:39 PM, Tobias Haupenthal <email address hidden>
wrote:
> Hi Jaap,
> thank you for your feedback.
>
> One of the most important aspects is indeed the syntax for the table.
> A futural feature might be including external files directly. With the
> object-oriented form I could use {{{table file=".
> simple markdown syntax it could be nearly impossible.
> Do you have any good idea to circumvent that issue? Maybe using a
> different plugin?
>
> Wrapping mode can be implemented with two colons, whereas normal mode is
> one colon.
> center - left - right alignment
>
> | h1 | h2 | h3 |
> |::-:|::--|--::|
>
> vs.
> | h1 | h2 | h3 |
> |:--:|:---|---:|
>
>
> Regards,
> Tobias
>
> --
> https:/
> You are reviewing the proposed merge of lp:~beutlin/zim/table-plugin into
> lp:zim.
>
Tobias Haupenthal (beutlin) wrote : | # |
Hello Jaap,
your mail sounds well thought-out. I like your vision.
Please give me any indication if you prefer that I do these syntax changes.
Many thanks for your code review!
Tobias
Jaap Karssenberg (jaap.karssenberg) wrote : | # |
Hi Tobias,
I was going to do a quick patch myself, but didn't get around it. Will be
offline for the next few days. So if you happen to have some time, please
go ahead and make the patch. Otherwise I'll get back to it in a week or two.
Regards,
Jaap
On Tue, Mar 24, 2015 at 11:14 PM, Tobias Haupenthal <email address hidden>
wrote:
> Hello Jaap,
> your mail sounds well thought-out. I like your vision.
> Please give me any indication if you prefer that I do these syntax changes.
>
> Many thanks for your code review!
>
> Tobias
> --
> https:/
> You are reviewing the proposed merge of lp:~beutlin/zim/table-plugin into
> lp:zim.
>
- 759. By Tobias Haupenthal <email address hidden>
-
sourcecode of table now markdown-like; wrapping and alignment syntax changed
Tobias Haupenthal (beutlin) wrote : | # |
Hi Jaap,
the branch was updated for the new markdown related syntax.
Maybe you can have a look at it again.
Regards,
Tobias
Jaap Karssenberg (jaap.karssenberg) wrote : | # |
Done
On Tue, Apr 14, 2015 at 8:43 PM, Tobias Haupenthal <email address hidden>
wrote:
> Review: Resubmit
>
> Hi Jaap,
> the branch was updated for the new markdown related syntax.
> Maybe you can have a look at it again.
>
> Regards,
> Tobias
> --
> https:/
> You are reviewing the proposed merge of lp:~beutlin/zim/table-plugin into
> lp:zim.
>
Preview Diff
1 | === modified file 'data/manual/Plugins.txt' |
2 | --- data/manual/Plugins.txt 2014-10-10 19:35:17 +0000 |
3 | +++ data/manual/Plugins.txt 2015-04-04 11:26:41 +0000 |
4 | @@ -27,6 +27,7 @@ |
5 | * [[+Sequence Diagram Editor|Sequence Diagram Editor]] |
6 | * [[+Source View|Source View]] |
7 | * [[+Spell Checker|Spell Checker]] |
8 | +* [[+Table Editor|Table Editor]] |
9 | * [[+Table Of Contents|Table Of Contents]] |
10 | * [[+Tags|Tags]] |
11 | * [[+Task List|Task List]] |
12 | |
13 | === modified file 'data/manual/Plugins/Journal.txt' |
14 | --- data/manual/Plugins/Journal.txt 2014-08-28 13:08:14 +0000 |
15 | +++ data/manual/Plugins/Journal.txt 2015-04-04 11:26:41 +0000 |
16 | @@ -15,7 +15,7 @@ |
17 | This plugin has the following options: |
18 | |
19 | The option **Show calendar in sidepane instead of as dialog** does just that; if enabled the calendar widget is embedded at the top of the side pane. Otherwise the calendar is shown in a separate dialog when you click the toolbar button. If the calendar is embedded in the side pane, the option **Position in the window** determines in which side pane the calendar is shown. |
20 | - |
21 | +The option **Expand journal page in index when opened** expands the index-tree if a date is opened. |
22 | The option **Use a page for each: ...** allows you to set the calendar to either use a separate page for each day, each week, each month or even each year. |
23 | |
24 | The **Section** controls where the calendar pages are stored. |
25 | |
26 | === added directory 'data/manual/Plugins/Table_Editor' |
27 | === added file 'data/manual/Plugins/Table_Editor.txt' |
28 | --- data/manual/Plugins/Table_Editor.txt 1970-01-01 00:00:00 +0000 |
29 | +++ data/manual/Plugins/Table_Editor.txt 2015-04-04 11:26:41 +0000 |
30 | @@ -0,0 +1,21 @@ |
31 | +Content-Type: text/x-zim-wiki |
32 | +Wiki-Format: zim 0.4 |
33 | + |
34 | +====== Table Editor ====== |
35 | + |
36 | +{{./table_demo.png}}[Only a picture] |
37 | +This plugin allows you to integrate a table into the zim editor. |
38 | +**Dependencies:** This plugin has no additional dependencies |
39 | + |
40 | +===== Options ===== |
41 | +Before you can use this plugin, it must be enabled in **Insert->Preferences** on the tab Plugins. |
42 | +Here you can configure it with these options: |
43 | + |
44 | +**Show helper toolbar**: Displays a menu with buttons beneath the table, if it is selected. |
45 | +{{./table_toolbar.png}}{{./table_footer.png}} |
46 | +**Grid lines**: Enables or disables lines between table cells |
47 | +{{./with_borders.png}}{{./without_borders.png}} |
48 | + |
49 | +===== Read more ===== |
50 | +[[+Basic Table Operations|Basic Table Operations]] |
51 | +[[+Superior Table Operations|Superior Table Operations]] |
52 | |
53 | === added directory 'data/manual/Plugins/Table_Editor/Basic_Table_Operations' |
54 | === added file 'data/manual/Plugins/Table_Editor/Basic_Table_Operations.txt' |
55 | --- data/manual/Plugins/Table_Editor/Basic_Table_Operations.txt 1970-01-01 00:00:00 +0000 |
56 | +++ data/manual/Plugins/Table_Editor/Basic_Table_Operations.txt 2015-04-04 11:26:41 +0000 |
57 | @@ -0,0 +1,41 @@ |
58 | +Content-Type: text/x-zim-wiki |
59 | +Wiki-Format: zim 0.4 |
60 | + |
61 | +====== Basic Table Operations ====== |
62 | + |
63 | +===== Insertion of a table ===== |
64 | + |
65 | +==== Insert directly within textarea ==== |
66 | + |
67 | +''' |
68 | +|Header 1|Header 2|Header 3| |
69 | +|Data 1a|Data 2a|Data 3a| |
70 | +|Data 1b|Data 2b|Data 2b| |
71 | + |
72 | +Text after table. Table has to end with a blank line. |
73 | +''' |
74 | + |
75 | +Press CTRL+R or open menu item **View->reload**, so that the table can be displayed as widget. |
76 | + |
77 | +==== Use the insert-table button ==== |
78 | +{{./insert-table.png}} or press the menu item **Insert -> Table** |
79 | +After this step you can define the table titles with following Options: |
80 | +**Title**: Column title |
81 | +**Alignment**: Alignment of the cell (left, center, right) |
82 | +**Automatic Wrapping**: Column Cells with long text should be **automatic wrapped** to new lines. |
83 | +{{./table_settings_dialog.png}} |
84 | + |
85 | +==== Add row / Change table layout ==== |
86 | +Press the right button on the area of the table - but not on the header - and select in the popup menu some item. |
87 | +{{./table_popup.png}} |
88 | + |
89 | +===== Enter text in a cell ===== |
90 | +1. Click one time on a cell, wait one second. |
91 | +2. Click again on the cell. |
92 | +3. Now it opens a textfield where you can fill the cell. |
93 | +4. Press <ENTER> and the text will be fixed. |
94 | + or Press <ESC> and the inserted text will be ignored. |
95 | + |
96 | + |
97 | +===== Sorting ===== |
98 | +Click one time on the column title, which you want to sort. |
99 | |
100 | === added file 'data/manual/Plugins/Table_Editor/Basic_Table_Operations/insert-table.png' |
101 | Binary files data/manual/Plugins/Table_Editor/Basic_Table_Operations/insert-table.png 1970-01-01 00:00:00 +0000 and data/manual/Plugins/Table_Editor/Basic_Table_Operations/insert-table.png 2015-04-04 11:26:41 +0000 differ |
102 | === added file 'data/manual/Plugins/Table_Editor/Basic_Table_Operations/table_popup.png' |
103 | Binary files data/manual/Plugins/Table_Editor/Basic_Table_Operations/table_popup.png 1970-01-01 00:00:00 +0000 and data/manual/Plugins/Table_Editor/Basic_Table_Operations/table_popup.png 2015-04-04 11:26:41 +0000 differ |
104 | === added file 'data/manual/Plugins/Table_Editor/Basic_Table_Operations/table_settings_dialog.png' |
105 | Binary files data/manual/Plugins/Table_Editor/Basic_Table_Operations/table_settings_dialog.png 1970-01-01 00:00:00 +0000 and data/manual/Plugins/Table_Editor/Basic_Table_Operations/table_settings_dialog.png 2015-04-04 11:26:41 +0000 differ |
106 | === added directory 'data/manual/Plugins/Table_Editor/Superior_Table_Operations' |
107 | === added file 'data/manual/Plugins/Table_Editor/Superior_Table_Operations.txt' |
108 | --- data/manual/Plugins/Table_Editor/Superior_Table_Operations.txt 1970-01-01 00:00:00 +0000 |
109 | +++ data/manual/Plugins/Table_Editor/Superior_Table_Operations.txt 2015-04-04 11:26:41 +0000 |
110 | @@ -0,0 +1,46 @@ |
111 | +Content-Type: text/x-zim-wiki |
112 | +Wiki-Format: zim 0.4 |
113 | + |
114 | +====== Superior Table Operations ====== |
115 | + |
116 | +===== Markup in a cell ===== |
117 | +The main toolbar buttons for formatting are disabled. You have to know the special syntax and insert it directly within the textbox. |
118 | +While editing: {{./cell_editing_with_text_formatting.png}} |
119 | +After formatting: {{./cell_after_text_formatting.png}} |
120 | + |
121 | +==== Common formats ==== |
122 | +**bold**, //italic//, __highlight__, ~~strike through and~~ and ''verbatim'' |
123 | + |
124 | +''' |
125 | +**bold**, //italic//, __highlight__, ~~strike through~~ and ''verbatim'' |
126 | +''' |
127 | + |
128 | + |
129 | +==== Special characters ==== |
130 | +\\n Newline |
131 | +\| | Character |
132 | + |
133 | +==== Links in a cell ==== |
134 | +''' |
135 | +[[http://www.python.org]] |
136 | +[[foo@bar.org]] |
137 | +[[foo]] Internal Link |
138 | +''' |
139 | + |
140 | +Note: Only **one link per cell** can be opened. The first one is taken. |
141 | + You can not use a special link text. The link text is the url, the mail-address or the Title of the linked page. |
142 | +A link can be accessed with: |
143 | +* ''RIGHT MOUSE-BUTTON-CLICK'' on cell with the link and selection of the menu item //Open cell in content link// |
144 | +* ''CTRL + LEFT MOUSE-BUTTON-CLICK'' on a cell |
145 | + |
146 | +See also: [[:Help:Links|Syntax for Links]] |
147 | + |
148 | +===== Exporting: ===== |
149 | +Exporting filters for Plain, HTML, Markdown, reStructuredText, Latex have been also adjusted for the table plugin. |
150 | + |
151 | +===== Limits of widget's integration ===== |
152 | +* search / replacing: |
153 | + * no highlighting |
154 | + * only first location is found and after that the next one within the main text |
155 | +* Undo / Redo not supported at all |
156 | +* There can not be any element right or left to the table. The table must stand alone. |
157 | |
158 | === added file 'data/manual/Plugins/Table_Editor/Superior_Table_Operations/cell_after_text_formatting.png' |
159 | Binary files data/manual/Plugins/Table_Editor/Superior_Table_Operations/cell_after_text_formatting.png 1970-01-01 00:00:00 +0000 and data/manual/Plugins/Table_Editor/Superior_Table_Operations/cell_after_text_formatting.png 2015-04-04 11:26:41 +0000 differ |
160 | === added file 'data/manual/Plugins/Table_Editor/Superior_Table_Operations/cell_editing_with_text_formatting.png' |
161 | Binary files data/manual/Plugins/Table_Editor/Superior_Table_Operations/cell_editing_with_text_formatting.png 1970-01-01 00:00:00 +0000 and data/manual/Plugins/Table_Editor/Superior_Table_Operations/cell_editing_with_text_formatting.png 2015-04-04 11:26:41 +0000 differ |
162 | === added file 'data/manual/Plugins/Table_Editor/table_demo.png' |
163 | Binary files data/manual/Plugins/Table_Editor/table_demo.png 1970-01-01 00:00:00 +0000 and data/manual/Plugins/Table_Editor/table_demo.png 2015-04-04 11:26:41 +0000 differ |
164 | === added file 'data/manual/Plugins/Table_Editor/table_footer.png' |
165 | Binary files data/manual/Plugins/Table_Editor/table_footer.png 1970-01-01 00:00:00 +0000 and data/manual/Plugins/Table_Editor/table_footer.png 2015-04-04 11:26:41 +0000 differ |
166 | === added file 'data/manual/Plugins/Table_Editor/table_toolbar.png' |
167 | Binary files data/manual/Plugins/Table_Editor/table_toolbar.png 1970-01-01 00:00:00 +0000 and data/manual/Plugins/Table_Editor/table_toolbar.png 2015-04-04 11:26:41 +0000 differ |
168 | === added file 'data/manual/Plugins/Table_Editor/with_borders.png' |
169 | Binary files data/manual/Plugins/Table_Editor/with_borders.png 1970-01-01 00:00:00 +0000 and data/manual/Plugins/Table_Editor/with_borders.png 2015-04-04 11:26:41 +0000 differ |
170 | === added file 'data/manual/Plugins/Table_Editor/without_borders.png' |
171 | Binary files data/manual/Plugins/Table_Editor/without_borders.png 1970-01-01 00:00:00 +0000 and data/manual/Plugins/Table_Editor/without_borders.png 2015-04-04 11:26:41 +0000 differ |
172 | === added file 'data/pixmaps/insert-table.png' |
173 | Binary files data/pixmaps/insert-table.png 1970-01-01 00:00:00 +0000 and data/pixmaps/insert-table.png 2015-04-04 11:26:41 +0000 differ |
174 | === modified file 'tests/__init__.py' |
175 | --- tests/__init__.py 2014-08-20 09:46:08 +0000 |
176 | +++ tests/__init__.py 2015-04-04 11:26:41 +0000 |
177 | @@ -55,7 +55,7 @@ |
178 | 'calendar', 'printtobrowser', 'versioncontrol', 'inlinecalculator', |
179 | 'tasklist', 'tags', 'imagegenerators', 'tableofcontents', |
180 | 'quicknote', 'attachmentbrowser', 'insertsymbol', |
181 | - 'sourceview', |
182 | + 'sourceview', 'tableeditor', |
183 | 'ipc' |
184 | ] |
185 | |
186 | |
187 | === modified file 'tests/data/formats/export.html' |
188 | --- tests/data/formats/export.html 2014-09-04 17:56:31 +0000 |
189 | +++ tests/data/formats/export.html 2015-04-04 11:26:41 +0000 |
190 | @@ -268,8 +268,33 @@ |
191 | </pre> |
192 | </div> |
193 | |
194 | +<h2>A table</h2> |
195 | + |
196 | <br> |
197 | |
198 | +<table> |
199 | +<thead><tr> |
200 | + <th align="center">H1</th> |
201 | + <th align="right">H2 h2</th> |
202 | + <th align="left">H3</th> |
203 | +</tr></thead> |
204 | +<tr> |
205 | + <td align="center">Column A1</td> |
206 | + <td align="right">Column A2</td> |
207 | + <td align="left">a</td> |
208 | +</tr> |
209 | +<tr> |
210 | + <td align="center">a very long cell</td> |
211 | + <td align="right"><b>bold text</b></td> |
212 | + <td align="left">b</td> |
213 | +</tr> |
214 | +<tr> |
215 | + <td align="center">hyperlinks</td> |
216 | + <td align="right"><a href="interwiki:wp?wiki" title="wp?wiki" class="interwiki">wp?wiki</a></td> |
217 | + <td align="left"><a href="http://x.org" title="Xorg" class="http">Xorg</a></td> |
218 | +</tr> |
219 | +</table> |
220 | + |
221 | <p> |
222 | ====<br> |
223 | This is not a header |
224 | |
225 | === modified file 'tests/data/formats/export.markdown' |
226 | --- tests/data/formats/export.markdown 2014-05-04 19:17:46 +0000 |
227 | +++ tests/data/formats/export.markdown 2015-04-04 11:26:41 +0000 |
228 | @@ -165,6 +165,14 @@ |
229 | brought countless ills upon |
230 | the Achaeans. |
231 | |
232 | +A table |
233 | +------- |
234 | + |
235 | +| H1 | H2 h2 | H3 | |
236 | +|:----------------:|-----------------------------:|:---------------------| |
237 | +| Column A1 | Column A2 | a | |
238 | +| a very long cell | **bold text** | b | |
239 | +| hyperlinks | [wp?wiki](interwiki:wp?wiki) | [Xorg](http://x.org) | |
240 | |
241 | ==== |
242 | This is not a header |
243 | |
244 | === modified file 'tests/data/formats/export.rst' |
245 | --- tests/data/formats/export.rst 2014-05-04 19:17:46 +0000 |
246 | +++ tests/data/formats/export.rst 2015-04-04 11:26:41 +0000 |
247 | @@ -192,6 +192,18 @@ |
248 | brought countless ills upon |
249 | the Achaeans. |
250 | |
251 | +A table |
252 | +------- |
253 | + |
254 | ++------------------+--------------------------------+------------------------+ |
255 | +| H1 | H2 h2 | H3 | |
256 | ++==================+================================+========================+ |
257 | +| Column A1 | Column A2 | a | |
258 | ++------------------+--------------------------------+------------------------+ |
259 | +| a very long cell | **bold text** | b | |
260 | ++------------------+--------------------------------+------------------------+ |
261 | +| hyperlinks | `wp?wiki <interwiki:wp?wiki>`_ | `Xorg <http://x.org>`_ | |
262 | ++------------------+--------------------------------+------------------------+ |
263 | |
264 | ==== |
265 | This is not a header |
266 | |
267 | === modified file 'tests/data/formats/export.tex' |
268 | --- tests/data/formats/export.tex 2014-05-04 19:17:46 +0000 |
269 | +++ tests/data/formats/export.tex 2015-04-04 11:26:41 +0000 |
270 | @@ -301,6 +301,22 @@ |
271 | \end{lstlisting} |
272 | |
273 | |
274 | +\section{A table} |
275 | + |
276 | + |
277 | + |
278 | +\begin{tabular}{ |c|r|l| } |
279 | +\hline |
280 | + H1 & H2 h2 & H3 \tabularnewline |
281 | +\hline |
282 | +\hline |
283 | + Column A1 & Column A2 & a \tabularnewline |
284 | +\hline |
285 | + a very long cell & \textbf{bold text} & b \tabularnewline |
286 | +\hline |
287 | + hyperlinks & \href{interwiki:wp?wiki}{wp?wiki} & \href{http://x.org}{Xorg} \tabularnewline |
288 | +\hline |
289 | +\end{tabular} |
290 | |
291 | |
292 | ==== |
293 | |
294 | === modified file 'tests/data/formats/parsetree.xml' |
295 | --- tests/data/formats/parsetree.xml 2012-11-06 18:20:37 +0000 |
296 | +++ tests/data/formats/parsetree.xml 2015-04-04 11:26:41 +0000 |
297 | @@ -125,7 +125,9 @@ |
298 | brought countless ills upon |
299 | the Achaeans. |
300 | </object> |
301 | +<h level="2">A table</h> |
302 | |
303 | +<table aligns="center,right,left" wraps="1,0,1"><thead><th>H1</th><th>H2 h2</th><th>H3</th></thead><trow><td>Column A1</td><td>Column A2</td><td>a</td></trow><trow><td>a very long cell</td><td><strong>bold text</strong></td><td>b</td></trow><trow><td>hyperlinks</td><td><link href="wp?wiki">wp?wiki</link></td><td><link href="http://x.org">Xorg</link></td></trow></table> |
304 | <p>==== |
305 | This is not a header |
306 | </p> |
307 | |
308 | === modified file 'tests/data/formats/plain.txt' |
309 | --- tests/data/formats/plain.txt 2012-11-06 18:20:37 +0000 |
310 | +++ tests/data/formats/plain.txt 2015-04-04 11:26:41 +0000 |
311 | @@ -155,6 +155,18 @@ |
312 | brought countless ills upon |
313 | the Achaeans. |
314 | |
315 | +A table |
316 | +------- |
317 | + |
318 | ++------------------+-----------+------+ |
319 | +| H1 | H2 h2 | H3 | |
320 | ++==================+===========+======+ |
321 | +| Column A1 | Column A2 | a | |
322 | ++------------------+-----------+------+ |
323 | +| a very long cell | bold text | b | |
324 | ++------------------+-----------+------+ |
325 | +| hyperlinks | wp?wiki | Xorg | |
326 | ++------------------+-----------+------+ |
327 | |
328 | ==== |
329 | This is not a header |
330 | |
331 | === modified file 'tests/data/formats/wiki.txt' |
332 | --- tests/data/formats/wiki.txt 2014-02-02 21:21:24 +0000 |
333 | +++ tests/data/formats/wiki.txt 2015-04-04 11:26:41 +0000 |
334 | @@ -161,6 +161,13 @@ |
335 | the Achaeans. |
336 | }}} |
337 | |
338 | +===== A table ===== |
339 | + |
340 | +| H1 <| H2 h2 | H3 <| |
341 | +|:----------------:|--------------:|:-----------------------| |
342 | +| Column A1 | Column A2 | a | |
343 | +| a very long cell | **bold text** | b | |
344 | +| hyperlinks | [[wp?wiki]] | [[http://x.org\|Xorg]] | |
345 | |
346 | ==== |
347 | This is not a header |
348 | |
349 | === modified file 'tests/objectmanager.py' |
350 | --- tests/objectmanager.py 2014-11-09 11:07:20 +0000 |
351 | +++ tests/objectmanager.py 2015-04-04 11:26:41 +0000 |
352 | @@ -48,7 +48,7 @@ |
353 | from zim.plugins.sourceview import SourceViewPlugin |
354 | self.assertEqual( |
355 | manager.find_plugin('code'), |
356 | - ('sourceview', 'Source View', True, SourceViewPlugin) |
357 | + ('sourceview', 'Source View', True, SourceViewPlugin, None) |
358 | ) |
359 | |
360 | |
361 | |
362 | === added file 'tests/tableeditor.py' |
363 | --- tests/tableeditor.py 1970-01-01 00:00:00 +0000 |
364 | +++ tests/tableeditor.py 2015-04-04 11:26:41 +0000 |
365 | @@ -0,0 +1,84 @@ |
366 | +# -*- coding: utf-8 -*- |
367 | + |
368 | +from __future__ import with_statement |
369 | + |
370 | +import tests |
371 | + |
372 | +from tests.pageview import setUpPageView |
373 | + |
374 | +from zim.config import ConfigDict |
375 | +from zim.formats import ParseTree, StubLinker |
376 | +from zim.formats.html import Dumper as HtmlDumper |
377 | + |
378 | +from zim.plugins.tableeditor import * |
379 | + |
380 | +class TestMainWindowExtension(tests.TestCase): |
381 | + |
382 | + def runTest(self): |
383 | + window = tests.MockObject() |
384 | + window.pageview = setUpPageView() |
385 | + window.ui = tests.MockObject() |
386 | + window.ui.uimanager = tests.MockObject() |
387 | + window.ui.uistate = ConfigDict() |
388 | + window.ui.mainwindow = window # XXX |
389 | + |
390 | + plugin = TableEditorPlugin() |
391 | + extension = MainWindowExtension(plugin, window) |
392 | + |
393 | + with tests.DialogContext(self.checkInsertTableDialog): |
394 | + extension.insert_table() |
395 | + |
396 | + tree = window.pageview.get_parsetree() |
397 | + #~ print tree.tostring() |
398 | + obj = tree.find('table') |
399 | + |
400 | + self.assertTrue(obj.attrib['aligns'] == 'left') |
401 | + self.assertTrue(obj.attrib['wraps'] == '0') |
402 | + |
403 | + # Parses tree to a table object |
404 | + tabledata = tree.tostring().replace("<?xml version='1.0' encoding='utf-8'?>", '')\ |
405 | + .replace('<zim-tree>', '').replace('</zim-tree>', '')\ |
406 | + .replace('<td> </td>', '<td>text</td>') |
407 | + |
408 | + table = plugin.create_table({'type': 'table'}, ElementTree.fromstring(tabledata)) |
409 | + |
410 | + self.assertTrue(isinstance(table, TableViewObject)) |
411 | + |
412 | + def checkInsertTableDialog(self, dialog): |
413 | + self.assertIsInstance(dialog, EditTableDialog) |
414 | + dialog.assert_response_ok() |
415 | + |
416 | +class TestEditTableExtension(tests.TestCase): |
417 | + def checkUpdateTableDialog(self, dialog): |
418 | + self.assertIsInstance(dialog, EditTableDialog) |
419 | + dialog.assert_response_ok() |
420 | + |
421 | + def testChangeTable(self): |
422 | + window = tests.MockObject() |
423 | + window.pageview = setUpPageView() |
424 | + window.ui = tests.MockObject() |
425 | + window.ui.uimanager = tests.MockObject() |
426 | + window.ui.uistate = ConfigDict() |
427 | + window.ui.mainwindow = window # XXX |
428 | + plugin = TableEditorPlugin() |
429 | + extension = MainWindowExtension(plugin, window) |
430 | + obj = plugin.create_table({'aligns': 'normal,normal', 'wraps': '0,0'}, (('h1', 'h2'),('t1', 't2'))) |
431 | + obj.get_widget() |
432 | + |
433 | + with tests.DialogContext(self.checkUpdateTableDialog): |
434 | + extension.do_edit_object(obj) |
435 | + |
436 | + self.assertTrue(isinstance(obj.get_widget().treeview, gtk.TreeView)) |
437 | + |
438 | +class TestTableFunctions(tests.TestCase): |
439 | + def testCellFormater(self): |
440 | + self.assertEqual(CellFormatReplacer.input_to_cell('**hello**', with_pango=True), '<b>hello</b>') |
441 | + self.assertEqual(CellFormatReplacer.cell_to_input('<span background="yellow">highlight</span>', with_pango=True), |
442 | + '__highlight__') |
443 | + self.assertEqual(CellFormatReplacer.zim_to_cell('<link href="./alink">hello</link>'), |
444 | + '<span foreground="blue">hello<span size="0">./alink</span></span>') |
445 | + self.assertEqual(CellFormatReplacer.cell_to_zim('<tt>code-block</tt>'), '<code>code-block</code>') |
446 | + |
447 | +class TestColumnSorting(tests.TestCase): |
448 | + def testSorting(self): |
449 | + pass |
450 | \ No newline at end of file |
451 | |
452 | === modified file 'translations/zim.pot' |
453 | --- translations/zim.pot 2014-09-04 19:38:24 +0000 |
454 | +++ translations/zim.pot 2015-04-04 11:26:41 +0000 |
455 | @@ -8,7 +8,7 @@ |
456 | msgstr "" |
457 | "Project-Id-Version: PACKAGE VERSION\n" |
458 | "Report-Msgid-Bugs-To: \n" |
459 | -"POT-Creation-Date: 2014-09-04 21:38+0200\n" |
460 | +"POT-Creation-Date: 2015-04-04 12:31+0200\n" |
461 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
462 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
463 | "Language-Team: LANGUAGE <LL@li.org>\n" |
464 | @@ -56,7 +56,7 @@ |
465 | |
466 | #. message for FileNotFoundError | |
467 | #. Error message in template lookup |
468 | -#: zim/fs.py:476 zim/templates/__init__.py:126 |
469 | +#: zim/fs.py:476 zim/templates/__init__.py:125 |
470 | #, python-format |
471 | msgid "No such file: %s" |
472 | msgstr "" |
473 | @@ -146,10 +146,10 @@ |
474 | #. label for properties dialog | |
475 | #. label for file name |
476 | #: zim/gui/applications.py:804 zim/gui/customtools.py:157 |
477 | -#: zim/gui/__init__.py:3352 zim/gui/notebookdialog.py:413 |
478 | -#: zim/gui/pageview.py:6857 zim/gui/preferencesdialog.py:244 |
479 | -#: zim/gui/templateeditordialog.py:157 zim/notebook.py:838 |
480 | -#: zim/plugins/attachmentbrowser.py:726 |
481 | +#: zim/gui/__init__.py:3378 zim/gui/notebookdialog.py:413 |
482 | +#: zim/gui/pageview.py:7042 zim/gui/preferencesdialog.py:247 |
483 | +#: zim/gui/templateeditordialog.py:158 zim/notebook.py:838 |
484 | +#: zim/plugins/attachmentbrowser.py:727 |
485 | msgid "Name" |
486 | msgstr "" |
487 | |
488 | @@ -189,7 +189,7 @@ |
489 | |
490 | #. Input in "Edit Custom Tool" dialog | |
491 | #. Heading in plugins tab of preferences dialog |
492 | -#: zim/gui/customtools.py:158 zim/gui/preferencesdialog.py:246 |
493 | +#: zim/gui/customtools.py:158 zim/gui/preferencesdialog.py:249 |
494 | msgid "Description" |
495 | msgstr "" |
496 | |
497 | @@ -231,58 +231,58 @@ |
498 | msgstr "" |
499 | |
500 | #. dialog title |
501 | -#: zim/gui/exportdialog.py:27 |
502 | +#: zim/gui/exportdialog.py:31 |
503 | msgid "Export" |
504 | msgstr "" |
505 | |
506 | #. Title of progressbar dialog |
507 | -#: zim/gui/exportdialog.py:43 zim/gui/__init__.py:2105 |
508 | +#: zim/gui/exportdialog.py:51 zim/gui/__init__.py:2130 |
509 | msgid "Updating index" |
510 | msgstr "" |
511 | |
512 | #. Title for progressbar window |
513 | -#: zim/gui/exportdialog.py:51 |
514 | +#: zim/gui/exportdialog.py:60 |
515 | msgid "Exporting notebook" |
516 | msgstr "" |
517 | |
518 | #. message heading |
519 | -#: zim/gui/exportdialog.py:136 |
520 | +#: zim/gui/exportdialog.py:146 |
521 | #, python-format |
522 | msgid "Folder exists: %s" |
523 | msgstr "" |
524 | |
525 | #. detailed message, answers are Yes and No |
526 | -#: zim/gui/exportdialog.py:137 |
527 | +#: zim/gui/exportdialog.py:147 |
528 | msgid "" |
529 | "Folder already exists and has content, exporting to this folder may " |
530 | "overwrite existing files. Do you want to continue?" |
531 | msgstr "" |
532 | |
533 | #. message heading |
534 | -#: zim/gui/exportdialog.py:150 |
535 | +#: zim/gui/exportdialog.py:160 |
536 | msgid "File exists" |
537 | msgstr "" |
538 | |
539 | #. detailed message, answers are Yes and No |
540 | -#: zim/gui/exportdialog.py:151 |
541 | +#: zim/gui/exportdialog.py:161 |
542 | msgid "" |
543 | "This file already exists.\n" |
544 | "Do you want to overwrite it?" |
545 | msgstr "" |
546 | |
547 | #. title of step in export dialog |
548 | -#: zim/gui/exportdialog.py:162 |
549 | +#: zim/gui/exportdialog.py:172 |
550 | msgid "Select the pages to export" |
551 | msgstr "" |
552 | |
553 | #. Option in export dialog to export complete notebook | |
554 | #. Option in versions dialog to show version for complete notebook |
555 | -#: zim/gui/exportdialog.py:168 zim/plugins/versioncontrol/__init__.py:934 |
556 | +#: zim/gui/exportdialog.py:178 zim/plugins/versioncontrol/__init__.py:934 |
557 | msgid "Complete _notebook" |
558 | msgstr "" |
559 | |
560 | #. Option in export dialog to export selection |
561 | -#: zim/gui/exportdialog.py:173 |
562 | +#: zim/gui/exportdialog.py:183 |
563 | msgid "Single _page" |
564 | msgstr "" |
565 | |
566 | @@ -291,586 +291,586 @@ |
567 | #. Column header | |
568 | #. Column header search dialog | |
569 | #. Column header Task List dialog |
570 | -#: zim/gui/exportdialog.py:176 zim/gui/pageview.py:6754 |
571 | -#: zim/gui/recentchangesdialog.py:54 zim/gui/searchdialog.py:150 |
572 | -#: zim/plugins/quicknote.py:221 zim/plugins/tasklist.py:943 |
573 | +#: zim/gui/exportdialog.py:186 zim/gui/pageview.py:6939 |
574 | +#: zim/gui/recentchangesdialog.py:54 zim/gui/searchdialog.py:151 |
575 | +#: zim/plugins/quicknote.py:221 zim/plugins/tasklist.py:947 |
576 | msgid "Page" |
577 | msgstr "" |
578 | |
579 | #. Input field in export dialog |
580 | -#: zim/gui/exportdialog.py:177 |
581 | +#: zim/gui/exportdialog.py:187 |
582 | msgid "Include subpages" |
583 | msgstr "" |
584 | |
585 | #. title of step in export dialog |
586 | -#: zim/gui/exportdialog.py:204 |
587 | +#: zim/gui/exportdialog.py:214 |
588 | msgid "Select the export format" |
589 | msgstr "" |
590 | |
591 | #. Option in drop down menu to specify another file |
592 | -#: zim/gui/exportdialog.py:206 |
593 | +#: zim/gui/exportdialog.py:216 |
594 | msgid "Other..." |
595 | msgstr "" |
596 | |
597 | #. Input label in the export dialog | |
598 | #. label in "insert date" dialog |
599 | -#: zim/gui/exportdialog.py:218 zim/gui/pageview.py:6031 |
600 | +#: zim/gui/exportdialog.py:228 zim/gui/pageview.py:6216 |
601 | msgid "Format" |
602 | msgstr "" |
603 | |
604 | #. Input label in the export dialog |
605 | -#: zim/gui/exportdialog.py:219 |
606 | +#: zim/gui/exportdialog.py:229 |
607 | msgid "Template" |
608 | msgstr "" |
609 | |
610 | #. radio option in export dialog |
611 | -#: zim/gui/exportdialog.py:222 |
612 | +#: zim/gui/exportdialog.py:232 |
613 | msgid "Link files under document root with full file path" |
614 | msgstr "" |
615 | |
616 | #. radio option in export dialog |
617 | -#: zim/gui/exportdialog.py:223 |
618 | +#: zim/gui/exportdialog.py:233 |
619 | msgid "Map document root to URL" |
620 | msgstr "" |
621 | |
622 | #. label for button with URL |
623 | -#: zim/gui/exportdialog.py:233 zim/gui/templateeditordialog.py:70 |
624 | +#: zim/gui/exportdialog.py:244 zim/gui/templateeditordialog.py:71 |
625 | msgid "Get more templates online" |
626 | msgstr "" |
627 | |
628 | #. title of step in export dialog |
629 | -#: zim/gui/exportdialog.py:307 |
630 | +#: zim/gui/exportdialog.py:318 |
631 | msgid "Select the output file or folder" |
632 | msgstr "" |
633 | |
634 | #. Label for option in export dialog |
635 | -#: zim/gui/exportdialog.py:313 |
636 | +#: zim/gui/exportdialog.py:324 |
637 | msgid "Export each page to a separate file" |
638 | msgstr "" |
639 | |
640 | #. Label for option in export dialog |
641 | -#: zim/gui/exportdialog.py:315 |
642 | +#: zim/gui/exportdialog.py:326 |
643 | msgid "Export all pages to a single file" |
644 | msgstr "" |
645 | |
646 | #. Label for folder selection in export dialog |
647 | -#: zim/gui/exportdialog.py:318 |
648 | +#: zim/gui/exportdialog.py:329 |
649 | msgid "Output folder" |
650 | msgstr "" |
651 | |
652 | #. Label for setting a name for the index of exported pages |
653 | -#: zim/gui/exportdialog.py:320 |
654 | +#: zim/gui/exportdialog.py:331 |
655 | msgid "Index page" |
656 | msgstr "" |
657 | |
658 | #. Label for file selection in export dialog |
659 | -#: zim/gui/exportdialog.py:323 |
660 | +#: zim/gui/exportdialog.py:334 |
661 | msgid "Output file" |
662 | msgstr "" |
663 | |
664 | #. label in export dialog |
665 | -#: zim/gui/exportdialog.py:412 |
666 | +#: zim/gui/exportdialog.py:423 |
667 | #, python-format |
668 | -msgid "%(n_errors)i errors and %(n_warnings)i warnings occurred, see log" |
669 | +msgid "%(n_error)i errors and %(n_warning)i warnings occurred, see log" |
670 | msgstr "" |
671 | |
672 | #. label in export dialog |
673 | -#: zim/gui/exportdialog.py:415 |
674 | +#: zim/gui/exportdialog.py:426 |
675 | #, python-format |
676 | msgid "%i errors occurred, see log" |
677 | msgstr "" |
678 | |
679 | #. label in export dialog |
680 | -#: zim/gui/exportdialog.py:418 |
681 | +#: zim/gui/exportdialog.py:429 |
682 | #, python-format |
683 | msgid "%i warnings occurred, see log" |
684 | msgstr "" |
685 | |
686 | #. label in export dialog |
687 | -#: zim/gui/exportdialog.py:423 |
688 | +#: zim/gui/exportdialog.py:434 |
689 | msgid "Export completed" |
690 | msgstr "" |
691 | |
692 | #. button in export dialog | |
693 | #. button in e.g. equation editor dialog |
694 | -#: zim/gui/exportdialog.py:426 zim/plugins/base/imagegenerator.py:286 |
695 | +#: zim/gui/exportdialog.py:437 zim/plugins/base/imagegenerator.py:286 |
696 | msgid "View _Log" |
697 | msgstr "" |
698 | |
699 | #. Menu title |
700 | -#: zim/gui/__init__.py:61 |
701 | +#: zim/gui/__init__.py:63 |
702 | msgid "_File" |
703 | msgstr "" |
704 | |
705 | #. Menu title |
706 | -#: zim/gui/__init__.py:62 |
707 | +#: zim/gui/__init__.py:64 |
708 | msgid "_Edit" |
709 | msgstr "" |
710 | |
711 | #. Menu title | |
712 | #. button label |
713 | -#: zim/gui/__init__.py:63 zim/gui/templateeditordialog.py:42 |
714 | +#: zim/gui/__init__.py:65 zim/gui/templateeditordialog.py:42 |
715 | msgid "_View" |
716 | msgstr "" |
717 | |
718 | #. Menu title | |
719 | #. Button label |
720 | -#: zim/gui/__init__.py:64 zim/gui/pageview.py:6021 |
721 | +#: zim/gui/__init__.py:66 zim/gui/pageview.py:6206 |
722 | #: zim/plugins/insertsymbol.py:138 |
723 | msgid "_Insert" |
724 | msgstr "" |
725 | |
726 | #. Menu title |
727 | -#: zim/gui/__init__.py:65 |
728 | +#: zim/gui/__init__.py:67 |
729 | msgid "_Search" |
730 | msgstr "" |
731 | |
732 | #. Menu title |
733 | -#: zim/gui/__init__.py:66 |
734 | +#: zim/gui/__init__.py:68 |
735 | msgid "For_mat" |
736 | msgstr "" |
737 | |
738 | #. Menu title |
739 | -#: zim/gui/__init__.py:67 |
740 | +#: zim/gui/__init__.py:69 |
741 | msgid "_Tools" |
742 | msgstr "" |
743 | |
744 | #. Menu title |
745 | -#: zim/gui/__init__.py:68 |
746 | +#: zim/gui/__init__.py:70 |
747 | msgid "_Go" |
748 | msgstr "" |
749 | |
750 | #. Menu title |
751 | -#: zim/gui/__init__.py:69 |
752 | +#: zim/gui/__init__.py:71 |
753 | msgid "_Help" |
754 | msgstr "" |
755 | |
756 | #. Menu title |
757 | -#: zim/gui/__init__.py:70 |
758 | +#: zim/gui/__init__.py:72 |
759 | msgid "P_athbar" |
760 | msgstr "" |
761 | |
762 | #. Menu title | |
763 | #. Menu item |
764 | -#: zim/gui/__init__.py:71 zim/gui/__init__.py:141 zim/gui/__init__.py:151 |
765 | +#: zim/gui/__init__.py:73 zim/gui/__init__.py:143 zim/gui/__init__.py:153 |
766 | msgid "_Toolbar" |
767 | msgstr "" |
768 | |
769 | #. Menu item |
770 | -#: zim/gui/__init__.py:74 |
771 | +#: zim/gui/__init__.py:76 |
772 | msgid "_New Page..." |
773 | msgstr "" |
774 | |
775 | #. Menu item |
776 | -#: zim/gui/__init__.py:75 |
777 | +#: zim/gui/__init__.py:77 |
778 | msgid "New S_ub Page..." |
779 | msgstr "" |
780 | |
781 | #. Menu item |
782 | -#: zim/gui/__init__.py:76 |
783 | +#: zim/gui/__init__.py:78 |
784 | msgid "_Open Another Notebook..." |
785 | msgstr "" |
786 | |
787 | #. Menu item | |
788 | #. menu item to open a link |
789 | -#: zim/gui/__init__.py:77 zim/gui/pageview.py:5405 |
790 | +#: zim/gui/__init__.py:79 zim/gui/pageview.py:5558 |
791 | #: zim/plugins/backlinkpane.py:120 |
792 | msgid "Open in New _Window" |
793 | msgstr "" |
794 | |
795 | #. Menu item |
796 | -#: zim/gui/__init__.py:78 |
797 | +#: zim/gui/__init__.py:80 |
798 | msgid "_Import Page..." |
799 | msgstr "" |
800 | |
801 | #. Menu item |
802 | -#: zim/gui/__init__.py:79 |
803 | +#: zim/gui/__init__.py:81 |
804 | msgid "_Save" |
805 | msgstr "" |
806 | |
807 | #. Menu item |
808 | -#: zim/gui/__init__.py:80 |
809 | +#: zim/gui/__init__.py:82 |
810 | msgid "Save A _Copy..." |
811 | msgstr "" |
812 | |
813 | #. Menu item |
814 | -#: zim/gui/__init__.py:81 |
815 | +#: zim/gui/__init__.py:83 |
816 | msgid "E_xport..." |
817 | msgstr "" |
818 | |
819 | #. Menu item |
820 | -#: zim/gui/__init__.py:82 |
821 | +#: zim/gui/__init__.py:84 |
822 | msgid "_Send To..." |
823 | msgstr "" |
824 | |
825 | #. Menu item |
826 | -#: zim/gui/__init__.py:83 |
827 | +#: zim/gui/__init__.py:85 |
828 | msgid "_Move Page..." |
829 | msgstr "" |
830 | |
831 | #. Menu item |
832 | -#: zim/gui/__init__.py:84 |
833 | +#: zim/gui/__init__.py:86 |
834 | msgid "_Rename Page..." |
835 | msgstr "" |
836 | |
837 | #. Menu item |
838 | -#: zim/gui/__init__.py:85 |
839 | +#: zim/gui/__init__.py:87 |
840 | msgid "_Delete Page" |
841 | msgstr "" |
842 | |
843 | #. Menu item |
844 | -#: zim/gui/__init__.py:86 |
845 | +#: zim/gui/__init__.py:88 |
846 | msgid "Proper_ties" |
847 | msgstr "" |
848 | |
849 | #. Menu item |
850 | -#: zim/gui/__init__.py:87 |
851 | +#: zim/gui/__init__.py:89 |
852 | msgid "_Close" |
853 | msgstr "" |
854 | |
855 | #. Menu item | |
856 | #. menu item in tray icon menu |
857 | -#: zim/gui/__init__.py:88 zim/plugins/trayicon.py:126 |
858 | +#: zim/gui/__init__.py:90 zim/plugins/trayicon.py:126 |
859 | msgid "_Quit" |
860 | msgstr "" |
861 | |
862 | #. Menu item |
863 | -#: zim/gui/__init__.py:89 |
864 | +#: zim/gui/__init__.py:91 |
865 | msgid "_Search..." |
866 | msgstr "" |
867 | |
868 | #. Menu item |
869 | -#: zim/gui/__init__.py:90 |
870 | +#: zim/gui/__init__.py:92 |
871 | msgid "Search _Backlinks..." |
872 | msgstr "" |
873 | |
874 | #. Menu item |
875 | -#: zim/gui/__init__.py:91 |
876 | +#: zim/gui/__init__.py:93 |
877 | msgid "Recent Changes..." |
878 | msgstr "" |
879 | |
880 | #. Menu item |
881 | -#: zim/gui/__init__.py:92 |
882 | +#: zim/gui/__init__.py:94 |
883 | msgid "Copy _Location" |
884 | msgstr "" |
885 | |
886 | #. Menu item |
887 | -#: zim/gui/__init__.py:93 |
888 | +#: zim/gui/__init__.py:95 |
889 | msgid "_Templates" |
890 | msgstr "" |
891 | |
892 | #. Menu item |
893 | -#: zim/gui/__init__.py:94 |
894 | +#: zim/gui/__init__.py:96 |
895 | msgid "Pr_eferences" |
896 | msgstr "" |
897 | |
898 | #. Menu item |
899 | -#: zim/gui/__init__.py:95 |
900 | +#: zim/gui/__init__.py:97 |
901 | msgid "_Reload" |
902 | msgstr "" |
903 | |
904 | #. Menu item |
905 | -#: zim/gui/__init__.py:96 |
906 | +#: zim/gui/__init__.py:98 |
907 | msgid "Open Attachments _Folder" |
908 | msgstr "" |
909 | |
910 | #. Menu item |
911 | -#: zim/gui/__init__.py:97 |
912 | +#: zim/gui/__init__.py:99 |
913 | msgid "Open _Notebook Folder" |
914 | msgstr "" |
915 | |
916 | #. Menu item |
917 | -#: zim/gui/__init__.py:98 |
918 | +#: zim/gui/__init__.py:100 |
919 | msgid "Open _Document Root" |
920 | msgstr "" |
921 | |
922 | #. Menu item |
923 | -#: zim/gui/__init__.py:99 |
924 | +#: zim/gui/__init__.py:101 |
925 | msgid "Open _Document Folder" |
926 | msgstr "" |
927 | |
928 | #. Menu item |
929 | -#: zim/gui/__init__.py:100 |
930 | +#: zim/gui/__init__.py:102 |
931 | msgid "Attach _File" |
932 | msgstr "" |
933 | |
934 | #. Menu item |
935 | -#: zim/gui/__init__.py:100 |
936 | +#: zim/gui/__init__.py:102 |
937 | msgid "Attach external file" |
938 | msgstr "" |
939 | |
940 | #. Menu item |
941 | -#: zim/gui/__init__.py:101 |
942 | +#: zim/gui/__init__.py:103 |
943 | msgid "Edit _Source" |
944 | msgstr "" |
945 | |
946 | #. Menu item |
947 | -#: zim/gui/__init__.py:102 |
948 | +#: zim/gui/__init__.py:104 |
949 | msgid "Start _Web Server" |
950 | msgstr "" |
951 | |
952 | #. Menu item |
953 | -#: zim/gui/__init__.py:103 |
954 | +#: zim/gui/__init__.py:105 |
955 | msgid "Update Index" |
956 | msgstr "" |
957 | |
958 | #. Menu item |
959 | -#: zim/gui/__init__.py:104 |
960 | +#: zim/gui/__init__.py:106 |
961 | msgid "Custom _Tools" |
962 | msgstr "" |
963 | |
964 | #. Menu item |
965 | -#: zim/gui/__init__.py:105 |
966 | +#: zim/gui/__init__.py:107 |
967 | msgid "_Back" |
968 | msgstr "" |
969 | |
970 | #. Menu item |
971 | -#: zim/gui/__init__.py:105 |
972 | +#: zim/gui/__init__.py:107 |
973 | msgid "Go page back" |
974 | msgstr "" |
975 | |
976 | #. Menu item |
977 | -#: zim/gui/__init__.py:106 |
978 | +#: zim/gui/__init__.py:108 |
979 | msgid "_Forward" |
980 | msgstr "" |
981 | |
982 | #. Menu item |
983 | -#: zim/gui/__init__.py:106 |
984 | +#: zim/gui/__init__.py:108 |
985 | msgid "Go page forward" |
986 | msgstr "" |
987 | |
988 | #. Menu item |
989 | -#: zim/gui/__init__.py:107 |
990 | +#: zim/gui/__init__.py:109 |
991 | msgid "_Parent" |
992 | msgstr "" |
993 | |
994 | #. Menu item |
995 | -#: zim/gui/__init__.py:107 |
996 | +#: zim/gui/__init__.py:109 |
997 | msgid "Go to parent page" |
998 | msgstr "" |
999 | |
1000 | #. Menu item |
1001 | -#: zim/gui/__init__.py:108 |
1002 | +#: zim/gui/__init__.py:110 |
1003 | msgid "_Child" |
1004 | msgstr "" |
1005 | |
1006 | #. Menu item |
1007 | -#: zim/gui/__init__.py:108 |
1008 | +#: zim/gui/__init__.py:110 |
1009 | msgid "Go to child page" |
1010 | msgstr "" |
1011 | |
1012 | #. Menu item |
1013 | -#: zim/gui/__init__.py:109 |
1014 | +#: zim/gui/__init__.py:111 |
1015 | msgid "_Previous in index" |
1016 | msgstr "" |
1017 | |
1018 | #. Menu item |
1019 | -#: zim/gui/__init__.py:109 |
1020 | +#: zim/gui/__init__.py:111 |
1021 | msgid "Go to previous page" |
1022 | msgstr "" |
1023 | |
1024 | #. Menu item |
1025 | -#: zim/gui/__init__.py:110 |
1026 | +#: zim/gui/__init__.py:112 |
1027 | msgid "_Next in index" |
1028 | msgstr "" |
1029 | |
1030 | #. Menu item |
1031 | -#: zim/gui/__init__.py:110 |
1032 | +#: zim/gui/__init__.py:112 |
1033 | msgid "Go to next page" |
1034 | msgstr "" |
1035 | |
1036 | #. Menu item |
1037 | -#: zim/gui/__init__.py:111 |
1038 | +#: zim/gui/__init__.py:113 |
1039 | msgid "_Home" |
1040 | msgstr "" |
1041 | |
1042 | #. Menu item |
1043 | -#: zim/gui/__init__.py:111 |
1044 | +#: zim/gui/__init__.py:113 |
1045 | msgid "Go home" |
1046 | msgstr "" |
1047 | |
1048 | #. Menu item |
1049 | -#: zim/gui/__init__.py:112 |
1050 | +#: zim/gui/__init__.py:114 |
1051 | msgid "_Jump To..." |
1052 | msgstr "" |
1053 | |
1054 | #. Menu item |
1055 | -#: zim/gui/__init__.py:113 |
1056 | +#: zim/gui/__init__.py:115 |
1057 | msgid "_Contents" |
1058 | msgstr "" |
1059 | |
1060 | #. Menu item |
1061 | -#: zim/gui/__init__.py:114 |
1062 | +#: zim/gui/__init__.py:116 |
1063 | msgid "_FAQ" |
1064 | msgstr "" |
1065 | |
1066 | #. Menu item |
1067 | -#: zim/gui/__init__.py:115 |
1068 | +#: zim/gui/__init__.py:117 |
1069 | msgid "_Keybindings" |
1070 | msgstr "" |
1071 | |
1072 | #. Menu item |
1073 | -#: zim/gui/__init__.py:116 |
1074 | +#: zim/gui/__init__.py:118 |
1075 | msgid "_Bugs" |
1076 | msgstr "" |
1077 | |
1078 | #. Menu item |
1079 | -#: zim/gui/__init__.py:117 |
1080 | +#: zim/gui/__init__.py:119 |
1081 | msgid "_About" |
1082 | msgstr "" |
1083 | |
1084 | #. Menu item |
1085 | -#: zim/gui/__init__.py:135 |
1086 | +#: zim/gui/__init__.py:137 |
1087 | msgid "_All Panes" |
1088 | msgstr "" |
1089 | |
1090 | #. Menu item |
1091 | -#: zim/gui/__init__.py:135 |
1092 | +#: zim/gui/__init__.py:137 |
1093 | msgid "Show All Panes" |
1094 | msgstr "" |
1095 | |
1096 | #. Menu item |
1097 | -#: zim/gui/__init__.py:142 zim/gui/__init__.py:152 |
1098 | +#: zim/gui/__init__.py:144 zim/gui/__init__.py:154 |
1099 | msgid "_Statusbar" |
1100 | msgstr "" |
1101 | |
1102 | #. Menu item # FIXME review text |
1103 | -#: zim/gui/__init__.py:143 zim/gui/__init__.py:153 |
1104 | +#: zim/gui/__init__.py:145 zim/gui/__init__.py:155 |
1105 | msgid "_Side Panes" |
1106 | msgstr "" |
1107 | |
1108 | #. Menu item # FIXME review text |
1109 | -#: zim/gui/__init__.py:143 zim/gui/__init__.py:153 |
1110 | +#: zim/gui/__init__.py:145 zim/gui/__init__.py:155 |
1111 | msgid "Show Side Panes" |
1112 | msgstr "" |
1113 | |
1114 | #. Menu item |
1115 | -#: zim/gui/__init__.py:144 zim/gui/__init__.py:154 |
1116 | +#: zim/gui/__init__.py:146 zim/gui/__init__.py:156 |
1117 | msgid "_Fullscreen" |
1118 | msgstr "" |
1119 | |
1120 | #. menu item |
1121 | -#: zim/gui/__init__.py:145 zim/gui/__init__.py:155 |
1122 | +#: zim/gui/__init__.py:147 zim/gui/__init__.py:157 |
1123 | msgid "Notebook _Editable" |
1124 | msgstr "" |
1125 | |
1126 | #. menu item |
1127 | -#: zim/gui/__init__.py:145 zim/gui/__init__.py:155 |
1128 | +#: zim/gui/__init__.py:147 zim/gui/__init__.py:157 |
1129 | msgid "Toggle notebook editable" |
1130 | msgstr "" |
1131 | |
1132 | #. Menu item |
1133 | -#: zim/gui/__init__.py:161 |
1134 | +#: zim/gui/__init__.py:163 |
1135 | msgid "_None" |
1136 | msgstr "" |
1137 | |
1138 | #. Menu item |
1139 | -#: zim/gui/__init__.py:162 |
1140 | +#: zim/gui/__init__.py:164 |
1141 | msgid "_Recent pages" |
1142 | msgstr "" |
1143 | |
1144 | #. Menu item |
1145 | -#: zim/gui/__init__.py:163 |
1146 | +#: zim/gui/__init__.py:165 |
1147 | msgid "Recently _Changed pages" |
1148 | msgstr "" |
1149 | |
1150 | #. Menu item |
1151 | -#: zim/gui/__init__.py:164 |
1152 | +#: zim/gui/__init__.py:166 |
1153 | msgid "_History" |
1154 | msgstr "" |
1155 | |
1156 | #. Menu item |
1157 | -#: zim/gui/__init__.py:165 |
1158 | +#: zim/gui/__init__.py:167 |
1159 | msgid "_Page Hierarchy" |
1160 | msgstr "" |
1161 | |
1162 | #. Menu item |
1163 | -#: zim/gui/__init__.py:178 |
1164 | +#: zim/gui/__init__.py:180 |
1165 | msgid "Icons _And Text" |
1166 | msgstr "" |
1167 | |
1168 | #. Menu item |
1169 | -#: zim/gui/__init__.py:179 |
1170 | +#: zim/gui/__init__.py:181 |
1171 | msgid "_Icons Only" |
1172 | msgstr "" |
1173 | |
1174 | #. Menu item |
1175 | -#: zim/gui/__init__.py:180 |
1176 | +#: zim/gui/__init__.py:182 |
1177 | msgid "_Text Only" |
1178 | msgstr "" |
1179 | |
1180 | #. Menu item |
1181 | -#: zim/gui/__init__.py:186 |
1182 | +#: zim/gui/__init__.py:188 |
1183 | msgid "_Large Icons" |
1184 | msgstr "" |
1185 | |
1186 | #. Menu item |
1187 | -#: zim/gui/__init__.py:187 |
1188 | +#: zim/gui/__init__.py:189 |
1189 | msgid "_Small Icons" |
1190 | msgstr "" |
1191 | |
1192 | #. Menu item |
1193 | -#: zim/gui/__init__.py:188 |
1194 | +#: zim/gui/__init__.py:190 |
1195 | msgid "_Tiny Icons" |
1196 | msgstr "" |
1197 | |
1198 | #. Option in the preferences dialog |
1199 | -#: zim/gui/__init__.py:202 |
1200 | +#: zim/gui/__init__.py:204 |
1201 | msgid "Add 'tearoff' strips to the menus" |
1202 | msgstr "" |
1203 | |
1204 | #. Option in the preferences dialog |
1205 | -#: zim/gui/__init__.py:204 |
1206 | +#: zim/gui/__init__.py:206 |
1207 | msgid "Use <Ctrl><Space> to switch to the side pane" |
1208 | msgstr "" |
1209 | |
1210 | #. Option in the preferences dialog |
1211 | -#: zim/gui/__init__.py:208 |
1212 | +#: zim/gui/__init__.py:210 |
1213 | msgid "Remove links when deleting pages" |
1214 | msgstr "" |
1215 | |
1216 | #. Option in the preferences dialog |
1217 | -#: zim/gui/__init__.py:210 |
1218 | +#: zim/gui/__init__.py:212 |
1219 | msgid "Always use last cursor position when opening a page" |
1220 | msgstr "" |
1221 | |
1222 | #. Error description for "no such file or folder" |
1223 | -#: zim/gui/__init__.py:272 |
1224 | +#: zim/gui/__init__.py:274 |
1225 | msgid "" |
1226 | "The file or folder you specified does not exist.\n" |
1227 | "Please check if you the path is correct." |
1228 | msgstr "" |
1229 | |
1230 | #. Error message, %s will be the file path |
1231 | -#: zim/gui/__init__.py:279 |
1232 | +#: zim/gui/__init__.py:281 |
1233 | #, python-format |
1234 | msgid "No such file or folder: %s" |
1235 | msgstr "" |
1236 | |
1237 | #. Error description |
1238 | -#: zim/gui/__init__.py:291 |
1239 | +#: zim/gui/__init__.py:293 |
1240 | msgid "Page has un-saved changes" |
1241 | msgstr "" |
1242 | |
1243 | #. Dialog title | |
1244 | #. input label |
1245 | -#: zim/gui/__init__.py:594 zim/gui/searchdialog.py:24 |
1246 | +#: zim/gui/__init__.py:596 zim/gui/searchdialog.py:24 |
1247 | #: zim/gui/searchdialog.py:31 |
1248 | msgid "Search" |
1249 | msgstr "" |
1250 | |
1251 | #. label in search entry |
1252 | -#: zim/gui/__init__.py:598 |
1253 | +#: zim/gui/__init__.py:601 |
1254 | msgid "Search Pages..." |
1255 | msgstr "" |
1256 | |
1257 | #. Short question for question prompt |
1258 | -#: zim/gui/__init__.py:665 |
1259 | +#: zim/gui/__init__.py:676 |
1260 | msgid "Upgrade Notebook?" |
1261 | msgstr "" |
1262 | |
1263 | #. Explanation for question to upgrade notebook |
1264 | -#: zim/gui/__init__.py:666 |
1265 | +#: zim/gui/__init__.py:677 |
1266 | msgid "" |
1267 | "This notebook was created by an older of version of zim.\n" |
1268 | "Do you want to upgrade it to the latest version now?\n" |
1269 | @@ -884,12 +884,12 @@ |
1270 | msgstr "" |
1271 | |
1272 | #. Title of progressbar dialog |
1273 | -#: zim/gui/__init__.py:678 |
1274 | +#: zim/gui/__init__.py:689 |
1275 | msgid "Upgrading notebook" |
1276 | msgstr "" |
1277 | |
1278 | #. question dialog text |
1279 | -#: zim/gui/__init__.py:1676 |
1280 | +#: zim/gui/__init__.py:1701 |
1281 | msgid "" |
1282 | "The index is still busy updating. Until this is finished links can not be " |
1283 | "updated correctly. Performing this action now could break links, do you want " |
1284 | @@ -897,22 +897,22 @@ |
1285 | msgstr "" |
1286 | |
1287 | #. Title of progressbar dialog |
1288 | -#: zim/gui/__init__.py:1687 |
1289 | +#: zim/gui/__init__.py:1712 |
1290 | msgid "Updating Links" |
1291 | msgstr "" |
1292 | |
1293 | #. Title of progressbar dialog |
1294 | -#: zim/gui/__init__.py:1714 zim/gui/__init__.py:3451 |
1295 | +#: zim/gui/__init__.py:1739 zim/gui/__init__.py:3477 |
1296 | msgid "Removing Links" |
1297 | msgstr "" |
1298 | |
1299 | #. Heading in a question dialog for creating a folder |
1300 | -#: zim/gui/__init__.py:1822 zim/gui/pageview.py:5715 |
1301 | +#: zim/gui/__init__.py:1847 zim/gui/pageview.py:5871 |
1302 | msgid "Create folder?" |
1303 | msgstr "" |
1304 | |
1305 | #. Text in a question dialog for creating a folder, %s will be the folder base name |
1306 | -#: zim/gui/__init__.py:1824 |
1307 | +#: zim/gui/__init__.py:1849 |
1308 | #, python-format |
1309 | msgid "" |
1310 | "The folder \"%s\" does not yet exist.\n" |
1311 | @@ -920,51 +920,51 @@ |
1312 | msgstr "" |
1313 | |
1314 | #. error when external application fails |
1315 | -#: zim/gui/__init__.py:1944 zim/gui/__init__.py:2062 |
1316 | +#: zim/gui/__init__.py:1969 zim/gui/__init__.py:2087 |
1317 | #, python-format |
1318 | msgid "Could not open: %s" |
1319 | msgstr "" |
1320 | |
1321 | #. Error message |
1322 | -#: zim/gui/__init__.py:1959 |
1323 | +#: zim/gui/__init__.py:1984 |
1324 | msgid "This page does not have an attachments folder" |
1325 | msgstr "" |
1326 | |
1327 | #. main text for dialog for editing external files |
1328 | -#: zim/gui/__init__.py:2053 |
1329 | +#: zim/gui/__init__.py:2078 |
1330 | #, python-format |
1331 | msgid "Editing file: %s" |
1332 | msgstr "" |
1333 | |
1334 | #. description for dialog for editing external files |
1335 | -#: zim/gui/__init__.py:2055 |
1336 | +#: zim/gui/__init__.py:2080 |
1337 | msgid "" |
1338 | "You are editing a file in an external application. You can close this dialog " |
1339 | "when you are done" |
1340 | msgstr "" |
1341 | |
1342 | #. General description of zim itself |
1343 | -#: zim/gui/__init__.py:2259 |
1344 | +#: zim/gui/__init__.py:2284 |
1345 | msgid "A desktop wiki" |
1346 | msgstr "" |
1347 | |
1348 | #. This string needs to be translated with names of the translators for this language |
1349 | -#: zim/gui/__init__.py:2267 |
1350 | +#: zim/gui/__init__.py:2292 |
1351 | msgid "translator-credits" |
1352 | msgstr "" |
1353 | |
1354 | #. Label for pageindex tab |
1355 | -#: zim/gui/__init__.py:2365 |
1356 | +#: zim/gui/__init__.py:2390 |
1357 | msgid "Index" |
1358 | msgstr "" |
1359 | |
1360 | #. page status in statusbar |
1361 | -#: zim/gui/__init__.py:2440 |
1362 | +#: zim/gui/__init__.py:2465 |
1363 | msgid "readonly" |
1364 | msgstr "" |
1365 | |
1366 | #. Label for button with backlinks in statusbar |
1367 | -#: zim/gui/__init__.py:2962 |
1368 | +#: zim/gui/__init__.py:2987 |
1369 | #, python-format |
1370 | msgid "%i _Backlink..." |
1371 | msgid_plural "%i _Backlinks..." |
1372 | @@ -972,13 +972,13 @@ |
1373 | msgstr[1] "" |
1374 | |
1375 | #. Heading of error dialog |
1376 | -#: zim/gui/__init__.py:3073 |
1377 | +#: zim/gui/__init__.py:3098 |
1378 | #, python-format |
1379 | msgid "Could not save page: %s" |
1380 | msgstr "" |
1381 | |
1382 | #. text in error dialog when saving page failed |
1383 | -#: zim/gui/__init__.py:3077 |
1384 | +#: zim/gui/__init__.py:3102 |
1385 | msgid "" |
1386 | "To continue you can save a copy of this page or discard\n" |
1387 | "any changes. If you save a copy changes will be also\n" |
1388 | @@ -986,85 +986,85 @@ |
1389 | msgstr "" |
1390 | |
1391 | #. Button in error dialog |
1392 | -#: zim/gui/__init__.py:3108 |
1393 | +#: zim/gui/__init__.py:3133 |
1394 | msgid "_Discard Changes" |
1395 | msgstr "" |
1396 | |
1397 | #. Button in error dialog |
1398 | -#: zim/gui/__init__.py:3113 |
1399 | +#: zim/gui/__init__.py:3138 |
1400 | msgid "_Save Copy" |
1401 | msgstr "" |
1402 | |
1403 | #. Dialog title |
1404 | -#: zim/gui/__init__.py:3150 |
1405 | +#: zim/gui/__init__.py:3176 |
1406 | msgid "Jump to" |
1407 | msgstr "" |
1408 | |
1409 | #. Label for page input |
1410 | -#: zim/gui/__init__.py:3155 |
1411 | +#: zim/gui/__init__.py:3181 |
1412 | msgid "Jump to Page" |
1413 | msgstr "" |
1414 | |
1415 | #. Dialog title |
1416 | -#: zim/gui/__init__.py:3174 |
1417 | +#: zim/gui/__init__.py:3200 |
1418 | msgid "New Sub Page" |
1419 | msgstr "" |
1420 | |
1421 | #. Dialog title |
1422 | -#: zim/gui/__init__.py:3175 |
1423 | +#: zim/gui/__init__.py:3201 |
1424 | msgid "New Page" |
1425 | msgstr "" |
1426 | |
1427 | #. Dialog text in 'new page' dialog |
1428 | -#: zim/gui/__init__.py:3179 |
1429 | +#: zim/gui/__init__.py:3205 |
1430 | msgid "" |
1431 | "Please note that linking to a non-existing page\n" |
1432 | "also creates a new page automatically." |
1433 | msgstr "" |
1434 | |
1435 | #. Input label |
1436 | -#: zim/gui/__init__.py:3194 |
1437 | +#: zim/gui/__init__.py:3220 |
1438 | msgid "Page Name" |
1439 | msgstr "" |
1440 | |
1441 | #. Choice label |
1442 | -#: zim/gui/__init__.py:3195 |
1443 | +#: zim/gui/__init__.py:3221 |
1444 | msgid "Page Template" |
1445 | msgstr "" |
1446 | |
1447 | #. Error when creating new page |
1448 | -#: zim/gui/__init__.py:3213 |
1449 | +#: zim/gui/__init__.py:3239 |
1450 | msgid "Page exists" |
1451 | msgstr "" |
1452 | |
1453 | #. Dialog title of file save dialog |
1454 | -#: zim/gui/__init__.py:3228 |
1455 | +#: zim/gui/__init__.py:3254 |
1456 | msgid "Save Copy" |
1457 | msgstr "" |
1458 | |
1459 | #. Dialog title |
1460 | -#: zim/gui/__init__.py:3252 |
1461 | +#: zim/gui/__init__.py:3278 |
1462 | msgid "Import Page" |
1463 | msgstr "" |
1464 | |
1465 | #. File filter for '*.txt' |
1466 | -#: zim/gui/__init__.py:3253 |
1467 | +#: zim/gui/__init__.py:3279 |
1468 | msgid "Text Files" |
1469 | msgstr "" |
1470 | |
1471 | #. Dialog title |
1472 | -#: zim/gui/__init__.py:3281 |
1473 | +#: zim/gui/__init__.py:3307 |
1474 | msgid "Move Page" |
1475 | msgstr "" |
1476 | |
1477 | #. Heading in 'move page' dialog - %s is the page name |
1478 | -#: zim/gui/__init__.py:3286 |
1479 | +#: zim/gui/__init__.py:3312 |
1480 | #, python-format |
1481 | msgid "Move page \"%s\"" |
1482 | msgstr "" |
1483 | |
1484 | #. label in MovePage dialog - %i is number of backlinks |
1485 | -#: zim/gui/__init__.py:3297 zim/gui/__init__.py:3346 |
1486 | +#: zim/gui/__init__.py:3323 zim/gui/__init__.py:3372 |
1487 | #, python-format |
1488 | msgid "Update %i page linking to this page" |
1489 | msgid_plural "Update %i pages linking to this page" |
1490 | @@ -1073,39 +1073,39 @@ |
1491 | |
1492 | #. Input label for the section to move a page to | |
1493 | #. input label |
1494 | -#: zim/gui/__init__.py:3302 zim/plugins/calendar.py:101 |
1495 | +#: zim/gui/__init__.py:3328 zim/plugins/calendar.py:101 |
1496 | msgid "Section" |
1497 | msgstr "" |
1498 | |
1499 | #. Dialog title |
1500 | -#: zim/gui/__init__.py:3331 |
1501 | +#: zim/gui/__init__.py:3357 |
1502 | msgid "Rename Page" |
1503 | msgstr "" |
1504 | |
1505 | #. label in 'rename page' dialog - %s is the page name |
1506 | -#: zim/gui/__init__.py:3335 |
1507 | +#: zim/gui/__init__.py:3361 |
1508 | #, python-format |
1509 | msgid "Rename page \"%s\"" |
1510 | msgstr "" |
1511 | |
1512 | #. Option in the 'rename page' dialog |
1513 | -#: zim/gui/__init__.py:3354 |
1514 | +#: zim/gui/__init__.py:3380 |
1515 | msgid "Update the heading of this page" |
1516 | msgstr "" |
1517 | |
1518 | #. Dialog title |
1519 | -#: zim/gui/__init__.py:3389 |
1520 | +#: zim/gui/__init__.py:3415 |
1521 | msgid "Delete Page" |
1522 | msgstr "" |
1523 | |
1524 | #. Heading in 'delete page' dialog - %s is the page name |
1525 | -#: zim/gui/__init__.py:3402 |
1526 | +#: zim/gui/__init__.py:3428 |
1527 | #, python-format |
1528 | msgid "Delete page \"%s\"?" |
1529 | msgstr "" |
1530 | |
1531 | #. Text in 'delete page' dialog - %s is the page name |
1532 | -#: zim/gui/__init__.py:3404 |
1533 | +#: zim/gui/__init__.py:3430 |
1534 | #, python-format |
1535 | msgid "" |
1536 | "Page \"%s\" and all of it's\n" |
1537 | @@ -1113,7 +1113,7 @@ |
1538 | msgstr "" |
1539 | |
1540 | #. label in DeletePage dialog - %i is number of backlinks |
1541 | -#: zim/gui/__init__.py:3417 |
1542 | +#: zim/gui/__init__.py:3443 |
1543 | #, python-format |
1544 | msgid "Remove links from %i page linking to this page" |
1545 | msgid_plural "Remove links from %i pages linking to this page" |
1546 | @@ -1121,7 +1121,7 @@ |
1547 | msgstr[1] "" |
1548 | |
1549 | #. label in the DeletePage dialog to warn user of attachments being deleted |
1550 | -#: zim/gui/__init__.py:3436 |
1551 | +#: zim/gui/__init__.py:3462 |
1552 | #, python-format |
1553 | msgid "%i file will be deleted" |
1554 | msgid_plural "%i files will be deleted" |
1555 | @@ -1129,25 +1129,25 @@ |
1556 | msgstr[1] "" |
1557 | |
1558 | #. Dialog title |
1559 | -#: zim/gui/__init__.py:3465 |
1560 | +#: zim/gui/__init__.py:3491 |
1561 | msgid "Attach File" |
1562 | msgstr "" |
1563 | |
1564 | #. Error dialog - %s is the full page name |
1565 | -#: zim/gui/__init__.py:3472 |
1566 | +#: zim/gui/__init__.py:3498 |
1567 | #, python-format |
1568 | msgid "Page \"%s\" does not have a folder for attachments" |
1569 | msgstr "" |
1570 | |
1571 | #. checkbox in the "Attach File" dialog |
1572 | -#: zim/gui/__init__.py:3477 |
1573 | +#: zim/gui/__init__.py:3503 |
1574 | msgid "Insert images as link" |
1575 | msgstr "" |
1576 | |
1577 | #. Column heading in 'open notebook' dialog | |
1578 | #. Field in web server gui | |
1579 | #. Field to select Notebook from drop down list |
1580 | -#: zim/gui/notebookdialog.py:175 zim/gui/server.py:97 |
1581 | +#: zim/gui/notebookdialog.py:175 zim/gui/server.py:98 |
1582 | #: zim/plugins/quicknote.py:428 |
1583 | msgid "Notebook" |
1584 | msgstr "" |
1585 | @@ -1177,394 +1177,405 @@ |
1586 | msgid "Folder" |
1587 | msgstr "" |
1588 | |
1589 | +#. Label for object manager |
1590 | +#: zim/gui/objectmanager.py:144 |
1591 | +msgid "No plugin is available to display this object." |
1592 | +msgstr "" |
1593 | + |
1594 | +#. Label for object manager |
1595 | +#: zim/gui/objectmanager.py:151 |
1596 | +#, python-format |
1597 | +msgid "Plugin %s is required to display this object." |
1598 | +msgstr "" |
1599 | + |
1600 | #. statusbar message |
1601 | -#: zim/gui/pageindex.py:758 |
1602 | +#: zim/gui/pageindex.py:745 |
1603 | msgid "Updating index..." |
1604 | msgstr "" |
1605 | |
1606 | #. Menu item |
1607 | -#: zim/gui/pageview.py:113 |
1608 | +#: zim/gui/pageview.py:119 |
1609 | msgid "_Undo" |
1610 | msgstr "" |
1611 | |
1612 | #. Menu item |
1613 | -#: zim/gui/pageview.py:114 |
1614 | +#: zim/gui/pageview.py:120 |
1615 | msgid "_Redo" |
1616 | msgstr "" |
1617 | |
1618 | #. Menu item |
1619 | -#: zim/gui/pageview.py:116 |
1620 | +#: zim/gui/pageview.py:122 |
1621 | msgid "Cu_t" |
1622 | msgstr "" |
1623 | |
1624 | #. Menu item |
1625 | -#: zim/gui/pageview.py:117 |
1626 | +#: zim/gui/pageview.py:123 |
1627 | msgid "_Copy" |
1628 | msgstr "" |
1629 | |
1630 | #. Menu item |
1631 | -#: zim/gui/pageview.py:118 |
1632 | +#: zim/gui/pageview.py:124 |
1633 | msgid "_Paste" |
1634 | msgstr "" |
1635 | |
1636 | #. Menu item |
1637 | -#: zim/gui/pageview.py:119 |
1638 | +#: zim/gui/pageview.py:125 |
1639 | msgid "_Delete" |
1640 | msgstr "" |
1641 | |
1642 | #. Menu item |
1643 | -#: zim/gui/pageview.py:120 |
1644 | +#: zim/gui/pageview.py:126 |
1645 | msgid "Toggle Checkbox 'V'" |
1646 | msgstr "" |
1647 | |
1648 | #. Menu item |
1649 | -#: zim/gui/pageview.py:121 |
1650 | +#: zim/gui/pageview.py:127 |
1651 | msgid "Toggle Checkbox 'X'" |
1652 | msgstr "" |
1653 | |
1654 | #. Menu item |
1655 | -#: zim/gui/pageview.py:122 |
1656 | +#: zim/gui/pageview.py:128 |
1657 | msgid "_Edit Link or Object..." |
1658 | msgstr "" |
1659 | |
1660 | #. Menu item |
1661 | -#: zim/gui/pageview.py:123 zim/gui/pageview.py:5327 |
1662 | +#: zim/gui/pageview.py:129 zim/gui/pageview.py:5480 |
1663 | msgid "_Remove Link" |
1664 | msgstr "" |
1665 | |
1666 | #. Menu item |
1667 | -#: zim/gui/pageview.py:124 |
1668 | +#: zim/gui/pageview.py:130 |
1669 | msgid "_Date and Time..." |
1670 | msgstr "" |
1671 | |
1672 | #. Menu item |
1673 | -#: zim/gui/pageview.py:125 |
1674 | +#: zim/gui/pageview.py:131 |
1675 | msgid "_Image..." |
1676 | msgstr "" |
1677 | |
1678 | #. Menu item | |
1679 | #. Menu item, |
1680 | -#: zim/gui/pageview.py:126 zim/gui/pageview.py:129 |
1681 | +#: zim/gui/pageview.py:132 zim/gui/pageview.py:135 |
1682 | msgid "Bulle_t List" |
1683 | msgstr "" |
1684 | |
1685 | #. Menu item | |
1686 | #. Menu item, |
1687 | -#: zim/gui/pageview.py:127 zim/gui/pageview.py:130 |
1688 | +#: zim/gui/pageview.py:133 zim/gui/pageview.py:136 |
1689 | msgid "_Numbered List" |
1690 | msgstr "" |
1691 | |
1692 | #. Menu item, |
1693 | -#: zim/gui/pageview.py:128 zim/gui/pageview.py:131 |
1694 | +#: zim/gui/pageview.py:134 zim/gui/pageview.py:137 |
1695 | msgid "Checkbo_x List" |
1696 | msgstr "" |
1697 | |
1698 | #. Menu item |
1699 | -#: zim/gui/pageview.py:132 |
1700 | +#: zim/gui/pageview.py:138 |
1701 | msgid "Text From _File..." |
1702 | msgstr "" |
1703 | |
1704 | #. Menu item |
1705 | -#: zim/gui/pageview.py:133 |
1706 | +#: zim/gui/pageview.py:139 |
1707 | msgid "_Link..." |
1708 | msgstr "" |
1709 | |
1710 | #. Menu item | |
1711 | #. Dialog title |
1712 | -#: zim/gui/pageview.py:133 zim/gui/pageview.py:6354 |
1713 | +#: zim/gui/pageview.py:139 zim/gui/pageview.py:6539 |
1714 | msgid "Insert Link" |
1715 | msgstr "" |
1716 | |
1717 | #. Menu item |
1718 | -#: zim/gui/pageview.py:134 |
1719 | +#: zim/gui/pageview.py:140 |
1720 | msgid "_Clear Formatting" |
1721 | msgstr "" |
1722 | |
1723 | #. Menu item |
1724 | -#: zim/gui/pageview.py:135 |
1725 | +#: zim/gui/pageview.py:141 |
1726 | msgid "_Find..." |
1727 | msgstr "" |
1728 | |
1729 | #. Menu item |
1730 | -#: zim/gui/pageview.py:137 |
1731 | +#: zim/gui/pageview.py:143 |
1732 | msgid "Find Ne_xt" |
1733 | msgstr "" |
1734 | |
1735 | #. Menu item |
1736 | -#: zim/gui/pageview.py:139 |
1737 | +#: zim/gui/pageview.py:145 |
1738 | msgid "Find Pre_vious" |
1739 | msgstr "" |
1740 | |
1741 | #. Menu item |
1742 | -#: zim/gui/pageview.py:141 |
1743 | +#: zim/gui/pageview.py:147 |
1744 | msgid "_Replace..." |
1745 | msgstr "" |
1746 | |
1747 | #. Menu item |
1748 | -#: zim/gui/pageview.py:142 |
1749 | +#: zim/gui/pageview.py:148 |
1750 | msgid "Word Count..." |
1751 | msgstr "" |
1752 | |
1753 | #. Menu item |
1754 | -#: zim/gui/pageview.py:143 |
1755 | +#: zim/gui/pageview.py:149 |
1756 | msgid "_Zoom In" |
1757 | msgstr "" |
1758 | |
1759 | #. Menu item |
1760 | -#: zim/gui/pageview.py:145 |
1761 | +#: zim/gui/pageview.py:151 |
1762 | msgid "Zoom _Out" |
1763 | msgstr "" |
1764 | |
1765 | #. Menu item to reset zoom |
1766 | -#: zim/gui/pageview.py:146 |
1767 | +#: zim/gui/pageview.py:152 |
1768 | msgid "_Normal Size" |
1769 | msgstr "" |
1770 | |
1771 | #. Menu title |
1772 | -#: zim/gui/pageview.py:149 |
1773 | +#: zim/gui/pageview.py:155 |
1774 | msgid "New _Attachment" |
1775 | msgstr "" |
1776 | |
1777 | #. Menu item in "Insert > New File Attachment" submenu |
1778 | -#: zim/gui/pageview.py:152 |
1779 | +#: zim/gui/pageview.py:158 |
1780 | msgid "File _Templates..." |
1781 | msgstr "" |
1782 | |
1783 | #. Menu item |
1784 | -#: zim/gui/pageview.py:157 |
1785 | +#: zim/gui/pageview.py:163 |
1786 | msgid "Heading _1" |
1787 | msgstr "" |
1788 | |
1789 | #. Menu item |
1790 | -#: zim/gui/pageview.py:157 |
1791 | +#: zim/gui/pageview.py:163 |
1792 | msgid "Heading 1" |
1793 | msgstr "" |
1794 | |
1795 | #. Menu item |
1796 | -#: zim/gui/pageview.py:158 |
1797 | +#: zim/gui/pageview.py:164 |
1798 | msgid "Heading _2" |
1799 | msgstr "" |
1800 | |
1801 | #. Menu item |
1802 | -#: zim/gui/pageview.py:158 |
1803 | +#: zim/gui/pageview.py:164 |
1804 | msgid "Heading 2" |
1805 | msgstr "" |
1806 | |
1807 | #. Menu item |
1808 | -#: zim/gui/pageview.py:159 |
1809 | +#: zim/gui/pageview.py:165 |
1810 | msgid "Heading _3" |
1811 | msgstr "" |
1812 | |
1813 | #. Menu item |
1814 | -#: zim/gui/pageview.py:159 |
1815 | +#: zim/gui/pageview.py:165 |
1816 | msgid "Heading 3" |
1817 | msgstr "" |
1818 | |
1819 | #. Menu item |
1820 | -#: zim/gui/pageview.py:160 |
1821 | +#: zim/gui/pageview.py:166 |
1822 | msgid "Heading _4" |
1823 | msgstr "" |
1824 | |
1825 | #. Menu item |
1826 | -#: zim/gui/pageview.py:160 |
1827 | +#: zim/gui/pageview.py:166 |
1828 | msgid "Heading 4" |
1829 | msgstr "" |
1830 | |
1831 | #. Menu item |
1832 | -#: zim/gui/pageview.py:161 |
1833 | +#: zim/gui/pageview.py:167 |
1834 | msgid "Heading _5" |
1835 | msgstr "" |
1836 | |
1837 | #. Menu item |
1838 | -#: zim/gui/pageview.py:161 |
1839 | +#: zim/gui/pageview.py:167 |
1840 | msgid "Heading 5" |
1841 | msgstr "" |
1842 | |
1843 | #. Menu item |
1844 | -#: zim/gui/pageview.py:162 zim/gui/pageview.py:173 |
1845 | +#: zim/gui/pageview.py:168 zim/gui/pageview.py:179 |
1846 | msgid "_Strong" |
1847 | msgstr "" |
1848 | |
1849 | #. Menu item |
1850 | -#: zim/gui/pageview.py:162 zim/gui/pageview.py:173 |
1851 | +#: zim/gui/pageview.py:168 zim/gui/pageview.py:179 |
1852 | msgid "Strong" |
1853 | msgstr "" |
1854 | |
1855 | #. Menu item |
1856 | -#: zim/gui/pageview.py:163 zim/gui/pageview.py:174 |
1857 | +#: zim/gui/pageview.py:169 zim/gui/pageview.py:180 |
1858 | msgid "_Emphasis" |
1859 | msgstr "" |
1860 | |
1861 | #. Menu item |
1862 | -#: zim/gui/pageview.py:163 zim/gui/pageview.py:174 |
1863 | +#: zim/gui/pageview.py:169 zim/gui/pageview.py:180 |
1864 | msgid "Emphasis" |
1865 | msgstr "" |
1866 | |
1867 | #. Menu item |
1868 | -#: zim/gui/pageview.py:164 zim/gui/pageview.py:175 |
1869 | +#: zim/gui/pageview.py:170 zim/gui/pageview.py:181 |
1870 | msgid "_Mark" |
1871 | msgstr "" |
1872 | |
1873 | #. Menu item |
1874 | -#: zim/gui/pageview.py:164 zim/gui/pageview.py:175 |
1875 | +#: zim/gui/pageview.py:170 zim/gui/pageview.py:181 |
1876 | msgid "Mark" |
1877 | msgstr "" |
1878 | |
1879 | #. Menu item |
1880 | -#: zim/gui/pageview.py:165 zim/gui/pageview.py:176 |
1881 | +#: zim/gui/pageview.py:171 zim/gui/pageview.py:182 |
1882 | msgid "_Strike" |
1883 | msgstr "" |
1884 | |
1885 | #. Menu item |
1886 | -#: zim/gui/pageview.py:165 zim/gui/pageview.py:176 |
1887 | +#: zim/gui/pageview.py:171 zim/gui/pageview.py:182 |
1888 | msgid "Strike" |
1889 | msgstr "" |
1890 | |
1891 | #. Menu item |
1892 | -#: zim/gui/pageview.py:166 |
1893 | +#: zim/gui/pageview.py:172 |
1894 | msgid "_Subscript" |
1895 | msgstr "" |
1896 | |
1897 | #. Menu item |
1898 | -#: zim/gui/pageview.py:167 |
1899 | +#: zim/gui/pageview.py:173 |
1900 | msgid "_Superscript" |
1901 | msgstr "" |
1902 | |
1903 | #. Menu item |
1904 | -#: zim/gui/pageview.py:168 |
1905 | +#: zim/gui/pageview.py:174 |
1906 | msgid "_Verbatim" |
1907 | msgstr "" |
1908 | |
1909 | #. Menu item |
1910 | -#: zim/gui/pageview.py:168 |
1911 | +#: zim/gui/pageview.py:174 |
1912 | msgid "Verbatim" |
1913 | msgstr "" |
1914 | |
1915 | #. option in preferences dialog |
1916 | -#: zim/gui/pageview.py:183 |
1917 | +#: zim/gui/pageview.py:189 |
1918 | msgid "" |
1919 | "Use the <Enter> key to follow links\n" |
1920 | "(If disabled you can still use <Alt><Enter>)" |
1921 | msgstr "" |
1922 | |
1923 | #. option in preferences dialog |
1924 | -#: zim/gui/pageview.py:186 |
1925 | +#: zim/gui/pageview.py:192 |
1926 | msgid "Show the cursor also for pages that can not be edited" |
1927 | msgstr "" |
1928 | |
1929 | #. option in preferences dialog |
1930 | -#: zim/gui/pageview.py:189 |
1931 | +#: zim/gui/pageview.py:195 |
1932 | msgid "Automatically turn \"CamelCase\" words into links" |
1933 | msgstr "" |
1934 | |
1935 | #. option in preferences dialog |
1936 | -#: zim/gui/pageview.py:192 |
1937 | +#: zim/gui/pageview.py:198 |
1938 | msgid "Automatically turn file paths into links" |
1939 | msgstr "" |
1940 | |
1941 | #. option in preferences dialog |
1942 | -#: zim/gui/pageview.py:195 |
1943 | +#: zim/gui/pageview.py:201 |
1944 | msgid "Automatically select the current word when you apply formatting" |
1945 | msgstr "" |
1946 | |
1947 | #. option in preferences dialog |
1948 | -#: zim/gui/pageview.py:198 |
1949 | +#: zim/gui/pageview.py:204 |
1950 | msgid "" |
1951 | "Unindent on <BackSpace>\n" |
1952 | "(If disabled you can still use <Shift><Tab>)" |
1953 | msgstr "" |
1954 | |
1955 | #. option in preferences dialog |
1956 | -#: zim/gui/pageview.py:201 |
1957 | +#: zim/gui/pageview.py:207 |
1958 | msgid "Repeated clicking a checkbox cyles through the checkbox states" |
1959 | msgstr "" |
1960 | |
1961 | #. option in preferences dialog |
1962 | -#: zim/gui/pageview.py:204 |
1963 | +#: zim/gui/pageview.py:210 |
1964 | msgid "(Un-)Indenting a list item also change any sub-items" |
1965 | msgstr "" |
1966 | |
1967 | #. option in preferences dialog |
1968 | -#: zim/gui/pageview.py:207 |
1969 | +#: zim/gui/pageview.py:213 |
1970 | msgid "Checking a checkbox also change any sub-items" |
1971 | msgstr "" |
1972 | |
1973 | #. option in preferences dialog |
1974 | -#: zim/gui/pageview.py:210 |
1975 | +#: zim/gui/pageview.py:216 |
1976 | msgid "Reformat wiki markup on the fly" |
1977 | msgstr "" |
1978 | |
1979 | #. option in preferences dialog |
1980 | -#: zim/gui/pageview.py:213 |
1981 | +#: zim/gui/pageview.py:219 |
1982 | msgid "Default format for copying text to the clipboard" |
1983 | msgstr "" |
1984 | |
1985 | #. option in preferences dialog |
1986 | -#: zim/gui/pageview.py:216 |
1987 | +#: zim/gui/pageview.py:222 |
1988 | msgid "Folder with templates for attachment files" |
1989 | msgstr "" |
1990 | |
1991 | #. error when unknown interwiki link is clicked |
1992 | -#: zim/gui/pageview.py:5216 |
1993 | +#: zim/gui/pageview.py:5369 |
1994 | #, python-format |
1995 | msgid "No such wiki defined: %s" |
1996 | msgstr "" |
1997 | |
1998 | #. menu item for context menu of editor |
1999 | -#: zim/gui/pageview.py:5269 |
2000 | +#: zim/gui/pageview.py:5422 |
2001 | msgid "Copy _As..." |
2002 | msgstr "" |
2003 | |
2004 | #. Context menu item for pageview to move selected text to new/other page |
2005 | -#: zim/gui/pageview.py:5276 |
2006 | +#: zim/gui/pageview.py:5429 |
2007 | msgid "Move Selected Text..." |
2008 | msgstr "" |
2009 | |
2010 | #. menu item in context menu for image |
2011 | -#: zim/gui/pageview.py:5334 |
2012 | +#: zim/gui/pageview.py:5487 |
2013 | msgid "_Edit Properties" |
2014 | msgstr "" |
2015 | |
2016 | #. menu item in context menu |
2017 | -#: zim/gui/pageview.py:5336 |
2018 | +#: zim/gui/pageview.py:5489 |
2019 | msgid "_Edit Link" |
2020 | msgstr "" |
2021 | |
2022 | #. context menu item |
2023 | -#: zim/gui/pageview.py:5356 zim/gui/pageview.py:5360 zim/gui/pageview.py:5367 |
2024 | +#: zim/gui/pageview.py:5509 zim/gui/pageview.py:5513 zim/gui/pageview.py:5520 |
2025 | msgid "Copy _Link" |
2026 | msgstr "" |
2027 | |
2028 | #. context menu item |
2029 | -#: zim/gui/pageview.py:5364 |
2030 | +#: zim/gui/pageview.py:5517 |
2031 | msgid "Copy Email Address" |
2032 | msgstr "" |
2033 | |
2034 | #. menu item to open containing folder of files |
2035 | -#: zim/gui/pageview.py:5375 |
2036 | +#: zim/gui/pageview.py:5528 |
2037 | msgid "Open Folder" |
2038 | msgstr "" |
2039 | |
2040 | #. menu item for sub menu with applications | |
2041 | #. menu item |
2042 | -#: zim/gui/pageview.py:5384 zim/gui/pageview.py:5395 |
2043 | -#: zim/plugins/attachmentbrowser.py:687 |
2044 | +#: zim/gui/pageview.py:5537 zim/gui/pageview.py:5548 |
2045 | +#: zim/plugins/attachmentbrowser.py:688 |
2046 | msgid "Open With..." |
2047 | msgstr "" |
2048 | |
2049 | #. menu item to open a link or file | |
2050 | #. menu item to open file or folder |
2051 | -#: zim/gui/pageview.py:5415 zim/plugins/attachmentbrowser.py:693 |
2052 | +#: zim/gui/pageview.py:5568 zim/plugins/attachmentbrowser.py:694 |
2053 | msgid "_Open" |
2054 | msgstr "" |
2055 | |
2056 | #. message when no file templates are found in ~/Templates |
2057 | -#: zim/gui/pageview.py:5654 |
2058 | +#: zim/gui/pageview.py:5810 |
2059 | msgid "No templates installed" |
2060 | msgstr "" |
2061 | |
2062 | #. Text in a question dialog for creating a folder, %s is the folder path |
2063 | -#: zim/gui/pageview.py:5717 |
2064 | +#: zim/gui/pageview.py:5873 |
2065 | #, python-format |
2066 | msgid "" |
2067 | "The folder\n" |
2068 | @@ -1574,317 +1585,317 @@ |
2069 | msgstr "" |
2070 | |
2071 | #. Dialog title |
2072 | -#: zim/gui/pageview.py:6020 |
2073 | +#: zim/gui/pageview.py:6205 |
2074 | msgid "Insert Date and Time" |
2075 | msgstr "" |
2076 | |
2077 | #. expander label in "insert date" dialog |
2078 | -#: zim/gui/pageview.py:6049 |
2079 | +#: zim/gui/pageview.py:6234 |
2080 | msgid "_Calendar" |
2081 | msgstr "" |
2082 | |
2083 | #. check box in InsertDate dialog |
2084 | -#: zim/gui/pageview.py:6063 |
2085 | +#: zim/gui/pageview.py:6248 |
2086 | msgid "_Link to date" |
2087 | msgstr "" |
2088 | |
2089 | #. Dialog title |
2090 | -#: zim/gui/pageview.py:6156 |
2091 | +#: zim/gui/pageview.py:6341 |
2092 | msgid "Insert Image" |
2093 | msgstr "" |
2094 | |
2095 | #. checkbox in the "Insert Image" dialog |
2096 | -#: zim/gui/pageview.py:6163 |
2097 | +#: zim/gui/pageview.py:6348 |
2098 | msgid "Attach image first" |
2099 | msgstr "" |
2100 | |
2101 | #. Error message when trying to insert a not supported file as image |
2102 | -#: zim/gui/pageview.py:6178 |
2103 | +#: zim/gui/pageview.py:6363 |
2104 | #, python-format |
2105 | msgid "File type not supported: %s" |
2106 | msgstr "" |
2107 | |
2108 | #. Dialog title |
2109 | -#: zim/gui/pageview.py:6206 |
2110 | +#: zim/gui/pageview.py:6391 |
2111 | msgid "Edit Image" |
2112 | msgstr "" |
2113 | |
2114 | #. Input in 'edit image' dialog |
2115 | -#: zim/gui/pageview.py:6225 |
2116 | +#: zim/gui/pageview.py:6410 |
2117 | msgid "Location" |
2118 | msgstr "" |
2119 | |
2120 | #. Input in 'edit image' dialog | |
2121 | #. Input in 'insert link' dialog |
2122 | -#: zim/gui/pageview.py:6226 zim/gui/pageview.py:6360 |
2123 | +#: zim/gui/pageview.py:6411 zim/gui/pageview.py:6545 |
2124 | msgid "Link to" |
2125 | msgstr "" |
2126 | |
2127 | #. Input in 'edit image' dialog |
2128 | -#: zim/gui/pageview.py:6227 |
2129 | +#: zim/gui/pageview.py:6412 |
2130 | msgid "Width" |
2131 | msgstr "" |
2132 | |
2133 | #. Input in 'edit image' dialog |
2134 | -#: zim/gui/pageview.py:6228 |
2135 | +#: zim/gui/pageview.py:6413 |
2136 | msgid "Height" |
2137 | msgstr "" |
2138 | |
2139 | #. Button in 'edit image' dialog |
2140 | -#: zim/gui/pageview.py:6236 |
2141 | +#: zim/gui/pageview.py:6421 |
2142 | msgid "_Reset Size" |
2143 | msgstr "" |
2144 | |
2145 | #. Dialog title |
2146 | -#: zim/gui/pageview.py:6331 |
2147 | +#: zim/gui/pageview.py:6516 |
2148 | msgid "Insert Text From File" |
2149 | msgstr "" |
2150 | |
2151 | #. Dialog title |
2152 | -#: zim/gui/pageview.py:6353 |
2153 | +#: zim/gui/pageview.py:6538 |
2154 | msgid "Edit Link" |
2155 | msgstr "" |
2156 | |
2157 | #. Dialog button |
2158 | -#: zim/gui/pageview.py:6357 |
2159 | +#: zim/gui/pageview.py:6542 |
2160 | msgid "_Link" |
2161 | msgstr "" |
2162 | |
2163 | #. Input in 'insert link' dialog |
2164 | -#: zim/gui/pageview.py:6361 |
2165 | +#: zim/gui/pageview.py:6546 |
2166 | msgid "Text" |
2167 | msgstr "" |
2168 | |
2169 | #. button in find bar and find & replace dialog |
2170 | -#: zim/gui/pageview.py:6460 |
2171 | +#: zim/gui/pageview.py:6645 |
2172 | msgid "_Next" |
2173 | msgstr "" |
2174 | |
2175 | #. button in find bar and find & replace dialog |
2176 | -#: zim/gui/pageview.py:6466 |
2177 | +#: zim/gui/pageview.py:6651 |
2178 | msgid "_Previous" |
2179 | msgstr "" |
2180 | |
2181 | #. checkbox option in find bar and find & replace dialog |
2182 | -#: zim/gui/pageview.py:6472 |
2183 | +#: zim/gui/pageview.py:6657 |
2184 | msgid "Match _case" |
2185 | msgstr "" |
2186 | |
2187 | #. checkbox option in find bar and find & replace dialog |
2188 | -#: zim/gui/pageview.py:6477 |
2189 | +#: zim/gui/pageview.py:6662 |
2190 | msgid "Whole _word" |
2191 | msgstr "" |
2192 | |
2193 | #. checkbox option in find bar and find & replace dialog |
2194 | -#: zim/gui/pageview.py:6482 |
2195 | +#: zim/gui/pageview.py:6667 |
2196 | msgid "_Regular expression" |
2197 | msgstr "" |
2198 | |
2199 | #. checkbox option in find bar and find & replace dialog |
2200 | -#: zim/gui/pageview.py:6487 |
2201 | +#: zim/gui/pageview.py:6672 |
2202 | msgid "_Highlight" |
2203 | msgstr "" |
2204 | |
2205 | #. label for input in find bar on bottom of page |
2206 | -#: zim/gui/pageview.py:6573 |
2207 | +#: zim/gui/pageview.py:6758 |
2208 | msgid "Find" |
2209 | msgstr "" |
2210 | |
2211 | #. Options button |
2212 | -#: zim/gui/pageview.py:6603 |
2213 | +#: zim/gui/pageview.py:6788 |
2214 | msgid "Options" |
2215 | msgstr "" |
2216 | |
2217 | #. Dialog title |
2218 | -#: zim/gui/pageview.py:6648 |
2219 | +#: zim/gui/pageview.py:6833 |
2220 | msgid "Find and Replace" |
2221 | msgstr "" |
2222 | |
2223 | #. input label in find & replace dialog |
2224 | -#: zim/gui/pageview.py:6658 |
2225 | +#: zim/gui/pageview.py:6843 |
2226 | msgid "Find what" |
2227 | msgstr "" |
2228 | |
2229 | #. input label in find & replace dialog |
2230 | -#: zim/gui/pageview.py:6668 |
2231 | +#: zim/gui/pageview.py:6853 |
2232 | msgid "Replace with" |
2233 | msgstr "" |
2234 | |
2235 | #. Button in search & replace dialog |
2236 | -#: zim/gui/pageview.py:6680 |
2237 | +#: zim/gui/pageview.py:6865 |
2238 | msgid "_Replace" |
2239 | msgstr "" |
2240 | |
2241 | #. Button in search & replace dialog |
2242 | -#: zim/gui/pageview.py:6685 |
2243 | +#: zim/gui/pageview.py:6870 |
2244 | msgid "Replace _All" |
2245 | msgstr "" |
2246 | |
2247 | #. Dialog title |
2248 | -#: zim/gui/pageview.py:6712 |
2249 | +#: zim/gui/pageview.py:6897 |
2250 | msgid "Word Count" |
2251 | msgstr "" |
2252 | |
2253 | #. label in word count dialog |
2254 | -#: zim/gui/pageview.py:6755 |
2255 | +#: zim/gui/pageview.py:6940 |
2256 | msgid "Paragraph" |
2257 | msgstr "" |
2258 | |
2259 | #. label in word count dialog |
2260 | -#: zim/gui/pageview.py:6756 |
2261 | +#: zim/gui/pageview.py:6941 |
2262 | msgid "Selection" |
2263 | msgstr "" |
2264 | |
2265 | #. label in word count dialog |
2266 | -#: zim/gui/pageview.py:6757 |
2267 | +#: zim/gui/pageview.py:6942 |
2268 | msgid "Words" |
2269 | msgstr "" |
2270 | |
2271 | #. label in word count dialog |
2272 | -#: zim/gui/pageview.py:6758 |
2273 | +#: zim/gui/pageview.py:6943 |
2274 | msgid "Lines" |
2275 | msgstr "" |
2276 | |
2277 | #. label in word count dialog |
2278 | -#: zim/gui/pageview.py:6759 |
2279 | +#: zim/gui/pageview.py:6944 |
2280 | msgid "Characters" |
2281 | msgstr "" |
2282 | |
2283 | #. label in word count dialog |
2284 | -#: zim/gui/pageview.py:6760 |
2285 | +#: zim/gui/pageview.py:6945 |
2286 | msgid "Characters excluding spaces" |
2287 | msgstr "" |
2288 | |
2289 | #. Dialog title |
2290 | -#: zim/gui/pageview.py:6799 |
2291 | +#: zim/gui/pageview.py:6984 |
2292 | msgid "Move Text to Other Page" |
2293 | msgstr "" |
2294 | |
2295 | #. Button label |
2296 | -#: zim/gui/pageview.py:6800 |
2297 | +#: zim/gui/pageview.py:6985 |
2298 | msgid "_Move" |
2299 | msgstr "" |
2300 | |
2301 | #. Input in 'move text' dialog |
2302 | -#: zim/gui/pageview.py:6815 |
2303 | +#: zim/gui/pageview.py:7000 |
2304 | msgid "Move text to" |
2305 | msgstr "" |
2306 | |
2307 | #. Input in 'move text' dialog |
2308 | -#: zim/gui/pageview.py:6816 |
2309 | +#: zim/gui/pageview.py:7001 |
2310 | msgid "Leave link to new page" |
2311 | msgstr "" |
2312 | |
2313 | #. Input in 'move text' dialog |
2314 | -#: zim/gui/pageview.py:6817 |
2315 | +#: zim/gui/pageview.py:7002 |
2316 | msgid "Open new page" |
2317 | msgstr "" |
2318 | |
2319 | #. Dialog title |
2320 | -#: zim/gui/pageview.py:6855 |
2321 | +#: zim/gui/pageview.py:7040 |
2322 | msgid "New File" |
2323 | msgstr "" |
2324 | |
2325 | #. Tab in preferences dialog |
2326 | +#: zim/gui/preferencesdialog.py:20 |
2327 | +msgid "Interface" |
2328 | +msgstr "" |
2329 | + |
2330 | +#. Tab in preferences dialog |
2331 | #: zim/gui/preferencesdialog.py:21 |
2332 | -msgid "Interface" |
2333 | -msgstr "" |
2334 | - |
2335 | -#. Tab in preferences dialog |
2336 | -#: zim/gui/preferencesdialog.py:22 |
2337 | msgid "Editing" |
2338 | msgstr "" |
2339 | |
2340 | #. Dialog title |
2341 | -#: zim/gui/preferencesdialog.py:32 |
2342 | +#: zim/gui/preferencesdialog.py:31 |
2343 | msgid "Preferences" |
2344 | msgstr "" |
2345 | |
2346 | #. Heading in preferences dialog |
2347 | -#: zim/gui/preferencesdialog.py:78 |
2348 | +#: zim/gui/preferencesdialog.py:77 |
2349 | msgid "Plugins" |
2350 | msgstr "" |
2351 | |
2352 | #. Heading in preferences dialog |
2353 | -#: zim/gui/preferencesdialog.py:88 |
2354 | +#: zim/gui/preferencesdialog.py:87 |
2355 | msgid "Applications" |
2356 | msgstr "" |
2357 | |
2358 | #. option in preferences dialog |
2359 | -#: zim/gui/preferencesdialog.py:95 |
2360 | +#: zim/gui/preferencesdialog.py:94 |
2361 | msgid "Use a custom font" |
2362 | msgstr "" |
2363 | |
2364 | #. Button in plugin tab |
2365 | -#: zim/gui/preferencesdialog.py:209 |
2366 | +#: zim/gui/preferencesdialog.py:208 |
2367 | msgid "_More" |
2368 | msgstr "" |
2369 | |
2370 | #. Button in plugin tab |
2371 | -#: zim/gui/preferencesdialog.py:214 |
2372 | +#: zim/gui/preferencesdialog.py:213 |
2373 | msgid "C_onfigure" |
2374 | msgstr "" |
2375 | |
2376 | #. label for button with URL |
2377 | -#: zim/gui/preferencesdialog.py:223 |
2378 | +#: zim/gui/preferencesdialog.py:226 |
2379 | msgid "Get more plugins online" |
2380 | msgstr "" |
2381 | |
2382 | #. Heading in plugins tab of preferences dialog |
2383 | -#: zim/gui/preferencesdialog.py:248 |
2384 | +#: zim/gui/preferencesdialog.py:251 |
2385 | msgid "Dependencies" |
2386 | msgstr "" |
2387 | |
2388 | #. label in plugin info in preferences dialog |
2389 | -#: zim/gui/preferencesdialog.py:252 |
2390 | +#: zim/gui/preferencesdialog.py:255 |
2391 | msgid "No dependencies" |
2392 | msgstr "" |
2393 | |
2394 | #. dependency is OK |
2395 | -#: zim/gui/preferencesdialog.py:258 |
2396 | +#: zim/gui/preferencesdialog.py:261 |
2397 | msgid "OK" |
2398 | msgstr "" |
2399 | |
2400 | #. dependency failed |
2401 | -#: zim/gui/preferencesdialog.py:260 zim/gui/preferencesdialog.py:263 |
2402 | +#: zim/gui/preferencesdialog.py:263 zim/gui/preferencesdialog.py:266 |
2403 | msgid "Failed" |
2404 | msgstr "" |
2405 | |
2406 | #. optional dependency |
2407 | -#: zim/gui/preferencesdialog.py:264 |
2408 | +#: zim/gui/preferencesdialog.py:267 |
2409 | msgid "Optional" |
2410 | msgstr "" |
2411 | |
2412 | #. Heading in plugins tab of preferences dialog | |
2413 | #. Column header versions dialog |
2414 | -#: zim/gui/preferencesdialog.py:268 |
2415 | +#: zim/gui/preferencesdialog.py:271 |
2416 | #: zim/plugins/versioncontrol/__init__.py:1180 |
2417 | msgid "Author" |
2418 | msgstr "" |
2419 | |
2420 | #. Column in plugin tab |
2421 | -#: zim/gui/preferencesdialog.py:349 |
2422 | +#: zim/gui/preferencesdialog.py:353 |
2423 | msgid "Enabled" |
2424 | msgstr "" |
2425 | |
2426 | #. Column in plugin tab |
2427 | -#: zim/gui/preferencesdialog.py:352 |
2428 | +#: zim/gui/preferencesdialog.py:356 |
2429 | msgid "Plugin" |
2430 | msgstr "" |
2431 | |
2432 | #. Dialog title |
2433 | -#: zim/gui/preferencesdialog.py:359 |
2434 | +#: zim/gui/preferencesdialog.py:363 |
2435 | msgid "Configure Plugin" |
2436 | msgstr "" |
2437 | |
2438 | #. Heading for 'configure plugin' dialog - %s is the plugin name |
2439 | -#: zim/gui/preferencesdialog.py:364 |
2440 | +#: zim/gui/preferencesdialog.py:368 |
2441 | #, python-format |
2442 | msgid "Options for plugin %s" |
2443 | msgstr "" |
2444 | |
2445 | #. button in preferences dialog to change default text editor |
2446 | -#: zim/gui/preferencesdialog.py:398 |
2447 | +#: zim/gui/preferencesdialog.py:402 |
2448 | msgid "Set default text editor" |
2449 | msgstr "" |
2450 | |
2451 | @@ -1926,12 +1937,12 @@ |
2452 | msgstr "" |
2453 | |
2454 | #. checkbox option in search dialog |
2455 | -#: zim/gui/searchdialog.py:58 |
2456 | +#: zim/gui/searchdialog.py:59 |
2457 | msgid "Limit search to the current page and sub-pages" |
2458 | msgstr "" |
2459 | |
2460 | #. Column header search dialog |
2461 | -#: zim/gui/searchdialog.py:151 |
2462 | +#: zim/gui/searchdialog.py:152 |
2463 | msgid "Score" |
2464 | msgstr "" |
2465 | |
2466 | @@ -1946,22 +1957,22 @@ |
2467 | msgstr "" |
2468 | |
2469 | #. Checkbox in web server gui |
2470 | -#: zim/gui/server.py:81 |
2471 | +#: zim/gui/server.py:82 |
2472 | msgid "Allow public access" |
2473 | msgstr "" |
2474 | |
2475 | #. Field in web server gui for HTTP port (e.g. port 80) |
2476 | -#: zim/gui/server.py:99 |
2477 | +#: zim/gui/server.py:100 |
2478 | msgid "Port" |
2479 | msgstr "" |
2480 | |
2481 | #. Status in web server gui |
2482 | -#: zim/gui/server.py:158 |
2483 | +#: zim/gui/server.py:159 |
2484 | msgid "Server started" |
2485 | msgstr "" |
2486 | |
2487 | #. Status in web server gui |
2488 | -#: zim/gui/server.py:199 |
2489 | +#: zim/gui/server.py:200 |
2490 | msgid "Server stopped" |
2491 | msgstr "" |
2492 | |
2493 | @@ -1977,114 +1988,114 @@ |
2494 | msgstr "" |
2495 | |
2496 | #. Dialog title |
2497 | -#: zim/gui/templateeditordialog.py:155 |
2498 | +#: zim/gui/templateeditordialog.py:156 |
2499 | msgid "Copy Template" |
2500 | msgstr "" |
2501 | |
2502 | #. dialog title |
2503 | -#: zim/gui/widgets.py:572 zim/gui/widgets.py:1753 |
2504 | +#: zim/gui/widgets.py:573 zim/gui/widgets.py:1759 |
2505 | msgid "Select File" |
2506 | msgstr "" |
2507 | |
2508 | #. menu item in context menu |
2509 | -#: zim/gui/widgets.py:710 |
2510 | +#: zim/gui/widgets.py:712 |
2511 | msgid "Expand _All" |
2512 | msgstr "" |
2513 | |
2514 | #. menu item in context menu |
2515 | -#: zim/gui/widgets.py:712 |
2516 | +#: zim/gui/widgets.py:714 |
2517 | msgid "_Collapse All" |
2518 | msgstr "" |
2519 | |
2520 | #. tooltip for the inline icon to clear a text entry widget |
2521 | -#: zim/gui/widgets.py:1555 |
2522 | +#: zim/gui/widgets.py:1561 |
2523 | msgid "Clear" |
2524 | msgstr "" |
2525 | |
2526 | #. dialog title |
2527 | -#: zim/gui/widgets.py:1749 |
2528 | +#: zim/gui/widgets.py:1755 |
2529 | msgid "Select Folder" |
2530 | msgstr "" |
2531 | |
2532 | #. dialog title |
2533 | -#: zim/gui/widgets.py:1751 |
2534 | +#: zim/gui/widgets.py:1757 |
2535 | msgid "Select Image" |
2536 | msgstr "" |
2537 | |
2538 | #. default text for empty page section selection |
2539 | -#: zim/gui/widgets.py:1884 |
2540 | +#: zim/gui/widgets.py:1890 |
2541 | msgid "<Top>" |
2542 | msgstr "" |
2543 | |
2544 | #. Option for placement of plugin widgets |
2545 | -#: zim/gui/widgets.py:2178 |
2546 | +#: zim/gui/widgets.py:2184 |
2547 | msgid "Left Side Pane" |
2548 | msgstr "" |
2549 | |
2550 | #. Option for placement of plugin widgets |
2551 | -#: zim/gui/widgets.py:2179 |
2552 | +#: zim/gui/widgets.py:2185 |
2553 | msgid "Right Side Pane" |
2554 | msgstr "" |
2555 | |
2556 | #. Option for placement of plugin widgets |
2557 | -#: zim/gui/widgets.py:2180 |
2558 | +#: zim/gui/widgets.py:2186 |
2559 | msgid "Bottom Pane" |
2560 | msgstr "" |
2561 | |
2562 | #. Option for placement of plugin widgets |
2563 | -#: zim/gui/widgets.py:2181 |
2564 | +#: zim/gui/widgets.py:2187 |
2565 | msgid "Top Pane" |
2566 | msgstr "" |
2567 | |
2568 | #. Option for placement of plugin widgets |
2569 | -#: zim/gui/widgets.py:2185 |
2570 | +#: zim/gui/widgets.py:2191 |
2571 | msgid "Top Left" |
2572 | msgstr "" |
2573 | |
2574 | #. Option for placement of plugin widgets |
2575 | -#: zim/gui/widgets.py:2186 |
2576 | +#: zim/gui/widgets.py:2192 |
2577 | msgid "Bottom Left" |
2578 | msgstr "" |
2579 | |
2580 | #. Option for placement of plugin widgets |
2581 | -#: zim/gui/widgets.py:2187 |
2582 | +#: zim/gui/widgets.py:2193 |
2583 | msgid "Top Right" |
2584 | msgstr "" |
2585 | |
2586 | #. Option for placement of plugin widgets |
2587 | -#: zim/gui/widgets.py:2188 |
2588 | +#: zim/gui/widgets.py:2194 |
2589 | msgid "Bottom Right" |
2590 | msgstr "" |
2591 | |
2592 | #. generic error dialog text |
2593 | -#: zim/gui/widgets.py:3231 |
2594 | +#: zim/gui/widgets.py:3241 |
2595 | msgid "" |
2596 | "When reporting this bug please include\n" |
2597 | "the information from the text box below" |
2598 | msgstr "" |
2599 | |
2600 | #. Filter in open file dialog, shows all files (*) |
2601 | -#: zim/gui/widgets.py:3552 |
2602 | +#: zim/gui/widgets.py:3562 |
2603 | msgid "All Files" |
2604 | msgstr "" |
2605 | |
2606 | #. Filter in open file dialog, shows image files only |
2607 | -#: zim/gui/widgets.py:3579 |
2608 | +#: zim/gui/widgets.py:3589 |
2609 | msgid "Images" |
2610 | msgstr "" |
2611 | |
2612 | #. dialog title for log view dialog - e.g. for Equation Editor |
2613 | -#: zim/gui/widgets.py:3775 |
2614 | +#: zim/gui/widgets.py:3785 |
2615 | msgid "Log file" |
2616 | msgstr "" |
2617 | |
2618 | #. Dialog title |
2619 | -#: zim/gui/widgets.py:4246 |
2620 | +#: zim/gui/widgets.py:4256 |
2621 | msgid "File Exists" |
2622 | msgstr "" |
2623 | |
2624 | #. Dialog text in 'new filename' dialog |
2625 | -#: zim/gui/widgets.py:4247 |
2626 | +#: zim/gui/widgets.py:4257 |
2627 | #, python-format |
2628 | msgid "" |
2629 | "A file with the name <b>\"%s\"</b> already exists.\n" |
2630 | @@ -2092,17 +2103,17 @@ |
2631 | msgstr "" |
2632 | |
2633 | #. Input label |
2634 | -#: zim/gui/widgets.py:4256 |
2635 | +#: zim/gui/widgets.py:4266 |
2636 | msgid "Filename" |
2637 | msgstr "" |
2638 | |
2639 | #. Button label |
2640 | -#: zim/gui/widgets.py:4265 |
2641 | +#: zim/gui/widgets.py:4275 |
2642 | msgid "_Browse" |
2643 | msgstr "" |
2644 | |
2645 | #. Button label |
2646 | -#: zim/gui/widgets.py:4270 |
2647 | +#: zim/gui/widgets.py:4280 |
2648 | msgid "Overwrite" |
2649 | msgstr "" |
2650 | |
2651 | @@ -2192,27 +2203,6 @@ |
2652 | msgid "Profile" |
2653 | msgstr "" |
2654 | |
2655 | -#. Label for object manager |
2656 | -#: zim/objectmanager.py:211 |
2657 | -#, python-format |
2658 | -msgid "Plugin %s is required to display this object." |
2659 | -msgstr "" |
2660 | - |
2661 | -#. Label for object manager |
2662 | -#: zim/objectmanager.py:216 |
2663 | -msgid "Enable plugin" |
2664 | -msgstr "" |
2665 | - |
2666 | -#. Label for object manager |
2667 | -#: zim/objectmanager.py:228 |
2668 | -msgid "Show plugin details" |
2669 | -msgstr "" |
2670 | - |
2671 | -#. Label for object manager |
2672 | -#: zim/objectmanager.py:232 |
2673 | -msgid "No plugin is available to display this object." |
2674 | -msgstr "" |
2675 | - |
2676 | #. placeholder for unknown file name |
2677 | #: zim/parser.py:269 |
2678 | msgid "<Unknown>" |
2679 | @@ -2284,37 +2274,37 @@ |
2680 | msgstr[1] "" |
2681 | |
2682 | #. unspecified value for file modification time |
2683 | -#: zim/plugins/attachmentbrowser.py:712 |
2684 | +#: zim/plugins/attachmentbrowser.py:713 |
2685 | msgid "Unknown" |
2686 | msgstr "" |
2687 | |
2688 | #. label for file type |
2689 | -#: zim/plugins/attachmentbrowser.py:727 |
2690 | +#: zim/plugins/attachmentbrowser.py:728 |
2691 | msgid "Type" |
2692 | msgstr "" |
2693 | |
2694 | #. label for file size |
2695 | -#: zim/plugins/attachmentbrowser.py:728 |
2696 | +#: zim/plugins/attachmentbrowser.py:729 |
2697 | msgid "Size" |
2698 | msgstr "" |
2699 | |
2700 | #. label for file modification date |
2701 | -#: zim/plugins/attachmentbrowser.py:729 |
2702 | +#: zim/plugins/attachmentbrowser.py:730 |
2703 | msgid "Modified" |
2704 | msgstr "" |
2705 | |
2706 | #. popup menu action on drag-drop of a file |
2707 | -#: zim/plugins/attachmentbrowser.py:764 |
2708 | +#: zim/plugins/attachmentbrowser.py:765 |
2709 | msgid "_Move Here" |
2710 | msgstr "" |
2711 | |
2712 | #. popup menu action on drag-drop of a file |
2713 | -#: zim/plugins/attachmentbrowser.py:768 |
2714 | +#: zim/plugins/attachmentbrowser.py:769 |
2715 | msgid "_Copy Here" |
2716 | msgstr "" |
2717 | |
2718 | #. popup menu action on drag-drop of a file |
2719 | -#: zim/plugins/attachmentbrowser.py:773 |
2720 | +#: zim/plugins/attachmentbrowser.py:774 |
2721 | msgid "Cancel" |
2722 | msgstr "" |
2723 | |
2724 | @@ -2392,33 +2382,38 @@ |
2725 | msgid "Use a page for each" |
2726 | msgstr "" |
2727 | |
2728 | +#. preferences option |
2729 | +#: zim/plugins/calendar.py:102 |
2730 | +msgid "Expand journal page in index when opened" |
2731 | +msgstr "" |
2732 | + |
2733 | #. menu item |
2734 | -#: zim/plugins/calendar.py:229 |
2735 | +#: zim/plugins/calendar.py:232 |
2736 | msgid "To_day" |
2737 | msgstr "" |
2738 | |
2739 | #. menu item |
2740 | -#: zim/plugins/calendar.py:266 |
2741 | +#: zim/plugins/calendar.py:269 |
2742 | msgid "Calen_dar" |
2743 | msgstr "" |
2744 | |
2745 | #. menu item |
2746 | -#: zim/plugins/calendar.py:266 |
2747 | +#: zim/plugins/calendar.py:269 |
2748 | msgid "Show calendar" |
2749 | msgstr "" |
2750 | |
2751 | #. strftime format for current date label |
2752 | -#: zim/plugins/calendar.py:419 |
2753 | +#: zim/plugins/calendar.py:423 |
2754 | msgid "%A %d %B %Y" |
2755 | msgstr "" |
2756 | |
2757 | #. dialog title |
2758 | -#: zim/plugins/calendar.py:471 |
2759 | +#: zim/plugins/calendar.py:475 |
2760 | msgid "Calendar" |
2761 | msgstr "" |
2762 | |
2763 | #. button label |
2764 | -#: zim/plugins/calendar.py:481 |
2765 | +#: zim/plugins/calendar.py:485 |
2766 | msgid "_Today" |
2767 | msgstr "" |
2768 | |
2769 | @@ -2745,7 +2740,7 @@ |
2770 | msgstr "" |
2771 | |
2772 | #. text entry field |
2773 | -#: zim/plugins/quicknote.py:224 |
2774 | +#: zim/plugins/quicknote.py:224 zim/plugins/tableeditor.py:999 |
2775 | msgid "Title" |
2776 | msgstr "" |
2777 | |
2778 | @@ -2869,64 +2864,64 @@ |
2779 | msgstr "" |
2780 | |
2781 | #. plugin name |
2782 | -#: zim/plugins/sourceview.py:37 |
2783 | +#: zim/plugins/sourceview.py:43 |
2784 | msgid "Source View" |
2785 | msgstr "" |
2786 | |
2787 | #. plugin description |
2788 | -#: zim/plugins/sourceview.py:38 |
2789 | +#: zim/plugins/sourceview.py:44 |
2790 | msgid "" |
2791 | "This plugin allows inserting 'Code Blocks' in the page. These will be\n" |
2792 | "shown as emdedded widgets with syntax highlighting, line numbers etc.\n" |
2793 | msgstr "" |
2794 | |
2795 | #. preference option for sourceview plugin |
2796 | -#: zim/plugins/sourceview.py:49 |
2797 | +#: zim/plugins/sourceview.py:55 |
2798 | msgid "Auto indenting" |
2799 | msgstr "" |
2800 | |
2801 | #. preference option for sourceview plugin |
2802 | -#: zim/plugins/sourceview.py:51 |
2803 | +#: zim/plugins/sourceview.py:57 |
2804 | msgid "Smart Home key" |
2805 | msgstr "" |
2806 | |
2807 | #. preference option for sourceview plugin |
2808 | -#: zim/plugins/sourceview.py:53 |
2809 | +#: zim/plugins/sourceview.py:59 |
2810 | msgid "Highlight current line" |
2811 | msgstr "" |
2812 | |
2813 | #. preference option for sourceview plugin |
2814 | -#: zim/plugins/sourceview.py:55 |
2815 | +#: zim/plugins/sourceview.py:61 |
2816 | msgid "Show right margin" |
2817 | msgstr "" |
2818 | |
2819 | #. preference option for sourceview plugin |
2820 | -#: zim/plugins/sourceview.py:57 |
2821 | +#: zim/plugins/sourceview.py:63 |
2822 | msgid "Right margin position" |
2823 | msgstr "" |
2824 | |
2825 | #. preference option for sourceview plugin |
2826 | -#: zim/plugins/sourceview.py:59 |
2827 | +#: zim/plugins/sourceview.py:65 |
2828 | msgid "Tab width" |
2829 | msgstr "" |
2830 | |
2831 | #. menu item |
2832 | -#: zim/plugins/sourceview.py:108 |
2833 | +#: zim/plugins/sourceview.py:112 |
2834 | msgid "Code Block" |
2835 | msgstr "" |
2836 | |
2837 | #. dialog title |
2838 | -#: zim/plugins/sourceview.py:123 |
2839 | +#: zim/plugins/sourceview.py:127 |
2840 | msgid "Insert Code Block" |
2841 | msgstr "" |
2842 | |
2843 | #. input label |
2844 | -#: zim/plugins/sourceview.py:126 zim/plugins/sourceview.py:361 |
2845 | +#: zim/plugins/sourceview.py:130 zim/plugins/sourceview.py:344 |
2846 | msgid "Syntax" |
2847 | msgstr "" |
2848 | |
2849 | #. preference option for sourceview plugin |
2850 | -#: zim/plugins/sourceview.py:351 |
2851 | +#: zim/plugins/sourceview.py:334 |
2852 | msgid "Show Line Numbers" |
2853 | msgstr "" |
2854 | |
2855 | @@ -2960,6 +2955,194 @@ |
2856 | "dictionaries installed" |
2857 | msgstr "" |
2858 | |
2859 | +#. alignment option |
2860 | +#: zim/plugins/tableeditor.py:56 zim/plugins/tableeditor.py:943 |
2861 | +msgid "Left" |
2862 | +msgstr "" |
2863 | + |
2864 | +#. alignment option |
2865 | +#: zim/plugins/tableeditor.py:57 |
2866 | +msgid "Center" |
2867 | +msgstr "" |
2868 | + |
2869 | +#. alignment option |
2870 | +#: zim/plugins/tableeditor.py:58 |
2871 | +msgid "Right" |
2872 | +msgstr "" |
2873 | + |
2874 | +#. alignment option |
2875 | +#: zim/plugins/tableeditor.py:59 |
2876 | +msgid "Unspecified" |
2877 | +msgstr "" |
2878 | + |
2879 | +#. plugin name |
2880 | +#: zim/plugins/tableeditor.py:90 |
2881 | +msgid "Table Editor" |
2882 | +msgstr "" |
2883 | + |
2884 | +#. plugin description |
2885 | +#: zim/plugins/tableeditor.py:91 |
2886 | +msgid "" |
2887 | +"With this plugin you can embed a 'Table' into the wiki page. Tables will be " |
2888 | +"shown as GTK TreeView widgets.\n" |
2889 | +"Exporting them to various formats (i.e. HTML/LaTeX) completes the feature " |
2890 | +"set.\n" |
2891 | +msgstr "" |
2892 | + |
2893 | +#. option value |
2894 | +#: zim/plugins/tableeditor.py:101 |
2895 | +msgid "with lines" |
2896 | +msgstr "" |
2897 | + |
2898 | +#. option value |
2899 | +#: zim/plugins/tableeditor.py:102 |
2900 | +msgid "no grid lines" |
2901 | +msgstr "" |
2902 | + |
2903 | +#. option value |
2904 | +#: zim/plugins/tableeditor.py:103 |
2905 | +msgid "horizontal lines" |
2906 | +msgstr "" |
2907 | + |
2908 | +#. option value |
2909 | +#: zim/plugins/tableeditor.py:104 |
2910 | +msgid "vertical lines" |
2911 | +msgstr "" |
2912 | + |
2913 | +#. preference description |
2914 | +#: zim/plugins/tableeditor.py:110 |
2915 | +msgid "Show helper toolbar" |
2916 | +msgstr "" |
2917 | + |
2918 | +#. preference description |
2919 | +#: zim/plugins/tableeditor.py:113 |
2920 | +msgid "Grid lines" |
2921 | +msgstr "" |
2922 | + |
2923 | +#. menu item |
2924 | +#: zim/plugins/tableeditor.py:252 |
2925 | +msgid "Table" |
2926 | +msgstr "" |
2927 | + |
2928 | +#. tooltip on mouse hover | |
2929 | +#. menu item |
2930 | +#: zim/plugins/tableeditor.py:547 zim/plugins/tableeditor.py:759 |
2931 | +msgid "Add row" |
2932 | +msgstr "" |
2933 | + |
2934 | +#. tooltip on mouse hover |
2935 | +#: zim/plugins/tableeditor.py:548 |
2936 | +msgid "Remove row" |
2937 | +msgstr "" |
2938 | + |
2939 | +#. tooltip on mouse hover | |
2940 | +#. menu item |
2941 | +#: zim/plugins/tableeditor.py:549 zim/plugins/tableeditor.py:761 |
2942 | +msgid "Clone row" |
2943 | +msgstr "" |
2944 | + |
2945 | +#. tooltip on mouse hover | |
2946 | +#. menu item |
2947 | +#: zim/plugins/tableeditor.py:551 zim/plugins/tableeditor.py:765 |
2948 | +msgid "Row up" |
2949 | +msgstr "" |
2950 | + |
2951 | +#. tooltip on mouse hover | |
2952 | +#. menu item |
2953 | +#: zim/plugins/tableeditor.py:552 zim/plugins/tableeditor.py:766 |
2954 | +msgid "Row down" |
2955 | +msgstr "" |
2956 | + |
2957 | +#. tooltip on mouse hover | |
2958 | +#. menu item |
2959 | +#: zim/plugins/tableeditor.py:554 zim/plugins/tableeditor.py:768 |
2960 | +msgid "Change columns" |
2961 | +msgstr "" |
2962 | + |
2963 | +#. tooltip on mouse hover |
2964 | +#: zim/plugins/tableeditor.py:556 |
2965 | +msgid "Open help" |
2966 | +msgstr "" |
2967 | + |
2968 | +#. menu item |
2969 | +#: zim/plugins/tableeditor.py:760 |
2970 | +msgid "Delete row" |
2971 | +msgstr "" |
2972 | + |
2973 | +#. menu item |
2974 | +#: zim/plugins/tableeditor.py:763 |
2975 | +msgid "Open cell content link" |
2976 | +msgstr "" |
2977 | + |
2978 | +#. Popup dialog |
2979 | +#: zim/plugins/tableeditor.py:826 |
2980 | +msgid "" |
2981 | +"The table must consist of at least on row!\n" |
2982 | +" No deletion done." |
2983 | +msgstr "" |
2984 | + |
2985 | +#. Popup dialog |
2986 | +#: zim/plugins/tableeditor.py:909 zim/plugins/tableeditor.py:1157 |
2987 | +msgid "Please select a row, before you push the button." |
2988 | +msgstr "" |
2989 | + |
2990 | +#. Dialog title |
2991 | +#: zim/plugins/tableeditor.py:938 |
2992 | +msgid "Insert Table" |
2993 | +msgstr "" |
2994 | + |
2995 | +#. Dialog title |
2996 | +#: zim/plugins/tableeditor.py:938 |
2997 | +msgid "Edit Table" |
2998 | +msgstr "" |
2999 | + |
3000 | +#. Description of "Table-Insert" Dialog |
3001 | +#: zim/plugins/tableeditor.py:948 |
3002 | +msgid "Managing table columns" |
3003 | +msgstr "" |
3004 | + |
3005 | +#. Initial data for column title in table |
3006 | +#: zim/plugins/tableeditor.py:974 |
3007 | +msgid "Column 1" |
3008 | +msgstr "" |
3009 | + |
3010 | +#. table header |
3011 | +#: zim/plugins/tableeditor.py:1008 |
3012 | +msgid "" |
3013 | +"Auto\n" |
3014 | +"Wrap" |
3015 | +msgstr "" |
3016 | + |
3017 | +#. table header |
3018 | +#: zim/plugins/tableeditor.py:1018 |
3019 | +msgid "Align" |
3020 | +msgstr "" |
3021 | + |
3022 | +#. hoover tooltip |
3023 | +#: zim/plugins/tableeditor.py:1046 |
3024 | +msgid "Add column" |
3025 | +msgstr "" |
3026 | + |
3027 | +#. hoover tooltip |
3028 | +#: zim/plugins/tableeditor.py:1047 |
3029 | +msgid "Remove column" |
3030 | +msgstr "" |
3031 | + |
3032 | +#. hoover tooltip |
3033 | +#: zim/plugins/tableeditor.py:1048 |
3034 | +msgid "Move column ahead" |
3035 | +msgstr "" |
3036 | + |
3037 | +#. hoover tooltip |
3038 | +#: zim/plugins/tableeditor.py:1049 |
3039 | +msgid "Move column backward" |
3040 | +msgstr "" |
3041 | + |
3042 | +#. popup dialog |
3043 | +#: zim/plugins/tableeditor.py:1131 |
3044 | +msgid "A table needs to have at least one column." |
3045 | +msgstr "" |
3046 | + |
3047 | #. plugin name |
3048 | #: zim/plugins/tableofcontents.py:66 |
3049 | msgid "Table of Contents" |
3050 | @@ -3001,7 +3184,7 @@ |
3051 | |
3052 | #. plugin name | |
3053 | #. Column header for tag list in Task List dialog |
3054 | -#: zim/plugins/tags.py:30 zim/plugins/tags.py:70 zim/plugins/tasklist.py:736 |
3055 | +#: zim/plugins/tags.py:30 zim/plugins/tags.py:70 zim/plugins/tasklist.py:739 |
3056 | msgid "Tags" |
3057 | msgstr "" |
3058 | |
3059 | @@ -3013,25 +3196,30 @@ |
3060 | msgstr "" |
3061 | |
3062 | #. menu option |
3063 | -#: zim/plugins/tags.py:125 |
3064 | +#: zim/plugins/tags.py:139 |
3065 | +msgid "Show full page name" |
3066 | +msgstr "" |
3067 | + |
3068 | +#. menu option |
3069 | +#: zim/plugins/tags.py:144 |
3070 | msgid "Sort pages by tags" |
3071 | msgstr "" |
3072 | |
3073 | #. label for untagged pages in side pane |
3074 | -#: zim/plugins/tags.py:250 |
3075 | +#: zim/plugins/tags.py:270 |
3076 | msgid "untagged" |
3077 | msgstr "" |
3078 | |
3079 | #. Context menu item for tag cloud |
3080 | -#: zim/plugins/tags.py:997 |
3081 | +#: zim/plugins/tags.py:1008 |
3082 | msgid "Sort alphabetically" |
3083 | msgstr "" |
3084 | |
3085 | #. plugin name | |
3086 | #. menu item | |
3087 | #. dialog title |
3088 | -#: zim/plugins/tasklist.py:82 zim/plugins/tasklist.py:405 |
3089 | -#: zim/plugins/tasklist.py:631 |
3090 | +#: zim/plugins/tasklist.py:82 zim/plugins/tasklist.py:408 |
3091 | +#: zim/plugins/tasklist.py:634 |
3092 | msgid "Task List" |
3093 | msgstr "" |
3094 | |
3095 | @@ -3091,12 +3279,12 @@ |
3096 | msgstr "" |
3097 | |
3098 | #. Short message text on first time use of task list plugin |
3099 | -#: zim/plugins/tasklist.py:409 |
3100 | +#: zim/plugins/tasklist.py:412 |
3101 | msgid "Need to index the notebook" |
3102 | msgstr "" |
3103 | |
3104 | #. Long message text on first time use of task list plugin |
3105 | -#: zim/plugins/tasklist.py:411 |
3106 | +#: zim/plugins/tasklist.py:414 |
3107 | msgid "" |
3108 | "This is the first time the task list is opened.\n" |
3109 | "Therefore the index needs to be rebuild.\n" |
3110 | @@ -3106,17 +3294,17 @@ |
3111 | msgstr "" |
3112 | |
3113 | #. Input label |
3114 | -#: zim/plugins/tasklist.py:661 |
3115 | +#: zim/plugins/tasklist.py:664 |
3116 | msgid "Filter" |
3117 | msgstr "" |
3118 | |
3119 | #. Checkbox in task list |
3120 | -#: zim/plugins/tasklist.py:681 |
3121 | +#: zim/plugins/tasklist.py:684 |
3122 | msgid "Only Show Actionable Tasks" |
3123 | msgstr "" |
3124 | |
3125 | #. Label for statistics in Task List, %i is the number of tasks |
3126 | -#: zim/plugins/tasklist.py:694 |
3127 | +#: zim/plugins/tasklist.py:697 |
3128 | #, python-format |
3129 | msgid "%i open item" |
3130 | msgid_plural "%i open items" |
3131 | @@ -3124,23 +3312,23 @@ |
3132 | msgstr[1] "" |
3133 | |
3134 | #. "tag" for showing all tasks |
3135 | -#: zim/plugins/tasklist.py:795 |
3136 | +#: zim/plugins/tasklist.py:798 |
3137 | msgid "All Tasks" |
3138 | msgstr "" |
3139 | |
3140 | #. label in tasklist plugins for tasks without a tag |
3141 | -#: zim/plugins/tasklist.py:806 |
3142 | +#: zim/plugins/tasklist.py:809 |
3143 | msgid "Untagged" |
3144 | msgstr "" |
3145 | |
3146 | #. Column header Task List dialog |
3147 | -#: zim/plugins/tasklist.py:890 |
3148 | +#: zim/plugins/tasklist.py:893 |
3149 | msgid "Task" |
3150 | msgstr "" |
3151 | |
3152 | #. Column header Task List dialog | |
3153 | #. Column header versions dialog |
3154 | -#: zim/plugins/tasklist.py:935 zim/plugins/versioncontrol/__init__.py:1179 |
3155 | +#: zim/plugins/tasklist.py:939 zim/plugins/versioncontrol/__init__.py:1179 |
3156 | msgid "Date" |
3157 | msgstr "" |
3158 | |
3159 | @@ -3375,7 +3563,7 @@ |
3160 | msgstr "" |
3161 | |
3162 | #. Error message in template lookup |
3163 | -#: zim/templates/__init__.py:122 |
3164 | +#: zim/templates/__init__.py:121 |
3165 | #, python-format |
3166 | msgid "Could not find template \"%s\"" |
3167 | msgstr "" |
3168 | |
3169 | === modified file 'zim/formats/__init__.py' |
3170 | --- zim/formats/__init__.py 2014-11-11 20:26:12 +0000 |
3171 | +++ zim/formats/__init__.py 2015-04-04 11:26:41 +0000 |
3172 | @@ -37,6 +37,13 @@ |
3173 | - link for links, attribute href gives the target |
3174 | - img for images, attributes src, width, height an optionally href and alt |
3175 | - type can be used to control plugin functionality, e.g. type=equation |
3176 | + - table for tables, attributes |
3177 | + * aligns - comma separated values: right,left,center |
3178 | + * wraps - 0 for not wrapped, 1 for auto-wrapped line display |
3179 | + - thead for table header row |
3180 | + - th for table header cell |
3181 | + - trow for table row |
3182 | + - td for table data cell |
3183 | |
3184 | Unlike html we respect line breaks and other whitespace as is. |
3185 | When rendering as html use the "white-space: pre" CSS definition to |
3186 | @@ -128,7 +135,13 @@ |
3187 | TAG = 'tag' |
3188 | ANCHOR = 'anchor' |
3189 | |
3190 | -BLOCK_LEVEL = (PARAGRAPH, HEADING, VERBATIM_BLOCK, BLOCK, OBJECT, IMAGE, LISTITEM) |
3191 | +TABLE = 'table' |
3192 | +HEADROW = 'thead' |
3193 | +HEADDATA = 'th' |
3194 | +TABLEROW = 'trow' |
3195 | +TABLEDATA = 'td' |
3196 | + |
3197 | +BLOCK_LEVEL = (PARAGRAPH, HEADING, VERBATIM_BLOCK, BLOCK, OBJECT, IMAGE, LISTITEM, TABLE) |
3198 | |
3199 | |
3200 | _letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" |
3201 | @@ -891,6 +904,7 @@ |
3202 | self._tail = True |
3203 | |
3204 | if len(self._stack) > 1 and not (tag == 'img' or tag == 'object' |
3205 | + or tag == HEADDATA or tag == TABLEDATA |
3206 | or (self._last.text and not self._last.text.isspace()) |
3207 | or self._last.getchildren() ): |
3208 | # purge empty tags |
3209 | @@ -1146,6 +1160,10 @@ |
3210 | if not tag or tag != self.context[-1].tag: |
3211 | raise AssertionError, 'Unexpected tag closed: %s' % tag |
3212 | _, attrib, strings = self.context.pop() |
3213 | + |
3214 | + if tag in (TABLEDATA, HEADDATA) and not isinstance(strings, basestring): |
3215 | + strings = [''.join(strings)] # child elements of td and th are concated to string |
3216 | + |
3217 | if tag in self.TAGS: |
3218 | assert strings, 'Can not append empty %s element' % tag |
3219 | start, end = self.TAGS[tag] |
3220 | @@ -1484,3 +1502,151 @@ |
3221 | self.attrib = None |
3222 | if content: |
3223 | self.extend(content) |
3224 | + |
3225 | + |
3226 | +class TableParser(): |
3227 | + '''Common functions for converting a table from its' xml structure to another format''' |
3228 | + |
3229 | + @staticmethod |
3230 | + def width2dim(lines): |
3231 | + ''' |
3232 | + Calculates the characters on each column and return list of widths |
3233 | + :param lines: 2-dim multiline rows |
3234 | + :return: the number of characters of the longest cell-value by column |
3235 | + ''' |
3236 | + widths = [max(map(len, line)) for line in zip(*lines)] |
3237 | + return widths |
3238 | + |
3239 | + @staticmethod |
3240 | + def width3dim(lines): |
3241 | + ''' |
3242 | + Calculates the characters on each column and return list of widths |
3243 | + :param lines: 3-dim multiline rows |
3244 | + :return: the number of characters of the longest cell-value by column |
3245 | + ''' |
3246 | + lines = reduce(lambda x, y: x+y, lines) |
3247 | + widths = [max(map(len, line)) for line in zip(*lines)] |
3248 | + return widths |
3249 | + |
3250 | + @staticmethod |
3251 | + def convert_to_multiline_cells(rows): |
3252 | + ''' |
3253 | + Each cell of a list of list is splitted by "\n" and a 3-dimensional list is returned, |
3254 | + whereas each tuple represents a line and multiple lines represents a row and multiple rows represents the table |
3255 | + c11a = Cell in Row 1 in Column 1 in first = a line |
3256 | + :param strings: format like (('c11a \n c11b', 'c12a \n c12b'), ('c21', 'c22a \n 22b')) |
3257 | + :return: format like (((c11a, c12a), (c11b, c12b)), ((c21, c22a), ('', c22b))) |
3258 | + ''' |
3259 | + multi_rows = [map(lambda cell: cell.split("\n"), row) for row in rows] |
3260 | + |
3261 | + # grouping by line, not by row |
3262 | + strings = [map(lambda *line: map(lambda val: val if val is not None else '', line), *row) for row in multi_rows] |
3263 | + return strings |
3264 | + |
3265 | + @staticmethod |
3266 | + def get_options(attrib): |
3267 | + ''' |
3268 | + Lists the attributes as tuple |
3269 | + :param attrib: |
3270 | + :return: tuple of attributes |
3271 | + ''' |
3272 | + aligns = attrib['aligns'].split(',') |
3273 | + wraps = map(int, attrib['wraps'].split(',')) |
3274 | + |
3275 | + return aligns, wraps |
3276 | + |
3277 | + @staticmethod |
3278 | + def rowsep(maxwidths, x='+', y='-'): |
3279 | + ''' |
3280 | + Displays a row separator |
3281 | + example: rowsep((3,0), '-', '+') -> +-----+--+ |
3282 | + :param maxwidths: list of column lengths |
3283 | + :param x: point-separator |
3284 | + :param y: line-separator |
3285 | + :return: a textline |
3286 | + ''' |
3287 | + return x + x.join(map(lambda width: (width+2) * y, maxwidths)) + x |
3288 | + |
3289 | + @staticmethod |
3290 | + def headsep(maxwidths, aligns, x='|', y='-'): |
3291 | + ''' |
3292 | + Displays a header separation with alignment infos |
3293 | + example: rowsep((3,0), '-', '+') -> +-----+--+ |
3294 | + :param maxwidths: list of column lengths |
3295 | + :param aligns: list of alignments |
3296 | + :param x: point-separator |
3297 | + :param y: line-separator |
3298 | + :return: a textline |
3299 | + ''' |
3300 | + cells = [] |
3301 | + for width, align in zip(maxwidths, aligns): |
3302 | + line = width * y |
3303 | + if align == 'left': |
3304 | + cell = ':' + line + y |
3305 | + elif align == 'right': |
3306 | + cell = y + line + ':' |
3307 | + elif align == 'center': |
3308 | + cell = ':' + line + ':' |
3309 | + else: |
3310 | + cell = y + line + y |
3311 | + cells.append(cell) |
3312 | + return x + x.join(cells) + x |
3313 | + |
3314 | + @staticmethod |
3315 | + def headline(row, maxwidths, aligns, wraps, x='|', y=' '): |
3316 | + ''' |
3317 | + Displays a headerline line in text format |
3318 | + :param row: tuple of cells |
3319 | + :param maxwidths: list of column length |
3320 | + :param aligns: list of alignments |
3321 | + :param x: point-separator |
3322 | + :param y: space-separator |
3323 | + :return: a textline |
3324 | + ''' |
3325 | + row = TableParser.alignrow(row, maxwidths, aligns, y) |
3326 | + cells = [] |
3327 | + for val, wrap in zip(row, wraps): |
3328 | + if wrap == 1: |
3329 | + val = val[:-1]+'<' |
3330 | + cells.append(val) |
3331 | + return x + x.join(cells) + x |
3332 | + |
3333 | + @staticmethod |
3334 | + def rowline(row, maxwidths, aligns, x='|', y=' '): |
3335 | + ''' |
3336 | + Displays a normal column line in text format |
3337 | + example: rowline((3,0), (left, left), '+','-') -> +-aa--+--+ |
3338 | + :param row: tuple of cells |
3339 | + :param maxwidths: list of column length |
3340 | + :param aligns: list of alignments |
3341 | + :param x: point-separator |
3342 | + :param y: space-separator |
3343 | + :return: a textline |
3344 | + ''' |
3345 | + cells = TableParser.alignrow(row, maxwidths, aligns, y) |
3346 | + return x + x.join(cells) + x |
3347 | + |
3348 | + @staticmethod |
3349 | + def alignrow(row, maxwidths, aligns, y=' '): |
3350 | + ''' |
3351 | + Formats a row with the right alignments |
3352 | + :param row: tuple of cells |
3353 | + :param maxwidths: list of column length |
3354 | + :param aligns: list of alignments |
3355 | + :param x: point-separator |
3356 | + :param y: space-separator |
3357 | + :return: a textline |
3358 | + ''' |
3359 | + cells = [] |
3360 | + for val, align, maxwidth in zip(row, aligns, maxwidths): |
3361 | + if align == 'left': |
3362 | + (lspace, rspace) = (1, maxwidth - len(val) + 1) |
3363 | + elif align == 'right': |
3364 | + (lspace, rspace) = (maxwidth - len(val) + 1, 1) |
3365 | + elif align == 'center': |
3366 | + lspace = (maxwidth - len(val)) / 2 + 1 |
3367 | + rspace = (maxwidth - lspace - len(val) + 2) |
3368 | + else: |
3369 | + (lspace, rspace) = (1, maxwidth - len(val) + 1) |
3370 | + cells.append(lspace * y + val + rspace * y) |
3371 | + return cells |
3372 | \ No newline at end of file |
3373 | |
3374 | === modified file 'zim/formats/html.py' |
3375 | --- zim/formats/html.py 2014-11-11 20:26:12 +0000 |
3376 | +++ zim/formats/html.py 2015-04-04 11:26:41 +0000 |
3377 | @@ -220,3 +220,48 @@ |
3378 | |
3379 | # TODO put content in attrib, use text for caption (with full recursion) |
3380 | # See img |
3381 | + |
3382 | + def dump_table(self, tag, attrib, strings): |
3383 | + aligns = attrib['aligns'].split(',') |
3384 | + tdcount = 0 |
3385 | + |
3386 | + def align(pos): |
3387 | + if pos == 'left' or pos == 'right' or pos == 'center': |
3388 | + return ' align="' + pos + '"' |
3389 | + return '' |
3390 | + |
3391 | + for i, string in enumerate(strings): |
3392 | + if '<tr' in string: |
3393 | + tdcount = 0 |
3394 | + elif '<th' in string: |
3395 | + strings[i] = string.replace('<th', '<th' + align(aligns[tdcount])) |
3396 | + tdcount += 1 |
3397 | + elif '<td' in string: |
3398 | + strings[i] = string.replace('<td', '<td' + align(aligns[tdcount])) |
3399 | + tdcount += 1 |
3400 | + |
3401 | + strings.insert(0, '<table>\n') |
3402 | + strings.append('</table>\n') |
3403 | + return strings |
3404 | + |
3405 | + def dump_thead(self, tag, attrib, strings): |
3406 | + strings.insert(0, '<thead><tr>\n') |
3407 | + strings.append('</tr></thead>\n') |
3408 | + return strings |
3409 | + |
3410 | + def dump_th(self, tag, attrib, strings): |
3411 | + strings.insert(0, ' <th>') |
3412 | + strings.append('</th>\n') |
3413 | + return strings |
3414 | + |
3415 | + def dump_trow(self, tag, attrib, strings): |
3416 | + strings.insert(0, '<tr>\n') |
3417 | + strings.append('</tr>\n') |
3418 | + return strings |
3419 | + |
3420 | + def dump_td(self, tag, attrib, strings): |
3421 | + if strings == [" "]: |
3422 | + strings = [" "] |
3423 | + strings.insert(0, ' <td>') |
3424 | + strings.append('</td>\n') |
3425 | + return strings |
3426 | |
3427 | === modified file 'zim/formats/latex.py' |
3428 | --- zim/formats/latex.py 2014-08-22 20:18:49 +0000 |
3429 | +++ zim/formats/latex.py 2015-04-04 11:26:41 +0000 |
3430 | @@ -227,3 +227,27 @@ |
3431 | assert False, 'Found no suitable delimiter for verbatim text: %s' % element |
3432 | |
3433 | dump_object_fallback = dump_pre |
3434 | + |
3435 | + def dump_table(self, tag, attrib, strings): |
3436 | + table = [] # result table |
3437 | + rows = strings |
3438 | + |
3439 | + aligns, _wraps = TableParser.get_options(attrib) |
3440 | + rowline = lambda row: '&'.join([' ' + cell + ' ' for cell in row]) + '\\tabularnewline\n\hline' |
3441 | + aligns = map(lambda a: 'l' if a == 'left' else 'r' if a == 'right' else 'c' if a == 'center' else 'l', aligns) |
3442 | + |
3443 | + for i, row in enumerate(rows): |
3444 | + for j, (cell, align) in enumerate(zip(row, aligns)): |
3445 | + if '\n' in cell: |
3446 | + rows[i][j] = '\shortstack[' + align + ']{' + cell.replace("\n", "\\") + '}' |
3447 | + |
3448 | + # print table |
3449 | + table.append('\\begin{tabular}{ |' + '|'.join(aligns) + '| }') |
3450 | + table.append('\hline') |
3451 | + |
3452 | + table += [rowline(rows[0])] |
3453 | + table.append('\hline') |
3454 | + table += [rowline(row) for row in rows[1:]] |
3455 | + |
3456 | + table.append('\end{tabular}') |
3457 | + return map(lambda line: line+"\n", table) |
3458 | |
3459 | === modified file 'zim/formats/markdown.py' |
3460 | --- zim/formats/markdown.py 2014-05-04 19:17:46 +0000 |
3461 | +++ zim/formats/markdown.py 2015-04-04 11:26:41 +0000 |
3462 | @@ -108,4 +108,31 @@ |
3463 | # dump object as verbatim block |
3464 | return self.prefix_lines('\t', strings) |
3465 | |
3466 | - |
3467 | + def dump_table(self, tag, attrib, strings): |
3468 | + table = [] # result table |
3469 | + rows = strings |
3470 | + |
3471 | + aligns, _wraps = TableParser.get_options(attrib) |
3472 | + maxwidths = TableParser.width2dim(rows) |
3473 | + headsep = TableParser.headsep(maxwidths, aligns, x='|', y='-') |
3474 | + rowline = lambda row: TableParser.rowline(row, maxwidths, aligns) |
3475 | + |
3476 | + # print table |
3477 | + table += [rowline(rows[0])] |
3478 | + table.append(headsep) |
3479 | + table += [rowline(row) for row in rows[1:]] |
3480 | + return map(lambda line: line+"\n", table) |
3481 | + |
3482 | + def dump_thead(self, tag, attrib, strings): |
3483 | + return [strings] |
3484 | + |
3485 | + def dump_th(self, tag, attrib, strings): |
3486 | + strings = [s.replace('\n', '<br>').replace('|', '∣') for s in strings] |
3487 | + return strings |
3488 | + |
3489 | + def dump_trow(self, tag, attrib, strings): |
3490 | + return [strings] |
3491 | + |
3492 | + def dump_td(self, tag, attrib, strings): |
3493 | + strings = [s.replace('\n', '<br>').replace('|', '∣') for s in strings] |
3494 | + return strings |
3495 | |
3496 | === modified file 'zim/formats/plain.py' |
3497 | --- zim/formats/plain.py 2014-05-04 19:17:46 +0000 |
3498 | +++ zim/formats/plain.py 2015-04-04 11:26:41 +0000 |
3499 | @@ -188,3 +188,36 @@ |
3500 | |
3501 | def dump_object_fallback(self, tag, attrib, strings): |
3502 | return strings |
3503 | + |
3504 | + def dump_table(self, tag, attrib, strings): |
3505 | + table = [] # result table |
3506 | + |
3507 | + aligns, _wraps = TableParser.get_options(attrib) |
3508 | + rows = TableParser.convert_to_multiline_cells(strings) |
3509 | + maxwidths = TableParser.width3dim(rows) |
3510 | + rowsep = lambda y: TableParser.rowsep(maxwidths, x='+', y=y) |
3511 | + rowline = lambda row: TableParser.rowline(row, maxwidths, aligns) |
3512 | + |
3513 | + # print table |
3514 | + table.append(rowsep('-')) |
3515 | + table += [rowline(line) for line in rows[0]] |
3516 | + table.append(rowsep('=')) |
3517 | + for row in rows[1:]: |
3518 | + table += [rowline(line) for line in row] |
3519 | + table.append(rowsep('-')) |
3520 | + |
3521 | + return map(lambda line: line+"\n", table) |
3522 | + |
3523 | + def dump_thead(self, tag, attrib, strings): |
3524 | + return [strings] |
3525 | + |
3526 | + def dump_th(self, tag, attrib, strings): |
3527 | + strings = [s.replace('|', '∣') for s in strings] |
3528 | + return strings |
3529 | + |
3530 | + def dump_trow(self, tag, attrib, strings): |
3531 | + return [strings] |
3532 | + |
3533 | + def dump_td(self, tag, attrib, strings): |
3534 | + strings = [s.replace('|', '∣') for s in strings] |
3535 | + return strings |
3536 | |
3537 | === modified file 'zim/formats/rst.py' |
3538 | --- zim/formats/rst.py 2014-05-04 19:17:46 +0000 |
3539 | +++ zim/formats/rst.py 2015-04-04 11:26:41 +0000 |
3540 | @@ -93,3 +93,36 @@ |
3541 | # can be done using "figure" directive |
3542 | |
3543 | dump_object_fallback = dump_pre |
3544 | + |
3545 | + def dump_table(self, tag, attrib, strings): |
3546 | + table = [] # result table |
3547 | + |
3548 | + aligns, _wraps = TableParser.get_options(attrib) |
3549 | + rows = TableParser.convert_to_multiline_cells(strings) |
3550 | + maxwidths = TableParser.width3dim(rows) |
3551 | + rowsep = lambda y: TableParser.rowsep(maxwidths, x='+', y=y) |
3552 | + rowline = lambda row: TableParser.rowline(row, maxwidths, aligns) |
3553 | + |
3554 | + # print table |
3555 | + table.append(rowsep('-')) |
3556 | + table += [rowline(line) for line in rows[0]] |
3557 | + table.append(rowsep('=')) |
3558 | + for row in rows[1:]: |
3559 | + table += [rowline(line) for line in row] |
3560 | + table.append(rowsep('-')) |
3561 | + |
3562 | + return map(lambda line: line+"\n", table) |
3563 | + |
3564 | + def dump_thead(self, tag, attrib, strings): |
3565 | + return [strings] |
3566 | + |
3567 | + def dump_th(self, tag, attrib, strings): |
3568 | + strings = [s.replace('|', '∣') for s in strings] |
3569 | + return strings |
3570 | + |
3571 | + def dump_trow(self, tag, attrib, strings): |
3572 | + return [strings] |
3573 | + |
3574 | + def dump_td(self, tag, attrib, strings): |
3575 | + strings = [s.replace('|', '∣') for s in strings] |
3576 | + return strings |
3577 | |
3578 | === modified file 'zim/formats/wiki.py' |
3579 | --- zim/formats/wiki.py 2014-11-09 11:07:20 +0000 |
3580 | +++ zim/formats/wiki.py 2015-04-04 11:26:41 +0000 |
3581 | @@ -163,6 +163,15 @@ |
3582 | r'^( ==+ [\ \t]+ \S.*? ) [\ \t]* =* \n', # "==== heading ====" |
3583 | process=self.parse_heading |
3584 | ), |
3585 | + # standard table format |
3586 | + Rule(TABLE, r''' |
3587 | + ^(\|.*\|)$\n # starting and ending with | |
3588 | + ^( (?:\| [ \|\-:]+ \|$\n)? ) # column align |
3589 | + ( (?:^\|.*\|$\n)+ ) # multi-lines: starting and ending with | |
3590 | + ^(?= \s*? \n) # empty line / only spaces |
3591 | + ''', |
3592 | + process=self.parse_table |
3593 | + ) |
3594 | ) |
3595 | p.process_unmatched = self.parse_para |
3596 | return p |
3597 | @@ -195,8 +204,7 @@ |
3598 | |
3599 | builder.append(VERBATIM_BLOCK, attrib, text) |
3600 | |
3601 | - @staticmethod |
3602 | - def parse_object(builder, indent, header, body): |
3603 | + def parse_object(self, builder, indent, header, body): |
3604 | '''Custom object''' |
3605 | type, param = header.split(':', 1) |
3606 | type = type.strip().lower() |
3607 | @@ -216,9 +224,91 @@ |
3608 | if indent: |
3609 | body = _remove_indent(body, indent) |
3610 | attrib['indent'] = len(indent) |
3611 | - |
3612 | builder.append(OBJECT, attrib, body) |
3613 | |
3614 | + def check_multi_attribute(self, attrib, key, default, list_length): |
3615 | + ''' |
3616 | + Correct multi-attributes, so they do fit with column length of table |
3617 | + :param attrib: key-value store |
3618 | + :param key: key to select of attribute |
3619 | + :param default: default value for one list entry |
3620 | + :param list_length: required length of selected attribute |
3621 | + :return: attribute-value as list of different options |
3622 | + ''' |
3623 | + if attrib and key in attrib and attrib[key]: |
3624 | + values = attrib[key].split(',') |
3625 | + else: |
3626 | + values = [] |
3627 | + |
3628 | + while len(values) > list_length: |
3629 | + values.pop() |
3630 | + while len(values) < list_length: |
3631 | + values.append(default) |
3632 | + return ','.join(values) |
3633 | + |
3634 | + def parse_table(self, builder, headerrow, alignstyle, body): |
3635 | + '''Table parsing''' |
3636 | + body = body.replace('\\|', '#124;') # escaping |
3637 | + rows = body.split('\n')[:-1] |
3638 | + # get maximum number of columns - each columns must have same size |
3639 | + nrcols = max([headerrow.count('|')]+[row.count('|') for row in rows])-1 |
3640 | + |
3641 | + # transform header separator line into alignment definitions |
3642 | + aligns = [] |
3643 | + while alignstyle.count('|') < nrcols+1: |
3644 | + alignstyle += '|' # fill cells thus they match with nr of headers |
3645 | + for celltext in alignstyle.split('|')[1:-1]: |
3646 | + celltext = celltext.strip() |
3647 | + if celltext.startswith(':') and celltext.endswith(':'): |
3648 | + alignment = 'center' |
3649 | + elif celltext.startswith(':'): |
3650 | + alignment = 'left' |
3651 | + elif celltext.endswith(':'): |
3652 | + alignment = 'right' |
3653 | + else: |
3654 | + alignment = 'normal' |
3655 | + aligns.append(alignment) |
3656 | + |
3657 | + # collect wrap settings from first table row |
3658 | + headers = [] |
3659 | + wraps = [] |
3660 | + for celltext in headerrow.split('|')[1:-1]: |
3661 | + if celltext.rstrip().endswith('<'): |
3662 | + celltext = celltext.rstrip().rstrip('<') |
3663 | + wraps.append(1) |
3664 | + else: |
3665 | + wraps.append(0) |
3666 | + headers.append(celltext) |
3667 | + |
3668 | + attrib = {'aligns': ','.join(aligns), 'wraps': ','.join(map(str, wraps))} |
3669 | + |
3670 | + |
3671 | + # build table |
3672 | + builder.start(TABLE, attrib) |
3673 | + |
3674 | + builder.start(HEADROW) |
3675 | + for celltext in headers: |
3676 | + celltext = celltext.replace('#124;', '|').replace('\\n', '\n').strip() |
3677 | + if not celltext: |
3678 | + celltext = ' ' # celltext must contain at least one character |
3679 | + builder.append(HEADDATA, {}, celltext) |
3680 | + builder.end(HEADROW) |
3681 | + |
3682 | + for bodyrow in rows: |
3683 | + while bodyrow.count('|') < nrcols+1: |
3684 | + bodyrow += '|' # fill cells thus they match with nr of headers |
3685 | + builder.start(TABLEROW) |
3686 | + for celltext in bodyrow.split('|')[1:-1]: |
3687 | + builder.start(TABLEDATA) |
3688 | + celltext = celltext.replace('#124;', '|').replace('\\n', '\n').strip() # cleanup cell |
3689 | + if not celltext: |
3690 | + celltext = ' ' # celltext must contain at least one character |
3691 | + self.inline_parser(builder, celltext) |
3692 | + builder.end(TABLEDATA) |
3693 | + builder.end(TABLEROW) |
3694 | + |
3695 | + builder.end(TABLE) |
3696 | + |
3697 | def parse_para(self, builder, text): |
3698 | '''Split a text into paragraphs and empty lines''' |
3699 | if text.isspace(): |
3700 | @@ -454,3 +544,35 @@ |
3701 | |
3702 | # TODO put content in attrib, use text for caption (with full recursion) |
3703 | # See img |
3704 | + |
3705 | + def dump_table(self, tag, attrib, strings): |
3706 | + # logger.debug("Dumping table: %s, %s", attrib, strings) |
3707 | + |
3708 | + table = [] # result table |
3709 | + rows = strings |
3710 | + |
3711 | + aligns, wraps = TableParser.get_options(attrib) |
3712 | + maxwidths = TableParser.width2dim(rows) |
3713 | + headsep = TableParser.headsep(maxwidths, aligns, x='|', y='-') |
3714 | + rowline = lambda row: TableParser.rowline(row, maxwidths, aligns) |
3715 | + |
3716 | + # print table |
3717 | + table += [TableParser.headline(rows[0], maxwidths, aligns, wraps)] |
3718 | + table.append(headsep) |
3719 | + table += [rowline(row) for row in rows[1:]] |
3720 | + return map(lambda line: line+"\n", table) |
3721 | + |
3722 | + def dump_thead(self, tag, attrib, strings): |
3723 | + return [strings] |
3724 | + |
3725 | + def dump_th(self, tag, attrib, strings): |
3726 | + strings = map(lambda s: s.replace('\n', '\\n').replace('|', '\\|'), strings) |
3727 | + return strings |
3728 | + |
3729 | + def dump_trow(self, tag, attrib, strings): |
3730 | + return [strings] |
3731 | + |
3732 | + def dump_td(self, tag, attrib, strings): |
3733 | + strings = map(lambda s: s.replace('\n', '\\n').replace('|', '\\|'), strings) |
3734 | + strings = map(lambda s: s.replace('<br>', '\\n'), strings) |
3735 | + return strings |
3736 | |
3737 | === modified file 'zim/gui/objectmanager.py' |
3738 | --- zim/gui/objectmanager.py 2014-11-09 11:07:20 +0000 |
3739 | +++ zim/gui/objectmanager.py 2015-04-04 11:26:41 +0000 |
3740 | @@ -145,7 +145,7 @@ |
3741 | self.vbox.pack_start(label) |
3742 | |
3743 | def _add_load_plugin_bar(self, plugin): |
3744 | - key, name, activatable, klass = plugin |
3745 | + key, name, activatable, klass, _winextension = plugin |
3746 | |
3747 | hbox = gtk.HBox(False, 5) |
3748 | label = gtk.Label(_("Plugin %s is required to display this object.") % name) |
3749 | |
3750 | === modified file 'zim/gui/pageview.py' |
3751 | --- zim/gui/pageview.py 2014-11-11 20:26:12 +0000 |
3752 | +++ zim/gui/pageview.py 2015-04-04 11:26:41 +0000 |
3753 | @@ -43,9 +43,12 @@ |
3754 | from zim.gui.applications import OpenWithMenu |
3755 | from zim.gui.clipboard import Clipboard, SelectionClipboard, \ |
3756 | PARSETREE_ACCEPT_TARGETS, parsetree_from_selectiondata |
3757 | -from zim.objectmanager import ObjectManager, CustomObjectClass |
3758 | +from zim.objectmanager import ObjectManager, CustomObjectClass, FallbackObject |
3759 | from zim.gui.objectmanager import CustomObjectWidget, POSITION_BEGIN, POSITION_END |
3760 | from zim.utils import WeakSet |
3761 | +from zim.formats import get_dumper |
3762 | +from zim.formats.wiki import Dumper as WikiDumper |
3763 | +from zim.plugins import PluginManager |
3764 | |
3765 | |
3766 | logger = logging.getLogger('zim.gui.pageview') |
3767 | @@ -515,6 +518,9 @@ |
3768 | @ivar user_action: A L{UserActionContext} context manager |
3769 | @ivar finder: A L{TextFinder} for this buffer |
3770 | |
3771 | + @signal: C{reload-page ()}: |
3772 | + Emitted when plugin is activated and current page should be reloaded |
3773 | + to display object properly |
3774 | @signal: C{begin-insert-tree ()}: |
3775 | Emitted at the begin of a complex insert |
3776 | @signal: C{end-insert-tree ()}: |
3777 | @@ -523,6 +529,8 @@ |
3778 | Gives inserted tree after inserting it |
3779 | @signal: C{textstyle-changed (style)}: |
3780 | Emitted when textstyle at the cursor changes |
3781 | + @signal: C{link-clicked ()}: |
3782 | + Emitted when a link is clicked; for example within a table cell |
3783 | @signal: C{clear ()}: |
3784 | emitted to clear the whole buffer before destruction |
3785 | @signal: C{undo-save-cursor (iter)}: |
3786 | @@ -530,6 +538,12 @@ |
3787 | lock the current cursor position |
3788 | @signal: C{insert-object (object_element)}: request inserting of |
3789 | custom object |
3790 | + @signal: C{edit-object (object_element)}: request editing of |
3791 | + custom object |
3792 | + @signal: C{insert-table (table_element)}: request inserting of |
3793 | + table object |
3794 | + @signal: C{edit-table (table_element)}: request editing of |
3795 | + table object |
3796 | |
3797 | @todo: document tag styles that are supported |
3798 | ''' |
3799 | @@ -551,6 +565,11 @@ |
3800 | 'clear': (gobject.SIGNAL_RUN_LAST, None, ()), |
3801 | 'undo-save-cursor': (gobject.SIGNAL_RUN_LAST, None, (object,)), |
3802 | 'insert-object': (gobject.SIGNAL_RUN_LAST, None, (object,)), |
3803 | + 'edit-object': (gobject.SIGNAL_RUN_LAST, None, (object,)), |
3804 | + 'insert-table': (gobject.SIGNAL_RUN_LAST, None, (object,)), |
3805 | + 'edit-table': (gobject.SIGNAL_RUN_LAST, None, (object,)), |
3806 | + 'link-clicked': (gobject.SIGNAL_RUN_LAST, None, (object,)), |
3807 | + 'reload-page': (gobject.SIGNAL_RUN_LAST, None, (object,)), |
3808 | } |
3809 | |
3810 | # style attributes |
3811 | @@ -919,6 +938,8 @@ |
3812 | self.insert_at_cursor(element.text) |
3813 | self.set_textstyle(None) |
3814 | set_indent(None) |
3815 | + elif element.tag == 'table': |
3816 | + self.emit('insert-table', element) |
3817 | elif element.tag == 'object': |
3818 | if 'indent' in element.attrib: |
3819 | set_indent(int(element.attrib['indent'])) |
3820 | @@ -2323,11 +2344,16 @@ |
3821 | continue |
3822 | if hasattr(anchor, 'manager'): |
3823 | attrib = anchor.manager.get_attrib() |
3824 | - data = anchor.manager.get_data() |
3825 | - logger.debug("Anchor with CustomObject: %s", anchor.manager) |
3826 | - builder.start('object', attrib) |
3827 | - builder.data(data) |
3828 | - builder.end('object') |
3829 | + if attrib and attrib['type'] == 'table': |
3830 | + self.build_parsetree_of_table(builder, anchor.manager, iter) |
3831 | + else: |
3832 | + # general object related parsing |
3833 | + data = anchor.manager.get_data() |
3834 | + logger.debug("Anchor with CustomObject: %s", anchor.manager) |
3835 | + builder.start('object', attrib) |
3836 | + builder.data(data) |
3837 | + builder.end('object') |
3838 | + |
3839 | anchor.manager.set_modified(False) |
3840 | iter.forward_char() |
3841 | else: |
3842 | @@ -2413,6 +2439,55 @@ |
3843 | |
3844 | return tree |
3845 | |
3846 | + def build_parsetree_of_table(self, builder, anchormanager, iter): |
3847 | + logger.debug("Anchor with TableObject: %s", anchormanager) |
3848 | + attrib = anchormanager.get_attrib() |
3849 | + del attrib['type'] |
3850 | + tabledata = anchormanager.get_data() |
3851 | + |
3852 | + # inserts a newline before and after table-object |
3853 | + bound = iter.copy() |
3854 | + bound.backward_char() |
3855 | + char_before_table = bound.get_slice(iter) |
3856 | + need_newline_infront = char_before_table.decode('utf-8') != "\n".decode('utf-8') |
3857 | + bound = iter.copy() |
3858 | + bound.forward_char() |
3859 | + iter2 = bound.copy() |
3860 | + bound.forward_char() |
3861 | + char_after_table = iter2.get_slice(bound) |
3862 | + need_newline_behind = char_after_table.decode('utf-8') != "\n".decode('utf-8') |
3863 | + |
3864 | + # table-editor plugin is not activated -> handle table-object like a fallback-object |
3865 | + if isinstance(tabledata, basestring): |
3866 | + if need_newline_infront: |
3867 | + builder.data('\n') |
3868 | + builder.data(tabledata) |
3869 | + if need_newline_behind: |
3870 | + builder.data('\n') |
3871 | + return |
3872 | + |
3873 | + headers, rows, attrib = tabledata |
3874 | + if need_newline_infront: |
3875 | + builder.data('\n') |
3876 | + |
3877 | + builder.start('table', attrib) |
3878 | + builder.start('thead') |
3879 | + for header in headers: |
3880 | + builder.start('th') |
3881 | + builder.data(header) |
3882 | + builder.end('th') |
3883 | + builder.end('thead') |
3884 | + for row in rows: |
3885 | + builder.start('trow') |
3886 | + for cell in row: |
3887 | + builder.start('td') |
3888 | + builder.data(cell) |
3889 | + builder.end('td') |
3890 | + builder.end('trow') |
3891 | + builder.end('table') |
3892 | + if need_newline_behind: |
3893 | + builder.data('\n') |
3894 | + |
3895 | def select_line(self): |
3896 | '''Selects the current line |
3897 | |
3898 | @@ -3290,6 +3365,14 @@ |
3899 | start = self.buffer.get_iter_at_line(line) |
3900 | if start.ends_line(): |
3901 | continue |
3902 | + |
3903 | + # search within external widgets |
3904 | + if start.get_child_anchor() is not None: |
3905 | + result = self._search_in_widget(start, step) |
3906 | + if result: |
3907 | + yield (start, start, result[2]) |
3908 | + else: |
3909 | + continue |
3910 | end = start.copy() |
3911 | end.forward_to_line_end() |
3912 | text = start.get_slice(end) |
3913 | @@ -3304,6 +3387,34 @@ |
3914 | line, match.end() ) |
3915 | yield startiter, enditer, match |
3916 | |
3917 | + |
3918 | + def _search_in_widget(self, start, step): |
3919 | + ''' |
3920 | + Search within a widget |
3921 | + :param start: position-of-widget |
3922 | + :param step: search direction (up / down): -1 / 1 |
3923 | + :return: tuple (startiter, enditer, match) |
3924 | + ''' |
3925 | + if start.get_child_anchor() is None or len(start.get_child_anchor().get_widgets()) < 1: |
3926 | + return |
3927 | + widgets = start.get_child_anchor().get_widgets() |
3928 | + if isinstance(widgets[0], zim.plugins.tableeditor.TableViewWidget): |
3929 | + table = widgets[0] |
3930 | + liststore = table.get_liststore() |
3931 | + iter = liststore.get_iter_root() |
3932 | + while iter is not None: |
3933 | + for col in range(liststore.get_n_columns()): |
3934 | + text = liststore.get_value(iter, col) |
3935 | + matches = self.regex.finditer(text) |
3936 | + if step == -1: |
3937 | + matches = list(matches) |
3938 | + matches.reverse() |
3939 | + for match in matches: |
3940 | + startiter = iter |
3941 | + enditer = iter |
3942 | + return startiter, enditer, match |
3943 | + iter = liststore.iter_next(iter) |
3944 | + |
3945 | def replace(self, string): |
3946 | '''Replace current match |
3947 | |
3948 | @@ -3326,11 +3437,14 @@ |
3949 | string = match.expand(string) |
3950 | |
3951 | offset = start.get_offset() |
3952 | - with self.buffer.user_action: |
3953 | - self.buffer.select_range(start, end) # ensure editmode logic is used |
3954 | - self.buffer.delete(start, end) |
3955 | - self.buffer.insert_at_cursor(string) |
3956 | |
3957 | + if start.get_child_anchor() is not None: |
3958 | + self._replace_in_widget(start, self.regex, string) # replace within external widgets |
3959 | + else: |
3960 | + with self.buffer.user_action: |
3961 | + self.buffer.select_range(start, end) # ensure editmode logic is used |
3962 | + self.buffer.delete(start, end) |
3963 | + self.buffer.insert_at_cursor(string) |
3964 | start = self.buffer.get_iter_at_offset(offset) |
3965 | end = self.buffer.get_iter_at_offset(offset+len(string)) |
3966 | self.buffer.select_range(start, end) |
3967 | @@ -3341,6 +3455,36 @@ |
3968 | |
3969 | self._update_highlight() |
3970 | |
3971 | + ''' |
3972 | + Replace within a widget |
3973 | + :param start: position-of-widget |
3974 | + :param regex: regular expression pattern |
3975 | + :param text: substituation text |
3976 | + :param replaceall: boolean if all matches should be replaced |
3977 | + :return: True / False - a replacement was done / no replaces |
3978 | + ''' |
3979 | + def _replace_in_widget(self, start, regex, string, replaceall=False): |
3980 | + if start.get_child_anchor() is None or len(start.get_child_anchor().get_widgets()) < 1: |
3981 | + return |
3982 | + widgets = start.get_child_anchor().get_widgets() |
3983 | + if isinstance(widgets[0], zim.plugins.tableeditor.TableViewWidget): |
3984 | + table = widgets[0] |
3985 | + liststore = table.get_liststore() |
3986 | + iter = liststore.get_iter_root() |
3987 | + has_replaced = False |
3988 | + while iter is not None: |
3989 | + for col in range(liststore.get_n_columns()): |
3990 | + text = liststore.get_value(iter, col) |
3991 | + if(regex.search(text)): |
3992 | + newtext = regex.sub(string, text) |
3993 | + liststore.set_value(iter, col, newtext) |
3994 | + if(not replaceall): |
3995 | + return True |
3996 | + else: |
3997 | + has_replaced = True |
3998 | + iter = liststore.iter_next(iter) |
3999 | + return has_replaced |
4000 | + |
4001 | def replace_all(self, string): |
4002 | '''Replace all matched |
4003 | |
4004 | @@ -3366,9 +3510,12 @@ |
4005 | for start, end, string in matches: |
4006 | start = self.buffer.get_iter_at_offset(start) |
4007 | end = self.buffer.get_iter_at_offset(end) |
4008 | - self.buffer.select_range(start, end) # ensure editmode logic is used |
4009 | - self.buffer.delete(start, end) |
4010 | - self.buffer.insert(start, string) |
4011 | + if start.get_child_anchor() is not None: |
4012 | + self._replace_in_widget(start, self.regex, string, True) |
4013 | + else: |
4014 | + self.buffer.select_range(start, end) # ensure editmode logic is used |
4015 | + self.buffer.delete(start, end) |
4016 | + self.buffer.insert(start, string) |
4017 | |
4018 | self._update_highlight() |
4019 | |
4020 | @@ -4875,6 +5022,7 @@ |
4021 | self.page = page |
4022 | buffer = TextBuffer(self.ui.notebook, self.page) |
4023 | buffer.connect('insert-object', self.on_insert_object) |
4024 | + buffer.connect('insert-table', self.on_insert_table) |
4025 | self.view.set_buffer(buffer) |
4026 | tree = page.get_parsetree() |
4027 | |
4028 | @@ -5428,6 +5576,9 @@ |
4029 | |
4030 | menu.show_all() |
4031 | |
4032 | + def do_reload_page(self): |
4033 | + self.ui.reload_page() |
4034 | + |
4035 | def undo(self): |
4036 | '''Menu action to undo a single step''' |
4037 | self.undostack.undo() |
4038 | @@ -5909,6 +6060,75 @@ |
4039 | |
4040 | widget.show_all() |
4041 | |
4042 | + def insert_table_at_cursor(self, obj): |
4043 | + '''Inserts a table object in the page |
4044 | + @param obj: an object implementing L{CustomerObjectClass} |
4045 | + ''' |
4046 | + assert isinstance(obj, CustomObjectClass) |
4047 | + self.on_insert_table(self.view.get_buffer(), obj) |
4048 | + |
4049 | + def on_insert_table(self, buffer, obj, interactive=False): |
4050 | + # adopted from on_insert_object, with some changes: |
4051 | + # - 'type=table' added to xml-element |
4052 | + # - content of fallback-object is like a plaintext table |
4053 | + # - content of table-widget is not text, but a xml table tree |
4054 | + # - callbacks for 'link-clicked' and 'edit-object' |
4055 | + logger.debug("Insert table(%s, %s)", buffer, obj) |
4056 | + |
4057 | + obj.attrib['type'] = 'table' |
4058 | + if not isinstance(obj, CustomObjectClass): |
4059 | + # assume obj is a parsetree element |
4060 | + element = obj |
4061 | + if not 'type' in element.attrib: |
4062 | + return None |
4063 | + obj = ObjectManager.get_object(element.attrib['type'], element.attrib, element) |
4064 | + if not hasattr(obj, 'attr'): |
4065 | + obj.attr = dict() |
4066 | + obj.attr['type'] = 'table' |
4067 | + |
4068 | + if isinstance(obj, FallbackObject): |
4069 | + # if table plugin is not loaded - show table as plain text |
4070 | + tree = ParseTree(element) |
4071 | + text = get_dumper('wiki').dump(tree) |
4072 | + lines = "".join(text) |
4073 | + obj._data = lines |
4074 | + |
4075 | + def on_modified_changed(obj): |
4076 | + if obj.get_modified() and not buffer.get_modified(): |
4077 | + buffer.set_modified(True) |
4078 | + |
4079 | + def on_edit_object(pageview, obj): |
4080 | + plugin = ObjectManager.find_plugin(obj._attrib['type']) |
4081 | + window_extension = plugin[4] |
4082 | + |
4083 | + if window_extension: |
4084 | + window_extension.do_edit_object(obj) |
4085 | + |
4086 | + obj.connect('modified-changed', on_modified_changed) |
4087 | + obj.connect_object('link-clicked', PageView.do_link_clicked, self) |
4088 | + obj.connect_object('edit-object', on_edit_object, self) |
4089 | + |
4090 | + iter = buffer.get_insert_iter() |
4091 | + |
4092 | + def on_release_cursor(widget, position, anchor): |
4093 | + myiter = buffer.get_iter_at_child_anchor(anchor) |
4094 | + if position == POSITION_END: |
4095 | + myiter.forward_char() |
4096 | + buffer.place_cursor(myiter) |
4097 | + self.view.grab_focus() |
4098 | + |
4099 | + anchor = ObjectAnchor(obj) |
4100 | + buffer.insert_child_anchor(iter, anchor) |
4101 | + widget = obj.get_widget() |
4102 | + assert isinstance(widget, CustomObjectWidget) |
4103 | + widget.connect('release-cursor', on_release_cursor, anchor) |
4104 | + self.view.add_child_at_anchor(widget, anchor) |
4105 | + self._object_widgets.add(widget) |
4106 | + |
4107 | + widget.show_all() |
4108 | + if not isinstance(obj, FallbackObject): |
4109 | + widget.toolbar_hide() |
4110 | + |
4111 | def on_view_size_allocate(self, textview, allocation): |
4112 | for widget in self._object_widgets: |
4113 | if widget._resize: |
4114 | |
4115 | === modified file 'zim/objectmanager.py' |
4116 | --- zim/objectmanager.py 2014-11-09 11:07:20 +0000 |
4117 | +++ zim/objectmanager.py 2015-04-04 11:26:41 +0000 |
4118 | @@ -23,11 +23,13 @@ |
4119 | def __init__(self): |
4120 | self.factories = {} |
4121 | self.objects = {'fallback': WeakSet()} |
4122 | + self.window_extensions = {} |
4123 | |
4124 | - def register_object(self, type, factory): |
4125 | + def register_object(self, type, factory, window_extension=None): |
4126 | '''Register a factory method or class for a specific object type. |
4127 | @param type: the object type as string (unique name) |
4128 | @param factory: can be either an object class or a method, |
4129 | + @param window_extension: dictionary - the plugin related window_extension |
4130 | should callable and return objects. When constructing objects |
4131 | this factory will be called as:: |
4132 | |
4133 | @@ -44,6 +46,7 @@ |
4134 | old = self.factories.get(type) |
4135 | self.factories[type] = factory |
4136 | self.objects[type] = WeakSet() |
4137 | + self.window_extensions[type] = window_extension |
4138 | return old |
4139 | |
4140 | def unregister_object(self, type): |
4141 | @@ -97,8 +100,8 @@ |
4142 | '''Find a plugin to handle a specific object type. Intended to |
4143 | suggest plugins to the user that can be loaded. |
4144 | @param type: object type as string |
4145 | - @returns: a 3-tuple of the plugin name, a boolean for the |
4146 | - dependency check, and the plugin class, or C{None}. |
4147 | + @returns: a 5-tuple of the plugin name, a boolean for the |
4148 | + dependency check, the plugin class, or C{None} and the related plugin window_extension |
4149 | ''' |
4150 | for name in zim.plugins.PluginManager.list_installed_plugins(): # XXX |
4151 | try: |
4152 | @@ -106,7 +109,8 @@ |
4153 | types = klass.plugin_info.get('object_types') |
4154 | if types and type in types: |
4155 | activatable = klass.check_dependencies_ok() |
4156 | - return (name, klass.plugin_info['name'], activatable, klass) |
4157 | + win_ext = self.window_extensions[type] if type in self.window_extensions else None |
4158 | + return (name, klass.plugin_info['name'], activatable, klass, win_ext) |
4159 | except: |
4160 | logger.exception('Could not load plugin %s', name) |
4161 | continue |
4162 | |
4163 | === added file 'zim/plugins/tableeditor.py' |
4164 | --- zim/plugins/tableeditor.py 1970-01-01 00:00:00 +0000 |
4165 | +++ zim/plugins/tableeditor.py 2015-04-04 11:26:41 +0000 |
4166 | @@ -0,0 +1,1159 @@ |
4167 | +# -*- coding: utf-8 -*- |
4168 | + |
4169 | +# Author: Tobias Haupenthal |
4170 | +# Plugin created: 2015 |
4171 | +# |
4172 | +# |
4173 | +# This plugin includes a whole featureset. Please be familiar with the documentation in 'Plugins:Table Editor', |
4174 | +# before you do here any code change. |
4175 | +# |
4176 | +# - Suggestions for the future: |
4177 | +# better column sort-algorithm "sort-by-number-or-string" |
4178 | +# undo / redo - not trivial, because currently only position in textview is saved |
4179 | +# ideas: save everytime the whole table OR save a tuple (position in textview, row, column) |
4180 | + |
4181 | + |
4182 | +import gobject |
4183 | +import gtk |
4184 | +import logging |
4185 | +from xml.etree import ElementTree |
4186 | +import re |
4187 | +import pango |
4188 | + |
4189 | +logger = logging.getLogger('zim.plugin.tableeditor') |
4190 | + |
4191 | +from zim.actions import action |
4192 | +from zim.plugins import PluginClass, extends, WindowExtension |
4193 | +from zim.utils import WeakSet |
4194 | +from zim.objectmanager import ObjectManager, CustomObjectClass |
4195 | +from zim.config import String |
4196 | +from zim.main import get_zim_application |
4197 | +from zim.gui.widgets import Dialog, ScrolledWindow, IconButton, InputEntry |
4198 | +from zim.gui.objectmanager import CustomObjectWidget |
4199 | + |
4200 | +OBJECT_TYPE = 'table' |
4201 | + |
4202 | +SYNTAX_CELL_INPUT = [ |
4203 | + ('&', '&'), ('>', '>'), ('<', '<'), ('"', '"'), (''', "'"), ('\n', '\\n') |
4204 | +] |
4205 | + |
4206 | +# Regex replacement strings: Wiki-Parsetree -> Pango (Table cell) -> Input (Table cell editing) |
4207 | +# the target pattern is easier to read, the source pattern is generated out of it |
4208 | +# With this syntax text can be format within a table-cell |
4209 | +SYNTAX_WIKI_PANGO2 = [ |
4210 | + (r'<strong>\1</strong>', r'<b>\1</b>', r'**\1**'), |
4211 | + (r'<mark>\1</mark>', r'<span background="yellow">\1</span>', r'__\1__'), |
4212 | + (r'<code>\1</code>', r'<tt>\1</tt>', r"''\1''"), |
4213 | + (r'<strike>\1</strike>', r'<s>\1</s>', r'~~\1~~'), |
4214 | + # Link url without link text - Link url has always size = 0 |
4215 | + (r'<link href="\1">\1</link>', r'<span foreground="blue">\1<span size="0">\1</span></span>', r'[[\1]]'), |
4216 | + # Link url with link text - Link url has always size = 0 |
4217 | + (r'<link href="\1">\2</link>', r'<span foreground="blue">\2<span size="0">\1</span></span>', r'[[\2|\1]]'), |
4218 | + (r'<emphasis>\1</emphasis>', r'<i>\1</i>', r'//\1//') |
4219 | +] |
4220 | + |
4221 | +# Possible alignments in edit-table-dialog |
4222 | +COLUMNS_ALIGNMENTS = {'left': ['left', gtk.STOCK_JUSTIFY_LEFT, _('Left')], # T: alignment option |
4223 | + 'center': ['center', gtk.STOCK_JUSTIFY_CENTER, _('Center')], # T: alignment option |
4224 | + 'right': ['right', gtk.STOCK_JUSTIFY_RIGHT, _('Right')], # T: alignment option |
4225 | + 'normal': ['normal', None, _('Unspecified')],} # T: alignment option |
4226 | + |
4227 | + |
4228 | +def reg_replace(string): |
4229 | + ''' |
4230 | + Target pattern is translated into source regex pattern |
4231 | + :param string: target pattern |
4232 | + :return:source pattern |
4233 | + ''' |
4234 | + string = string.replace('*', '\*').replace('[', '\[').replace(']', '\]') \ |
4235 | + .replace(r'\1', '(.+?)', 1).replace(r'\2', '(.+?)', 1).replace('|', '\|') |
4236 | + return re.compile(string) |
4237 | + |
4238 | +# Regex compiled search patterns |
4239 | +SYNTAX_WIKI_PANGO = [tuple(map(reg_replace, expr_list)) for expr_list in SYNTAX_WIKI_PANGO2] |
4240 | + |
4241 | + |
4242 | +class TableEditorPlugin(PluginClass): |
4243 | + ''' |
4244 | + This is the plugin for displaying tables within the wiki. |
4245 | + A table consists always of a header with at least one header-cell and at least one or several rows. |
4246 | + The number of cells in a row must be equal to the header. |
4247 | + Currently there are two attributes, which have a tuple format, so they can describe all columns: |
4248 | + - aligns: left, center, right |
4249 | + - wraps: 0 / display text in a row 1 / long text will be broken and wrapped |
4250 | + |
4251 | + Most other files which are linked to this plugin are: |
4252 | + - zim.gui.pageview |
4253 | + - zim.formats.wiki |
4254 | + ''' |
4255 | + plugin_info = { |
4256 | + 'name': _('Table Editor'), # T: plugin name |
4257 | + 'description': _('''\ |
4258 | +With this plugin you can embed a 'Table' into the wiki page. Tables will be shown as GTK TreeView widgets. |
4259 | +Exporting them to various formats (i.e. HTML/LaTeX) completes the feature set. |
4260 | +'''), # T: plugin description |
4261 | + 'object_types': (OBJECT_TYPE, ), |
4262 | + 'help': 'Plugins:Table Editor', |
4263 | + 'author': 'Tobias Haupenthal', |
4264 | + } |
4265 | + |
4266 | + global LINES_NONE, LINES_HORIZONTAL, LINES_VERTICAL, LINES_BOTH # Hack - to make sure translation is loaded |
4267 | + LINES_BOTH = _('with lines') # T: option value |
4268 | + LINES_NONE = _('no grid lines') # T: option value |
4269 | + LINES_HORIZONTAL = _('horizontal lines') # T: option value |
4270 | + LINES_VERTICAL = _('vertical lines') # T: option value |
4271 | + |
4272 | + |
4273 | + |
4274 | + plugin_preferences = ( |
4275 | + # key, type, label, default |
4276 | + ('show_helper_toolbar', 'bool', _('Show helper toolbar'), True), # T: preference description |
4277 | + |
4278 | + # option for displaying grid-lines within the table |
4279 | + ('grid_lines', 'choice', _('Grid lines'), LINES_BOTH, (LINES_BOTH, LINES_NONE, LINES_HORIZONTAL, LINES_VERTICAL)), |
4280 | + # T: preference description |
4281 | + ) |
4282 | + |
4283 | + def __init__(self, config=None): |
4284 | + ''' Constructor ''' |
4285 | + PluginClass.__init__(self, config) |
4286 | + self.connectto(self.preferences, 'changed', self.on_preferences_changed) |
4287 | + |
4288 | + def create_table(self, attrib, text): |
4289 | + ''' |
4290 | + Automatic way for displaying the table-object as a table within the wiki, |
4291 | + :param attrib: {type: 'table', wraps:'1,0,1' , aligns:'left,right,center' } |
4292 | + :param text: XML - formated as a zim-tree table-object OR tuple of [header], [row1], [row2] |
4293 | + :return: a TableViewObject |
4294 | + ''' |
4295 | + if ElementTree.iselement(text) and attrib.get('type') == 'table': |
4296 | + (header, rows) = self._tabledom_to_list(text) |
4297 | + else: |
4298 | + # parameters in case of the Table-Insert-Dialog |
4299 | + header = text[0] |
4300 | + rows = [len(text[0]) * [' ']] |
4301 | + |
4302 | + '''Factory method for Table objects''' |
4303 | + obj = TableViewObject(attrib, header, rows, self.preferences) |
4304 | + return obj |
4305 | + |
4306 | + def _tabledom_to_list(self, tabledata): |
4307 | + ''' |
4308 | + Extracts necessary data out of a xml-table into a list structure |
4309 | + |
4310 | + :param tabledata: XML - formated as a zim-tree table-object |
4311 | + :return: tuple of header-list and list of row lists - ([h1,h2],[[r11,r12],[r21,r22]) |
4312 | + ''' |
4313 | + header = map(lambda head: head.text.decode('utf-8'), tabledata.findall('thead/th')) |
4314 | + header = map(CellFormatReplacer.zim_to_cell, header) |
4315 | + |
4316 | + rows = [] |
4317 | + for trow in tabledata.findall('trow'): |
4318 | + row = trow.findall('td') |
4319 | + row = [ElementTree.tostring(r, 'utf-8').replace('<td>', '').replace('</td>', '') for r in row] |
4320 | + row = map(CellFormatReplacer.zim_to_cell, row) |
4321 | + rows.append(row) |
4322 | + return header, rows |
4323 | + |
4324 | + def on_preferences_changed(self, preferences): |
4325 | + '''Update preferences on open table objects''' |
4326 | + for obj in ObjectManager.get_active_objects(OBJECT_TYPE): |
4327 | + obj.preferences_changed() |
4328 | + |
4329 | + |
4330 | +class CellFormatReplacer: |
4331 | + ''' |
4332 | + Static class for converting formated text from one into the other format: |
4333 | + - cell: in a wiki pageview the table-cell must be of this format |
4334 | + - input: if a user is editing the cell, this format is used |
4335 | + - zimtree: Format for zimtree xml structure |
4336 | + ''' |
4337 | + @staticmethod |
4338 | + def cell_to_input(text, with_pango=False): |
4339 | + ''' Displayed table-cell will converted to gtk-entry input text ''' |
4340 | + if with_pango: |
4341 | + for pattern, replace in zip(SYNTAX_WIKI_PANGO, SYNTAX_WIKI_PANGO2): |
4342 | + text = pattern[1].sub(replace[2], text) |
4343 | + for k, v in SYNTAX_CELL_INPUT: |
4344 | + text = text.replace(k, v) |
4345 | + return text |
4346 | + |
4347 | + @staticmethod |
4348 | + def input_to_cell(text, with_pango=False): |
4349 | + for k, v in SYNTAX_CELL_INPUT: |
4350 | + text = text.replace(v, k) |
4351 | + if with_pango: |
4352 | + # Links without text are handled as [[link]] and not as [[link|text]], therefore reverse order of replacements |
4353 | + for pattern, replace in zip(reversed(SYNTAX_WIKI_PANGO), reversed(SYNTAX_WIKI_PANGO2)): |
4354 | + text = pattern[2].sub(replace[1], text) |
4355 | + return text |
4356 | + |
4357 | + @staticmethod |
4358 | + def zim_to_cell(text): |
4359 | + for pattern, replace in zip(SYNTAX_WIKI_PANGO, SYNTAX_WIKI_PANGO2): |
4360 | + text = pattern[0].sub(replace[1], text) |
4361 | + return text |
4362 | + |
4363 | + @staticmethod |
4364 | + def cell_to_zim(text): |
4365 | + for pattern, replace in zip(SYNTAX_WIKI_PANGO, SYNTAX_WIKI_PANGO2): |
4366 | + text = pattern[1].sub(replace[0], text) |
4367 | + return text |
4368 | + |
4369 | +@extends('MainWindow') |
4370 | +class MainWindowExtension(WindowExtension): |
4371 | + ''' |
4372 | + Connector between the zim application with its toolbar and menu and the tableview-object |
4373 | + In GTK there is no native table symbol. So this image is needed: data/pixmaps/insert-table.png |
4374 | + ''' |
4375 | + uimanager_xml = ''' |
4376 | + <ui> |
4377 | + <menubar name='menubar'> |
4378 | + <menu action='insert_menu'> |
4379 | + <placeholder name='plugin_items'> |
4380 | + <menuitem action='insert_table'/> |
4381 | + </placeholder> |
4382 | + </menu> |
4383 | + </menubar> |
4384 | + <toolbar name='toolbar'> |
4385 | + <placeholder name='format'> |
4386 | + <toolitem action='insert_table'/> |
4387 | + </placeholder> |
4388 | + </toolbar> |
4389 | + </ui> |
4390 | + ''' |
4391 | + |
4392 | + ''' static reference to window object, necessary for access to pageview ''' |
4393 | + window = None |
4394 | + |
4395 | + def __init__(self, plugin, window): |
4396 | + ''' Constructor ''' |
4397 | + WindowExtension.__init__(self, plugin, window) |
4398 | + MainWindowExtension.window = window |
4399 | + |
4400 | + ObjectManager.register_object(OBJECT_TYPE, self.plugin.create_table, self) |
4401 | + |
4402 | + # reload tables on current page after plugin activation |
4403 | + if self.window.ui.page: |
4404 | + self.window.ui.reload_page() |
4405 | + |
4406 | + def teardown(self): |
4407 | + ''' Deconstructor ''' |
4408 | + ObjectManager.unregister_object(OBJECT_TYPE) |
4409 | + self.window.ui.reload_page() |
4410 | + |
4411 | + @staticmethod |
4412 | + def get_geometry(): |
4413 | + '''Returns tuple of geometry data from wiki textview window. Useful for setting width of object''' |
4414 | + win = MainWindowExtension.window.pageview.view.get_window(gtk.TEXT_WINDOW_TEXT) |
4415 | + geometry = win.get_geometry() if hasattr(win, 'get_geometry') else None |
4416 | + return geometry |
4417 | + |
4418 | + @action(_('Table'), stock='zim-insert-table', readonly=False) # T: menu item |
4419 | + def insert_table(self): |
4420 | + '''Run the InsertTableDialog''' |
4421 | + col_model = EditTableDialog(self.window, self.plugin, self.window.pageview).run() |
4422 | + if not col_model: |
4423 | + return |
4424 | + |
4425 | + _ids, headers, aligns, wraps = ([], [], [], []) |
4426 | + |
4427 | + for model in col_model: |
4428 | + headers.append(model[1]) |
4429 | + aligns.append(model[3]) |
4430 | + wraps.append(model[2]) |
4431 | + |
4432 | + attrs = {'aligns': aligns, 'wraps': wraps} |
4433 | + |
4434 | + obj = self.plugin.create_table(attrs, [headers]) |
4435 | + obj.attrib = {'type': OBJECT_TYPE} |
4436 | + pageview = self.window.pageview |
4437 | + pageview.insert_table_at_cursor(obj) |
4438 | + |
4439 | + def do_edit_object(self, obj): |
4440 | + ''' |
4441 | + With the right button press a context-menu is opened and a table can then be edited |
4442 | + ''' |
4443 | + self.do_edit_table(obj) |
4444 | + |
4445 | + def do_edit_table(self, obj): |
4446 | + '''Run the EditTableDialog ''' |
4447 | + |
4448 | + aligns = obj.get_aligns() |
4449 | + wraps = obj.get_wraps() |
4450 | + titles = [col.get_title() for col in obj.treeview.get_columns()] |
4451 | + old_model = [] |
4452 | + for i in range(len(titles)): |
4453 | + old_model.append([i, titles[i], aligns[i], wraps[i]]) |
4454 | + new_model = EditTableDialog(self.window, self.plugin, self.window.pageview, old_model).run() |
4455 | + |
4456 | + if new_model: |
4457 | + self._update_table_view(obj, new_model) |
4458 | + |
4459 | + def _update_table_view(self, obj, new_model): |
4460 | + ''' |
4461 | + Replaces liststore of currently displayed treeview with updated data and fixes references to attributes |
4462 | + :param obj: tableview object |
4463 | + :param new_model: tuple of lists for ([id], [header], [warps], [aligns]) |
4464 | + ''' |
4465 | + # prepare results out of dialog-window |
4466 | + id_mapping, headers, aligns, wraps = ({}, [], [], []) |
4467 | + for i, model in enumerate(new_model): |
4468 | + if model[0] != -1: |
4469 | + id_mapping[i] = model[0] |
4470 | + header = model[1] if model[1] else ' ' |
4471 | + headers.append(header) |
4472 | + aligns.append(model[3]) |
4473 | + wraps.append(model[2]) |
4474 | + |
4475 | + # creation of new table-view widget |
4476 | + attrs = {'aligns': aligns, 'wraps': wraps} |
4477 | + newrows = self._calculate_new_liststore(obj.treeview.get_model(), id_mapping, len(headers)) |
4478 | + widget = TableViewWidget(obj, headers, newrows, attrs) |
4479 | + new_treeview = widget.get_treeview() |
4480 | + new_model = new_treeview.get_model() |
4481 | + |
4482 | + # update of displayed table(=treeview) and its structure (=liststore) |
4483 | + obj.treeview.set_model(new_model) |
4484 | + # remove all old columns and move new columns to original treeview |
4485 | + for col in obj.treeview.get_columns(): |
4486 | + obj.treeview.remove_column(col) |
4487 | + for i, col in enumerate(new_treeview.get_columns()): |
4488 | + new_treeview.remove_column(col) |
4489 | + obj.treeview.append_column(col) |
4490 | + title_label = TableViewWidget.create_headerlabel(headers[i]) |
4491 | + col.set_widget(title_label) |
4492 | + |
4493 | + geometry = MainWindowExtension.get_geometry()[2] if MainWindowExtension.get_geometry() else 600 |
4494 | + TableViewWidget.wrap_columns(obj.treeview, geometry, wraps) |
4495 | + |
4496 | + obj.set_aligns(aligns) |
4497 | + obj.set_wraps(wraps) |
4498 | + obj.set_modified(True) |
4499 | + |
4500 | + def _calculate_new_liststore(self, liststore, id_mapping, nr_cols): |
4501 | + ''' Old value of cells are used in the new table, but only if its column is not deleted ''' |
4502 | + new_rows = [] |
4503 | + for oldrow in liststore: |
4504 | + newrow = [' ']*nr_cols |
4505 | + for v, k in id_mapping.iteritems(): |
4506 | + newrow[v] = oldrow[k] |
4507 | + new_rows.append(newrow) |
4508 | + return new_rows |
4509 | + |
4510 | + |
4511 | +class TableViewObject(CustomObjectClass): |
4512 | + '''data presenter of an inserted table within a page''' |
4513 | + OBJECT_ATTR = { |
4514 | + 'type': String('table'), |
4515 | + 'aligns': String(''), # i.e. String(left,right,center) |
4516 | + 'wraps': String('') # i.e. String(0,1,0) |
4517 | + } |
4518 | + |
4519 | + def __init__(self, attrib, header, rows, preferences): |
4520 | + ''' |
4521 | + Creates a new object which can displayed within the page |
4522 | + :param attrib: aligns, wraps |
4523 | + :param header: titles of the table as list |
4524 | + :param rows: body-rows of the table as list of lists |
4525 | + :param preferences: optionally some preferences |
4526 | + ''' |
4527 | + _attrib = {} |
4528 | + for k, v in attrib.iteritems(): |
4529 | + if isinstance(v, list): |
4530 | + v = ','.join(map(str, v)) |
4531 | + _attrib[k] = v |
4532 | + CustomObjectClass.__init__(self, _attrib, [header]+rows) |
4533 | + |
4534 | + self._tableattrib = attrib |
4535 | + self._header = header |
4536 | + self._rows = rows |
4537 | + self.modified = False |
4538 | + self.preferences = preferences |
4539 | + self.treeview = None |
4540 | + self._widgets = WeakSet() |
4541 | + self.textview_geometry = None |
4542 | + |
4543 | + # getters and setters for attributes |
4544 | + def get_aligns(self): |
4545 | + ''' get the list of align-attributes ''' |
4546 | + return self._attrib['aligns'].split(',') |
4547 | + |
4548 | + def set_aligns(self, data): |
4549 | + ''' Set list of align attributes for the current table. Each item belongs to a column.''' |
4550 | + assert(isinstance(data, list)) |
4551 | + self._attrib['aligns'] = ','.join(data) |
4552 | + |
4553 | + def get_wraps(self): |
4554 | + ''' get the list of wrap-attributes ''' |
4555 | + return map(int, self._attrib['wraps'].split(',')) |
4556 | + |
4557 | + def set_wraps(self, data): |
4558 | + ''' Set list of wrap attributes for the current table. Each item belongs to a column.''' |
4559 | + assert(isinstance(data, list)) |
4560 | + self._attrib['wraps'] = ','.join(str(item) for item in data) |
4561 | + |
4562 | + def get_widget(self): |
4563 | + ''' Creates a new table-widget which can displayed on the wiki-page ''' |
4564 | + attrib = {'aligns': self.get_aligns(), 'wraps': self.get_wraps()} |
4565 | + widget = TableViewWidget(self, self._header, self._rows, attrib) |
4566 | + treeview = widget.get_treeview() |
4567 | + self.treeview = treeview |
4568 | + liststore = treeview.get_model() |
4569 | + liststore.connect('row-changed', self.on_modified_changed) |
4570 | + |
4571 | + self._widgets.add(widget) |
4572 | + widget.set_preferences(self.preferences) |
4573 | + return widget |
4574 | + |
4575 | + def preferences_changed(self): |
4576 | + ''' Updates all created table-widgets, if preferences have changed ''' |
4577 | + for widget in self._widgets: |
4578 | + widget.set_preferences(self.preferences) |
4579 | + |
4580 | + def on_sort_column_changed(self, liststore): |
4581 | + ''' Trigger after a column-header is clicked and therefore its sort order has changed ''' |
4582 | + self.set_modified(True) |
4583 | + |
4584 | + def on_modified_changed(self, liststore, path, treeiter): |
4585 | + ''' Trigger after a table cell content is changed by the user ''' |
4586 | + self.set_modified(True) |
4587 | + |
4588 | + def get_data(self): |
4589 | + '''Returns table-object into textual data, for saving it as text.''' |
4590 | + liststore = self.treeview.get_model() |
4591 | + headers = [] |
4592 | + rows = [] |
4593 | + |
4594 | + # parsing table header and attributes |
4595 | + for column in self.treeview.get_columns(): |
4596 | + title = column.get_title() if column.get_title() else ' ' |
4597 | + headers.append(title) |
4598 | + attrs = {'aligns': self._attrib['aligns'], 'wraps': self._attrib['wraps']} |
4599 | + |
4600 | + # parsing rows |
4601 | + treeiter = liststore.get_iter_first() |
4602 | + while treeiter is not None: |
4603 | + row = [] |
4604 | + for colid in range(len(self.treeview.get_columns())): |
4605 | + val = liststore.get_value(treeiter, colid) if liststore.get_value(treeiter, colid) else ' ' |
4606 | + row.append(val) |
4607 | + rows.append(row) |
4608 | + treeiter = liststore.iter_next(treeiter) |
4609 | + rows = [map(lambda cell: CellFormatReplacer.cell_to_input(cell, True), row) for row in rows] |
4610 | + |
4611 | + # logger.debug("Table as get-data: : %s, %s, %s", headers, rows, attrs) |
4612 | + return headers, rows, attrs |
4613 | + |
4614 | + |
4615 | + def dump(self, format, dumper, linker=None): |
4616 | + ''' Dumps currently structure for table into textual format - mostly used for debugging / testing purposes ''' |
4617 | + return CustomObjectClass.dump(self, format, dumper, linker) |
4618 | + |
4619 | + |
4620 | +class TableViewWidget(CustomObjectWidget): |
4621 | + |
4622 | + def __init__(self, obj, headers, rows, attrs): |
4623 | + ''' |
4624 | + This is a group of GTK Gui elements which are directly displayed within the wiki textarea |
4625 | + On initilizing also some signals are registered and a toolbar is initialized |
4626 | + :param obj: a Table-View-Object |
4627 | + :param headers: list of titles |
4628 | + :param rows: list of list of cells |
4629 | + :param attrs: table settings, like alignment and wrapping |
4630 | + :return: |
4631 | + ''' |
4632 | + self.textarea_width = 0 |
4633 | + |
4634 | + # used in pageview |
4635 | + self._resize = True # attribute, that triggers resizing |
4636 | + self._has_cursor = False # Skip table object, if someone moves cursor around in textview |
4637 | + |
4638 | + # used here |
4639 | + self.obj = obj |
4640 | + self._timer = None # NONE or number of current gobject.timer, which is running |
4641 | + self._keep_toolbar_open = False # a cell is currently edited, toolbar should not be hidden |
4642 | + self._cellinput_canceled = None # cell changes should be skipped |
4643 | + self._toolbar_enabled = True # sets if toolbar should be shown beneath a selected table |
4644 | + |
4645 | + gtk.EventBox.__init__(self) |
4646 | + self.set_border_width(5) |
4647 | + |
4648 | + # Add vbox and wrap it to have a shadow around it |
4649 | + self.vbox = gtk.VBox() #: C{gtk.VBox} to contain widget contents |
4650 | + |
4651 | + # Toolbar for table actions |
4652 | + toolbar = self.create_toolbar() |
4653 | + self.obj.toolbar = toolbar |
4654 | + |
4655 | + # Actual gtk table object |
4656 | + self.treeview = self.create_treeview(headers, rows, attrs) |
4657 | + |
4658 | + # Hook up signals & set options |
4659 | + self.treeview.connect('button-press-event', self.on_button_press_event) |
4660 | + self.treeview.connect('focus-in-event', self.on_focus_in, toolbar) |
4661 | + self.treeview.connect('focus-out-event', self.on_focus_out, toolbar) |
4662 | + self.treeview.connect('move-cursor', self.on_move_cursor) |
4663 | + |
4664 | + # Set options |
4665 | + self.treeview.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH) |
4666 | + self.treeview.set_receives_default(True) |
4667 | + self.treeview.set_size_request(-1, -1) |
4668 | + self.treeview.set_border_width(2) |
4669 | + |
4670 | + # disable interactive column search |
4671 | + self.treeview.set_enable_search(False) |
4672 | + gtk.binding_entry_remove(gtk.TreeView, gtk.keysyms.f, gtk.gdk.CONTROL_MASK) |
4673 | + self.treeview.set_search_column(-1) |
4674 | + |
4675 | + # package gui elements |
4676 | + self.vbox.pack_end(toolbar) |
4677 | + self.add(self.vbox) |
4678 | + win = ScrolledWindow(self.treeview, gtk.POLICY_NEVER, gtk.POLICY_NEVER, gtk.SHADOW_OUT) |
4679 | + self.vbox.pack_start(win) |
4680 | + |
4681 | + def on_focus_in(self, treeview, event, toolbar): |
4682 | + '''After a table is selected, this function will be triggered''' |
4683 | + |
4684 | + self._keep_toolbar_open = False |
4685 | + if self._timer: |
4686 | + gobject.source_remove(self._timer) |
4687 | + if self._toolbar_enabled: |
4688 | + toolbar.show() |
4689 | + |
4690 | + def on_focus_out(self, treeview, event, toolbar): |
4691 | + '''After a table is deselected, this function will be triggered''' |
4692 | + def receive_alarm(): |
4693 | + if self._keep_toolbar_open: |
4694 | + self._timer = None |
4695 | + if self._timer: |
4696 | + self._timer = None |
4697 | + treeview.get_selection().unselect_all() |
4698 | + if self._toolbar_enabled: |
4699 | + toolbar.hide() |
4700 | + return False |
4701 | + |
4702 | + self._timer = gobject.timeout_add(500, receive_alarm) |
4703 | + |
4704 | + def create_toolbar(self): |
4705 | + '''This function creates a toolbar which is displayed next to the table''' |
4706 | + toolbar = gtk.Toolbar() |
4707 | + toolbar.set_orientation(gtk.ORIENTATION_HORIZONTAL) |
4708 | + toolbar.set_style(gtk.TOOLBAR_ICONS) |
4709 | + toolbar.set_border_width(1) |
4710 | + |
4711 | + tooltips = gtk.Tooltips() |
4712 | + for pos, stock, handler, data, tooltip in ( |
4713 | + (0, gtk.STOCK_ADD, self.on_add_row, None, _('Add row')), # T: tooltip on mouse hover |
4714 | + (1, gtk.STOCK_DELETE, self.on_delete_row, None, _('Remove row')), # T: tooltip on mouse hover |
4715 | + (2, gtk.STOCK_COPY, self.on_clone_row, None, _('Clone row')), # T: tooltip on mouse hover |
4716 | + (3, None, None, None, None), |
4717 | + (4, gtk.STOCK_GO_UP, self.on_move_row, -1, _('Row up')), # T: tooltip on mouse hover |
4718 | + (5, gtk.STOCK_GO_DOWN, self.on_move_row, 1, _('Row down')), # T: tooltip on mouse hover |
4719 | + (6, None, None, None, None), |
4720 | + (7, gtk.STOCK_PREFERENCES, self.on_change_columns, None, _('Change columns')), # T: tooltip on mouse hover |
4721 | + (8, None, None, None, None), |
4722 | + (9, gtk.STOCK_HELP, self.on_open_help, None, _('Open help')), # T: tooltip on mouse hover |
4723 | + ): |
4724 | + if stock is None: |
4725 | + toolbar.insert(gtk.SeparatorToolItem(), pos) |
4726 | + else: |
4727 | + button = gtk.ToolButton(stock) |
4728 | + if data: |
4729 | + button.connect('clicked', handler, data) |
4730 | + else: |
4731 | + button.connect('clicked', handler) |
4732 | + tooltips.set_tip(button, tooltip) |
4733 | + toolbar.insert(button, pos) |
4734 | + |
4735 | + toolbar.set_size_request(300,-1) |
4736 | + toolbar.set_icon_size(gtk.ICON_SIZE_MENU) |
4737 | + |
4738 | + return toolbar |
4739 | + |
4740 | + def toolbar_hide(self): |
4741 | + ''' Hide toolbar of the table (moving rows around, etc.) ''' |
4742 | + self.obj.toolbar.hide() |
4743 | + |
4744 | + def resize_to_textview(self, view): |
4745 | + ''' Overriding - on resizing the table should not expanded to 100% width''' |
4746 | + win = view.get_window(gtk.TEXT_WINDOW_TEXT) |
4747 | + if not win or win.get_geometry()[2] == self.textarea_width: |
4748 | + return |
4749 | + |
4750 | + self.textarea_width = win.get_geometry()[2] |
4751 | + self.wrap_columns(self.treeview, self.textarea_width, self.obj.get_wraps()) |
4752 | + |
4753 | + def wrap_columns(self, treeview, textarea_width, wraps): |
4754 | + TableViewWidget.wrap_columns(treeview, textarea_width, wraps) |
4755 | + |
4756 | + @staticmethod |
4757 | + def wrap_columns(treeview, textarea_width, wraps): |
4758 | + ''' Wrap all columns, which should be wrapped ''' |
4759 | + nrcols = len(treeview.get_columns()) |
4760 | + |
4761 | + if wraps == [1] * nrcols: # in case all columns are wrapped |
4762 | + max_width = textarea_width - 38 - nrcols * 10 if textarea_width else -1 |
4763 | + else: |
4764 | + max_width = textarea_width - 40 |
4765 | + if (not gtk.gtk_version >= (2, 8)) or max_width <= 0 or not textarea_width: |
4766 | + return |
4767 | + |
4768 | + for column, wrap in zip(treeview.get_columns(), wraps): |
4769 | + cell = column.get_cell_renderers()[0] |
4770 | + cell.set_property('yalign', 0.0) # no vertical alignment, text starts on the top |
4771 | + if wrap == 1: |
4772 | + cell.set_property('wrap-width', max_width//nrcols) |
4773 | + cell.set_property('wrap-mode', pango.WRAP_WORD) |
4774 | + |
4775 | + column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE) # allow column shrinks |
4776 | + column.set_max_width(0) # shrink column |
4777 | + column.set_max_width(-1) # reset value |
4778 | + column.set_sizing(gtk.TREE_VIEW_COLUMN_GROW_ONLY) # reset value |
4779 | + |
4780 | + def _column_alignment(self, aligntext): |
4781 | + ''' The column alignment must be converted from numeric to keywords ''' |
4782 | + if aligntext == 'left': |
4783 | + align = 0.0 |
4784 | + elif aligntext == 'center': |
4785 | + align = 0.5 |
4786 | + elif aligntext == 'right': |
4787 | + align = 1.0 |
4788 | + else: |
4789 | + align = None |
4790 | + return align |
4791 | + |
4792 | + def create_treeview(self, headers, rows, attrs): |
4793 | + ''' |
4794 | + Initializes a treeview with its model (liststore) and all its columns |
4795 | + :param headers: a list of title values for the column-headers |
4796 | + :param rows: a list of list of cells, for the table body |
4797 | + :param attrs: some more attributes, which define the layout of a column |
4798 | + :return: gtk.treeview |
4799 | + ''' |
4800 | + nrcols = len(headers) |
4801 | + |
4802 | + cols = [str]*nrcols |
4803 | + |
4804 | + liststore = gtk.ListStore(*cols) |
4805 | + treeview = gtk.TreeView(liststore) |
4806 | + for trow in rows: |
4807 | + liststore.append(trow) |
4808 | + |
4809 | + for i, headcol in enumerate(headers): |
4810 | + cell = gtk.CellRendererText() |
4811 | + tview_column = gtk.TreeViewColumn(headcol, cell) |
4812 | + treeview.append_column(tview_column) |
4813 | + |
4814 | + # set title as label |
4815 | + header_label = self.create_headerlabel(headcol) |
4816 | + tview_column.set_widget(header_label) |
4817 | + |
4818 | + # set properties of column |
4819 | + tview_column.set_attributes(cell, markup=i) |
4820 | + cell.set_property('editable', True) |
4821 | + tview_column.set_sort_column_id(i) |
4822 | + # set sort function |
4823 | + liststore.set_sort_func(i, self.sort_by_number_or_string, i) |
4824 | + # set alignment - left center right |
4825 | + align = self._column_alignment(attrs['aligns'][i]) |
4826 | + if align: |
4827 | + tview_column.set_alignment(align) |
4828 | + cell.set_alignment(align, 0.0) |
4829 | + |
4830 | + # callbacks after an action |
4831 | + cell.connect('edited', self.on_cell_changed, treeview.get_model(), i) |
4832 | + cell.connect('editing-started', self.on_cell_editing_started, treeview.get_model(), i) |
4833 | + cell.connect('editing-canceled', self.on_cell_editing_canceled) |
4834 | + |
4835 | + return treeview |
4836 | + |
4837 | + def create_headerlabel(self, title): |
4838 | + return TableViewWidget.create_headerlabel(title) |
4839 | + |
4840 | + @staticmethod |
4841 | + def create_headerlabel(title): |
4842 | + ''' Sets options for the treeview header''' |
4843 | + col_widget = gtk.VBox() |
4844 | + col_widget.show() |
4845 | + |
4846 | + |
4847 | + col_label = gtk.Label('<u>'+title+'</u>') |
4848 | + col_label.set_use_markup(True) |
4849 | + col_label.show() |
4850 | + col_widget.pack_start(col_label) |
4851 | + #col_align.add(col_label) |
4852 | + |
4853 | + '''col_entry = InputEntry() |
4854 | + col_entry.set_name('treeview-header-entry') |
4855 | + col_entry.show() |
4856 | + col_widget.pack_start(col_entry)''' |
4857 | + |
4858 | + return col_widget |
4859 | + |
4860 | + def get_treeview(self): |
4861 | + # treeview of current table |
4862 | + return self.treeview |
4863 | + |
4864 | + def set_preferences(self, preferences): |
4865 | + self._toolbar_enabled = preferences['show_helper_toolbar'] |
4866 | + |
4867 | + ''' Sets general plugin settings for this object''' |
4868 | + grid_option = self.pref_gridlines(preferences['grid_lines']) |
4869 | + self.treeview.set_grid_lines(grid_option) |
4870 | + pass |
4871 | + |
4872 | + def pref_gridlines(self, option): |
4873 | + if option == LINES_BOTH: |
4874 | + return gtk.TREE_VIEW_GRID_LINES_BOTH |
4875 | + elif option == LINES_NONE: |
4876 | + return gtk.TREE_VIEW_GRID_LINES_NONE |
4877 | + elif option == LINES_HORIZONTAL: |
4878 | + return gtk.TREE_VIEW_GRID_LINES_HORIZONTAL |
4879 | + elif option == LINES_VERTICAL: |
4880 | + return gtk.TREE_VIEW_GRID_LINES_VERTICAL |
4881 | + |
4882 | + def on_move_cursor(self, view, step_size, count): |
4883 | + ''' If you try to move the cursor out of the tableditor release the cursor to the parent textview ''' |
4884 | + return None # let parent handle this signal |
4885 | + |
4886 | + def fetch_cell_by_event(self, event, treeview): |
4887 | + ''' Looks for the cell where the mouse clicked on it ''' |
4888 | + liststore = treeview.get_model() |
4889 | + (xpos, ypos) = event.get_coords() |
4890 | + (treepath, treecol, xrel, yrel) = treeview.get_path_at_pos(int(xpos), int(ypos)) |
4891 | + treeiter = liststore.get_iter(treepath) |
4892 | + cellvalue = liststore.get_value(treeiter, treeview.get_columns().index(treecol)) |
4893 | + return cellvalue |
4894 | + |
4895 | + def get_linkurl(self, celltext): |
4896 | + ''' Checks a cellvalue if it contains a link and returns only the link value ''' |
4897 | + linkregex = r'<span foreground="blue">.*?<span.*?>(.*?)</span></span>' |
4898 | + matches = re.match(linkregex, celltext) |
4899 | + linkvalue = matches.group(1) if matches else None |
4900 | + return linkvalue |
4901 | + |
4902 | + def on_button_press_event(self, treeview, event): |
4903 | + ''' |
4904 | + Displays a context-menu on right button click |
4905 | + Opens the link of a tablecell on CTRL pressed and left button click |
4906 | + ''' |
4907 | + if event.type == gtk.gdk.BUTTON_PRESS and event.button == 1 and event.get_state() & gtk.gdk.CONTROL_MASK: |
4908 | + # With CTRL + LEFT-Mouse-Click link of cell is opened |
4909 | + cellvalue = self.fetch_cell_by_event(event, treeview) |
4910 | + linkvalue = self.get_linkurl(cellvalue) |
4911 | + if linkvalue: |
4912 | + self.obj.emit('link-clicked', {'href': linkvalue}) |
4913 | + return |
4914 | + |
4915 | + if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3: |
4916 | + # Right button opens context menu |
4917 | + self._keep_toolbar_open = True |
4918 | + cellvalue = self.fetch_cell_by_event(event, treeview) |
4919 | + linkvalue = self.get_linkurl(cellvalue) |
4920 | + linkitem_is_activated = (linkvalue is not None) |
4921 | + |
4922 | + menu = gtk.Menu() |
4923 | + |
4924 | + for stock, handler, data, tooltip in ( |
4925 | + (gtk.STOCK_ADD, self.on_add_row, None, _('Add row')), # T: menu item |
4926 | + (gtk.STOCK_DELETE, self.on_delete_row, None, _('Delete row')), # T: menu item |
4927 | + (gtk.STOCK_COPY, self.on_clone_row, None, _('Clone row')), # T: menu item |
4928 | + (None, None, None, None), # T: menu item |
4929 | + (gtk.STOCK_JUMP_TO, self.on_open_link, linkvalue, _('Open cell content link')), # T: menu item |
4930 | + (None, None, None, None), |
4931 | + (gtk.STOCK_GO_UP, self.on_move_row, -1, _('Row up')), # T: menu item |
4932 | + (gtk.STOCK_GO_DOWN, self.on_move_row, 1, _('Row down')), # T: menu item |
4933 | + (None, None, None, None), |
4934 | + (gtk.STOCK_PREFERENCES, self.on_change_columns, None, _('Change columns')) # T: menu item |
4935 | + ): |
4936 | + |
4937 | + if stock is None: |
4938 | + menu.append(gtk.SeparatorMenuItem()) |
4939 | + else: |
4940 | + item = gtk.ImageMenuItem(stock) |
4941 | + item.set_always_show_image(True) |
4942 | + item.set_label(_(tooltip)) |
4943 | + if data: |
4944 | + item.connect_after('activate', handler, data) |
4945 | + else: |
4946 | + item.connect_after('activate', handler) |
4947 | + if handler == self.on_open_link: |
4948 | + item.set_sensitive(linkitem_is_activated) |
4949 | + menu.append(item) |
4950 | + |
4951 | + menu.show_all() |
4952 | + menu.popup(None, None, None, event.button, event.time) |
4953 | + |
4954 | + def on_add_row(self, action): |
4955 | + ''' Context menu: Add a row ''' |
4956 | + selection = self.treeview.get_selection() |
4957 | + model, treeiter = selection.get_selected() |
4958 | + if not treeiter: # no selected item |
4959 | + self.selection_info() |
4960 | + return |
4961 | + |
4962 | + row = len(self.treeview.get_columns())*[''] |
4963 | + path = model.insert_after(treeiter, row) |
4964 | + self.obj.set_modified(True) |
4965 | + |
4966 | + def on_clone_row(self, action): |
4967 | + ''' Context menu: Clone a row ''' |
4968 | + selection = self.treeview.get_selection() |
4969 | + model, treeiter = selection.get_selected() |
4970 | + if not treeiter: # no selected item |
4971 | + self.selection_info() |
4972 | + return |
4973 | + |
4974 | + path = model.get_path(treeiter) |
4975 | + row = model[path[0]] |
4976 | + model.insert_after(treeiter, row) |
4977 | + self.obj.set_modified(True) |
4978 | + |
4979 | + def on_delete_row(self, action): |
4980 | + ''' Context menu: Delete a row ''' |
4981 | + selection = self.treeview.get_selection() |
4982 | + model, treeiter = selection.get_selected() |
4983 | + if not treeiter: # no selected item |
4984 | + self.selection_info() |
4985 | + return |
4986 | + |
4987 | + if len(model) > 1: |
4988 | + model.remove(treeiter) |
4989 | + self.obj.set_modified(True) |
4990 | + else: |
4991 | + md = gtk.MessageDialog(None, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE, |
4992 | + _("The table must consist of at least on row!\n No deletion done.")) |
4993 | + # T: Popup dialog |
4994 | + md.run() |
4995 | + md.destroy() |
4996 | + |
4997 | + def on_move_row(self, action, direction): |
4998 | + ''' Trigger for moving a row one position up/down ''' |
4999 | + selection = self.treeview.get_selection() |
5000 | + model, treeiter = selection.get_selected() |
Hi,
could you provide your plugin for testing?
Regards,
Murat
-----Ursprüngliche Nachricht-----
Von: <email address hidden> [mailto:<email address hidden>] Im Auftrag von Tobias Haupenthal
Gesendet: Mittwoch, 4. März 2015 00:58
An: <email address hidden>; Jaap Karssenberg
Betreff: [Merge] lp:~beutlin/zim/table-plugin into lp:zim
Tobias Haupenthal has proposed merging lp:~beutlin/zim/table-plugin into lp:zim.
Requested reviews:
Jaap Karssenberg (jaap.karssenberg)
For more details, see: /code.launchpad .net/~beutlin/ zim/table- plugin/ +merge/ 251673
https:/
Hello,
currently I've created a table plugin for zim and wish to get this into the main branch.
Please have a look at it.
Tobias Haupenthal
--
You are subscribed to branch lp:zim.