Merge lp:~ubuntu-branches/ubuntu/raring/labyrinth/raring-201211110411 into lp:ubuntu/raring/labyrinth
- Raring (13.04)
- raring-201211110411
- Merge into raring
Status: | Rejected |
---|---|
Rejected by: | James Westby |
Proposed branch: | lp:~ubuntu-branches/ubuntu/raring/labyrinth/raring-201211110411 |
Merge into: | lp:ubuntu/raring/labyrinth |
Diff against target: |
8900 lines (+8702/-0) (has conflicts) 34 files modified
.gitignore (+3/-0) .tx/config (+8/-0) README.rst (+113/-0) data/labyrinth.desktop (+10/-0) debian/source/format (+1/-0) help/C/labyrinth.xml (+449/-0) help/C/legal.xml (+76/-0) help/Makefile.am (+11/-0) help/de/de.po (+327/-0) install_data_files.sh (+18/-0) labyrinth (+93/-0) labyrinth_lib/BaseThought.py (+341/-0) labyrinth_lib/Browser.py (+340/-0) labyrinth_lib/DrawingThought.py (+619/-0) labyrinth_lib/ImageThought.py (+342/-0) labyrinth_lib/Links.py (+281/-0) labyrinth_lib/MMapArea.py (+1368/-0) labyrinth_lib/MainWindow.py (+770/-0) labyrinth_lib/MapList.py (+218/-0) labyrinth_lib/PeriodicSaveThread.py (+36/-0) labyrinth_lib/ResourceThought.py (+142/-0) labyrinth_lib/TextBufferMarkup.py (+307/-0) labyrinth_lib/TextThought.py (+1179/-0) labyrinth_lib/TrayIcon.py (+73/-0) labyrinth_lib/UndoManager.py (+249/-0) labyrinth_lib/__init__.py (+1/-0) labyrinth_lib/debug.py (+16/-0) labyrinth_lib/prefs.py (+38/-0) labyrinth_lib/utils.py (+205/-0) po/Makefile (+48/-0) po/es.po (+347/-0) po/labyrinth.pot (+316/-0) po/zh_CN.po (+337/-0) setup.py (+20/-0) Conflict adding file .gitignore. Moved existing file to .gitignore.moved. Conflict adding file .tx. Moved existing file to .tx.moved. Path conflict: <deleted> / labyrinth_lib Conflict adding file README.rst. Moved existing file to README.rst.moved. Conflict adding file data/labyrinth.desktop. Moved existing file to data/labyrinth.desktop.moved. Conflict adding file debian/source. Moved existing file to debian/source.moved. Conflict adding file help. Moved existing file to help.moved. Conflict adding file install_data_files.sh. Moved existing file to install_data_files.sh.moved. Conflict adding file labyrinth. Moved existing file to labyrinth.moved. Conflict adding files to labyrinth_lib. Created directory. Conflict because labyrinth_lib is not versioned, but has versioned children. Versioned directory. Conflict adding file labyrinth_lib. Moved existing file to labyrinth_lib.moved. Conflict adding file po/Makefile. Moved existing file to po/Makefile.moved. Conflict adding file po/es.po. Moved existing file to po/es.po.moved. Conflict adding file po/labyrinth.pot. Moved existing file to po/labyrinth.pot.moved. Conflict adding file po/zh_CN.po. Moved existing file to po/zh_CN.po.moved. Conflict adding file setup.py. Moved existing file to setup.py.moved. |
To merge this branch: | bzr merge lp:~ubuntu-branches/ubuntu/raring/labyrinth/raring-201211110411 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu branches | Pending | ||
Review via email: mp+133816@code.launchpad.net |
Commit message
Description of the change
The package importer has detected a possible inconsistency between the package history in the archive and the history in bzr. As the archive is authoritative the importer has made lp:ubuntu/raring/labyrinth reflect what is in the archive and the old bzr branch has been pushed to lp:~ubuntu-branches/ubuntu/raring/labyrinth/raring-201211110411. This merge proposal was created so that an Ubuntu developer can review the situations and perform a merge/upload if necessary. There are three typical cases where this can happen.
1. Where someone pushes a change to bzr and someone else uploads the package without that change. This is the reason that this check is done by the importer. If this appears to be the case then a merge/upload should be done if the changes that were in bzr are still desirable.
2. The importer incorrectly detected the above situation when someone made a change in bzr and then uploaded it.
3. The importer incorrectly detected the above situation when someone just uploaded a package and didn't touch bzr.
If this case doesn't appear to be the first situation then set the status of the merge proposal to "Rejected" and help avoid the problem in future by filing a bug at https:/
(this is an automatically generated message)
Unmerged revisions
Preview Diff
1 | === added file '.gitignore' |
2 | --- .gitignore 1970-01-01 00:00:00 +0000 |
3 | +++ .gitignore 2012-11-11 04:19:21 +0000 |
4 | @@ -0,0 +1,3 @@ |
5 | +*.pyc |
6 | +build/ |
7 | +*.mo |
8 | |
9 | === renamed file '.gitignore' => '.gitignore.moved' |
10 | === added directory '.tx' |
11 | === renamed directory '.tx' => '.tx.moved' |
12 | === added file '.tx/config' |
13 | --- .tx/config 1970-01-01 00:00:00 +0000 |
14 | +++ .tx/config 2012-11-11 04:19:21 +0000 |
15 | @@ -0,0 +1,8 @@ |
16 | +[main] |
17 | +host = https://www.transifex.com |
18 | + |
19 | +[labyrinth.master] |
20 | +file_filter = po/<lang>.po |
21 | +source_file = po/labyrinth.pot |
22 | +source_lang = en |
23 | +type = PO |
24 | |
25 | === added file 'README.rst' |
26 | --- README.rst 1970-01-01 00:00:00 +0000 |
27 | +++ README.rst 2012-11-11 04:19:21 +0000 |
28 | @@ -0,0 +1,113 @@ |
29 | +Labyrinth |
30 | +========= |
31 | + |
32 | +Labyrinth is a lightweight mind-mapping tool, written in Python using Gtk and |
33 | +Cairo to do the drawing. It is intended to be as light and intuitive as |
34 | +possible, but still provide a wide range of powerful features. |
35 | + |
36 | +A mind-map is a diagram used to represent words, ideas, tasks or other items |
37 | +linked to and arranged radially around a central key word or idea. It is used |
38 | +to generate, visualise, structure and classify ideas, and as an aid in study, |
39 | +organisation, problem solving, and decision making. (From wikipedia) |
40 | + |
41 | +Currently, Labyrinth provides 3 different types of thoughts, or nodes - Text, |
42 | +Image and Drawing. Text is the basic standard text node. Images allow you to |
43 | +insert and scale any supported image file (png, jpeg, svg). Drawings are for |
44 | +those times when you want to illustrate something, but don't want to fire up |
45 | +a separate drawing program. It allows you to quickly and easily sketch very |
46 | +simple line diagrams. |
47 | + |
48 | +License |
49 | +------- |
50 | + |
51 | +This software is released under the GNU GPL v2 (or later) license. All source |
52 | +files are included in this, unless explicitly stated in the source file itself. |
53 | +For copyright owners, please refer to the source files individually. |
54 | + |
55 | +The "labyrinth" icon (``data/labyrinth.svg`` and ``data/labyrinth-*.png``) is |
56 | +copyright Josef Vybíral and is released under the GNU GPL v2 license. |
57 | + |
58 | +Please refer to the "COPYING" file for a complete copy of the GNU GPL v2 |
59 | +license. |
60 | + |
61 | +All documentation (This file, anything in the docs directory) released with |
62 | +this package is released as public domain. The documentation, you can do with |
63 | +as you please. |
64 | + |
65 | +Requirements |
66 | +------------ |
67 | + |
68 | +* Python >= 2.6 |
69 | +* gtk+ |
70 | +* pygtk |
71 | +* pygobject |
72 | +* pycairo |
73 | +* PyXDG |
74 | + |
75 | +The minimum required versions are unknown, but any reasonably recent packages |
76 | +should work. |
77 | + |
78 | +How to use it |
79 | +------------- |
80 | + |
81 | +From the top directory of the package, run the command:: |
82 | + |
83 | + ./labyrinth |
84 | + |
85 | +You can also install Labyrinth with ``python setup.py install``, and |
86 | +``./install_data_files.sh`` for icons and translations. It can then be run as |
87 | +``labyrinth``. |
88 | + |
89 | +This will open a browser window, showing you all the maps currently available |
90 | +and allow you to modify / delete them and create new maps. The title is |
91 | +(currently) the primary thought text(truncated to 27 characters long). This is |
92 | +usually the first thought created in a new map. |
93 | + |
94 | +In a new map, single click somewhere to create a new "thought". This is your |
95 | +root. Add your main thought to this. Click somewhere else will create a new |
96 | +thought, linked to the first. To move thoughts around, single-click and drag. |
97 | +To edit a current thought, double click on it (text thoughts only). |
98 | + |
99 | +Drawing and Image thoughts can be resized using their corners / sides. |
100 | + |
101 | +Links between thoughts can be created, strengthened and weakened. To create a |
102 | +new link, in edit mode, click and drag from the "parent" thought to the "child" |
103 | +thought while holding down the ctrl key. Doing this with a link already in |
104 | +place will strengthen the link by 1 and dragging from child to parent will |
105 | +weaken the link by 1. If the link goes to 0 strength (it starts at 2), |
106 | +the link is deleted. Links can also be created / deleted by selecting both |
107 | +thoughts (hold down the shift key to select > 1 thought) and choosing |
108 | +"Edit->(Un)Link Thoughts" from the menu (shortcut: Ctrl-L). |
109 | + |
110 | +Loading and saving of maps is in the tomboy style - they are automatically |
111 | +saved, you shouldn't have to worry about them. For reference anyway, the maps |
112 | +are saved in ``~/.gnome2/labyrinth/<longstring>.map``. |
113 | + |
114 | +Future Plans |
115 | +------------ |
116 | + |
117 | +In ``doc/TheFuture``, there are a list of goals for a 1.0 release and for the next |
118 | +release. Releases are feature-based at this stage. Once all the required |
119 | +features are in place, a release is made. |
120 | + |
121 | +However a release may also be made without all the changes if it is deemed |
122 | +that this is in the best interest. |
123 | + |
124 | +Getting the Latest Development Code |
125 | +----------------------------------- |
126 | + |
127 | +Development happens on Github. See https://github.com/labyrinth-team/labyrinth |
128 | + |
129 | +Helping Out and Questions |
130 | +------------------------- |
131 | + |
132 | +If you have any questions about Labyrinth or just want to be part of our gang, |
133 | +the mailing list address is labyrinth-devel@googlegroups.com |
134 | + |
135 | +If you want to help out with developing labyrinth, please let us know on the |
136 | +mailing list. We aren't just looking for coders. We're looking for packagers, |
137 | +artists, doc writers, interface designers, web developers, and just about |
138 | +anyone else. |
139 | + |
140 | +Translations now take place `on Transifex <https://www.transifex.com/projects/p/labyrinth/>`_. |
141 | +If you want to use Transifex in your language, it's very easy to get started. |
142 | |
143 | === renamed file 'README.rst' => 'README.rst.moved' |
144 | === added file 'data/labyrinth.desktop' |
145 | --- data/labyrinth.desktop 1970-01-01 00:00:00 +0000 |
146 | +++ data/labyrinth.desktop 2012-11-11 04:19:21 +0000 |
147 | @@ -0,0 +1,10 @@ |
148 | +[Desktop Entry] |
149 | +Name=Labyrinth Mind-mapping |
150 | +GenericName=Mind-mapping |
151 | +Comment=Map your mind |
152 | +Exec=labyrinth |
153 | +Icon=labyrinth |
154 | +StartupNotify=true |
155 | +Terminal=false |
156 | +Type=Application |
157 | +Categories=GNOME;GTK;Application;Office; |
158 | |
159 | === renamed file 'data/labyrinth.desktop' => 'data/labyrinth.desktop.moved' |
160 | === added directory 'debian/source' |
161 | === renamed directory 'debian/source' => 'debian/source.moved' |
162 | === added file 'debian/source/format' |
163 | --- debian/source/format 1970-01-01 00:00:00 +0000 |
164 | +++ debian/source/format 2012-11-11 04:19:21 +0000 |
165 | @@ -0,0 +1,1 @@ |
166 | +3.0 (quilt) |
167 | |
168 | === added directory 'help' |
169 | === renamed directory 'help' => 'help.moved' |
170 | === added directory 'help/C' |
171 | === added directory 'help/C/figures' |
172 | === added file 'help/C/figures/lab_alignment.png' |
173 | Binary files help/C/figures/lab_alignment.png 1970-01-01 00:00:00 +0000 and help/C/figures/lab_alignment.png 2012-11-11 04:19:21 +0000 differ |
174 | === added file 'help/C/figures/lab_browser_start.png' |
175 | Binary files help/C/figures/lab_browser_start.png 1970-01-01 00:00:00 +0000 and help/C/figures/lab_browser_start.png 2012-11-11 04:19:21 +0000 differ |
176 | === added file 'help/C/figures/lab_map_start.png' |
177 | Binary files help/C/figures/lab_map_start.png 1970-01-01 00:00:00 +0000 and help/C/figures/lab_map_start.png 2012-11-11 04:19:21 +0000 differ |
178 | === added file 'help/C/labyrinth.xml' |
179 | --- help/C/labyrinth.xml 1970-01-01 00:00:00 +0000 |
180 | +++ help/C/labyrinth.xml 2012-11-11 04:19:21 +0000 |
181 | @@ -0,0 +1,449 @@ |
182 | +<?xml version="1.0"?> |
183 | +<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" |
184 | +"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [ |
185 | + <!ENTITY legal SYSTEM "legal.xml"> |
186 | + <!ENTITY appversion "0.5.0"> |
187 | + <!ENTITY manrevision "1.0"> |
188 | + <!ENTITY date "April 2008"> |
189 | + <!ENTITY app "<application>Labyrinth</application>"> |
190 | +]> |
191 | +<!-- |
192 | + (Do not remove this comment block.) |
193 | + Maintained by the GNOME Documentation Project |
194 | + http://developer.gnome.org/projects/gdp |
195 | + Template version: 2.0 beta |
196 | + Template last modified Apr 11, 2002 |
197 | +--> |
198 | +<!-- =============Document Header ============================= --> |
199 | +<article id="index" lang="en"> |
200 | +<!-- please do not change the id; for translations, change lang to --> |
201 | +<!-- appropriate code --> |
202 | + <articleinfo> |
203 | + <title>&app; Manual V&manrevision;</title> |
204 | + |
205 | + <copyright> |
206 | + <year>2008</year> |
207 | + <holder>Matthias Vogelgesang</holder> |
208 | + </copyright> |
209 | +<!-- translators: uncomment this: |
210 | + |
211 | + <copyright> |
212 | + <year>2002</year> |
213 | + <holder>ME-THE-TRANSLATOR (Latin translation)</holder> |
214 | + </copyright> |
215 | + |
216 | + --> |
217 | +<!-- An address can be added to the publisher information. If a role is |
218 | + not specified, the publisher/author is the same for all versions of the |
219 | + document. --> |
220 | + <publisher> |
221 | + <publishername> GNOME Documentation Project </publishername> |
222 | + </publisher> |
223 | + |
224 | + &legal; |
225 | + <!-- This file contains link to license for the documentation (GNU FDL), and |
226 | + other legal stuff such as "NO WARRANTY" statement. Please do not change |
227 | + any of this. --> |
228 | + |
229 | + <authorgroup> |
230 | + <author> |
231 | + <firstname>Documentation</firstname> |
232 | + <surname>Writer 2</surname> |
233 | + <affiliation> |
234 | + <orgname>GNOME Documentation Project</orgname> |
235 | + <address> <email>doc-writer2@gnome.org</email> </address> |
236 | + </affiliation> |
237 | + </author> |
238 | + <author> |
239 | + <firstname>Documentation</firstname> |
240 | + <surname>Writer 1</surname> |
241 | + <affiliation> |
242 | + <orgname>GNOME Documentation Project</orgname> |
243 | + <address> <email>doc-writer1@gnome.org</email> </address> |
244 | + </affiliation> |
245 | + </author> |
246 | + |
247 | +<!-- This is appropriate place for other contributors: translators, |
248 | + maintainers, etc. Commented out by default. |
249 | + |
250 | + <othercredit role="translator"> |
251 | + <firstname>Latin</firstname> |
252 | + <surname>Translator 1</surname> |
253 | + <affiliation> |
254 | + <orgname>Latin Translation Team</orgname> |
255 | + <address> <email>translator@gnome.org</email> </address> |
256 | + </affiliation> |
257 | + <contrib>Latin translation</contrib> |
258 | + </othercredit> |
259 | +--> |
260 | + </authorgroup> |
261 | + |
262 | + |
263 | +<!-- According to GNU FDL, revision history is mandatory if you are --> |
264 | +<!-- modifying/reusing someone else's document. If not, you can omit it. --> |
265 | +<!-- Remember to remove the &manrevision; entity from the revision entries other |
266 | +--> |
267 | +<!-- than the current revision. --> |
268 | +<!-- The revision numbering system for GNOME manuals is as follows: --> |
269 | +<!-- * the revision number consists of two components --> |
270 | +<!-- * the first component of the revision number reflects the release version of the GNOME desktop. --> |
271 | +<!-- * the second component of the revision number is a decimal unit that is incremented with each revision of the manual. --> |
272 | +<!-- For example, if the GNOME desktop release is V2.x, the first version of the manual that --> |
273 | +<!-- is written in that desktop timeframe is V2.0, the second version of the manual is V2.1, etc. --> |
274 | +<!-- When the desktop release version changes to V3.x, the revision number of the manual changes --> |
275 | +<!-- to V3.0, and so on. --> |
276 | + <releaseinfo>This manual describes version &appversion; of Labyrinth |
277 | + </releaseinfo> |
278 | + <legalnotice> |
279 | + <title>Feedback</title> |
280 | + <para>To report a bug or make a suggestion regarding the &app; application or |
281 | + this manual, follow the directions in the <ulink url="ghelp:gnome-feedback" |
282 | + type="help">GNOME Feedback Page</ulink>. |
283 | + </para> |
284 | +<!-- Translators may also add here feedback address for translations --> |
285 | + </legalnotice> |
286 | + </articleinfo> |
287 | + |
288 | + <indexterm zone="index"> |
289 | + <primary>MY-GNOME-APP</primary> |
290 | + </indexterm> |
291 | + <indexterm zone="index"> |
292 | + <primary>mygnomeapp</primary> |
293 | + </indexterm> |
294 | + |
295 | +<!-- ============= Document Body ============================= --> |
296 | +<!-- ============= Introduction ============================== --> |
297 | + <sect1 id="myapp-introduction"> |
298 | + <title>Introduction</title> |
299 | + <para>&app; is a light-weight mind-mapping tool to structure one's thoughts. |
300 | + &app; provides the following features:</para> |
301 | + <itemizedlist> |
302 | + <listitem> |
303 | + <para>Export maps as:</para> |
304 | + <itemizedlist> |
305 | + <listitem> |
306 | + <para>PDF - Portable Document Format</para> |
307 | + </listitem> |
308 | + <listitem> |
309 | + <para>SVG - Scalable Vector Graphics</para> |
310 | + </listitem> |
311 | + <listitem> |
312 | + <para>JPEG - Joint Photographic Experts Group</para> |
313 | + </listitem> |
314 | + <listitem> |
315 | + <para>PNG - Portable Network Graphics </para> |
316 | + </listitem> |
317 | + </itemizedlist> |
318 | + </listitem> |
319 | + </itemizedlist> |
320 | + </sect1> |
321 | + |
322 | +<!-- =========== Getting Started ============================== --> |
323 | + <sect1 id="myapp-getting-started"> |
324 | + <title>Getting Started</title> |
325 | + |
326 | + <sect2 id="myapp-start"> |
327 | + <title>To Start &app;</title> |
328 | + <para>You can start <application>&app;</application> in the following ways: |
329 | + </para> |
330 | + <variablelist> |
331 | + <varlistentry> |
332 | + <term><guimenu>Applications</guimenu> menu</term> |
333 | + <listitem> |
334 | + <para>Choose |
335 | + <menuchoice> |
336 | + <guisubmenu>Office</guisubmenu> |
337 | + <guimenuitem>Labyrinth</guimenuitem> |
338 | + </menuchoice>. </para> |
339 | + </listitem> |
340 | + </varlistentry> |
341 | + <varlistentry> |
342 | + <term>Command line</term> |
343 | + <listitem> |
344 | + <para>To start <application>&app;</application> from a command line, type the following command, |
345 | + then press <keycap>Return</keycap>:</para> |
346 | + <para> |
347 | + <command>labyrinth</command> |
348 | + </para> |
349 | + </listitem> |
350 | + </varlistentry> |
351 | + </variablelist> |
352 | + </sect2> |
353 | + |
354 | + <sect2 id="myapp-when-start"> |
355 | + <title>When You Start &app;</title> |
356 | + <para>When you start <application>&app;</application>, the following window is displayed.</para> |
357 | + |
358 | + <!-- ==== Figure ==== --> |
359 | + <figure id="browserwindow-fig"> |
360 | + <title>&app; Start Up Window</title> |
361 | + <screenshot> |
362 | + <mediaobject> |
363 | + <imageobject><imagedata |
364 | + fileref="figures/lab_browser_start.png" format="PNG"/> |
365 | + </imageobject> |
366 | + <!-- EPS versions of the figures are not required at the moment. --> |
367 | + <!-- |
368 | + <imageobject> |
369 | + <imagedata fileref="figures/image.eps" format="EPS"/> |
370 | + </imageobject> |
371 | + --> |
372 | + <textobject> |
373 | + <phrase>Shows &app; browser window. Contains titlebar, menubar, toolbar and the map list area. Menubar contains File and Help menus.</phrase> |
374 | + </textobject> |
375 | + </mediaobject> |
376 | + </screenshot> |
377 | + </figure> |
378 | + <!-- ==== End of Figure ==== --> |
379 | + |
380 | + <!-- Include any descriptions of the GUI immediately after the screenshot of the main UI, --> |
381 | + <!-- for example, the items on the menubar and on the toolbar. This section is optional. --> |
382 | + |
383 | + <para>The &app; browser window contains the following elements: |
384 | + </para> |
385 | + <variablelist> |
386 | + <varlistentry> |
387 | + <term>Menubar. </term> |
388 | + <listitem> |
389 | + <para>The menus on the menubar contain all of the commands you need to work with files in |
390 | + <application>&app;</application>.</para> |
391 | + </listitem> |
392 | + </varlistentry> |
393 | + <varlistentry> |
394 | + <term>Toolbar. </term> |
395 | + <listitem> |
396 | + <para> The toolbar contains a subset of the commands that you can access from the menubar.</para> |
397 | + </listitem> |
398 | + </varlistentry> |
399 | + </variablelist> |
400 | + </sect2> |
401 | + </sect1> |
402 | +<!-- ================ Usage ================================ --> |
403 | +<!-- Use this section to describe how to use the application to perform the tasks for |
404 | + which the application is designed. --> |
405 | + <sect1 id="myapp-usage"> |
406 | + <title>Basic Usage</title> |
407 | + <para>You can use the &app; application to perform the following tasks:</para> |
408 | + <!-- ================ Usage Subsection ================================ --> |
409 | + <sect2 id="labyrinth-new-map"> |
410 | + <title>To Create a new Map</title> |
411 | + <para>To create a new map, choose |
412 | + <menuchoice> |
413 | + <guimenu>File</guimenu> |
414 | + <guimenuitem>New</guimenuitem> |
415 | + </menuchoice> or click on <guimenuitem>New</guimenuitem> from the toolbar. A |
416 | + new empty map window appears: |
417 | + </para> |
418 | + <screenshot> |
419 | + <mediaobject> |
420 | + <imageobject><imagedata |
421 | + fileref="figures/lab_map_start.png" format="PNG"/> |
422 | + </imageobject> |
423 | + <textobject> |
424 | + <phrase>Shows &app; map window. Contains titlebar, menubar, toolbar and the map canvas area. Menubar contains File, Edit, View and Mode menus.</phrase> |
425 | + </textobject> |
426 | + </mediaobject> |
427 | + </screenshot> |
428 | + </sect2> |
429 | + <!-- ================ Usage Subsection ================================ --> |
430 | + <sect2 id="labyrinth-manipulate-view"> |
431 | + <title>To Manipulate the View of the Map</title> |
432 | + <para>You can use the following methods to resize the view of the Map in |
433 | + the &app; window:</para> |
434 | + <itemizedlist> |
435 | + <listitem> |
436 | + <para>To enlarge the view, choose |
437 | + <menuchoice> |
438 | + <guimenu>View</guimenu> |
439 | + <guimenuitem>Zoom In</guimenuitem> |
440 | + </menuchoice>.</para> |
441 | + </listitem> |
442 | + <listitem> |
443 | + <para>To shrink the view, choose |
444 | + <menuchoice> |
445 | + <guimenu>View</guimenu> |
446 | + <guimenuitem>Zoom Out</guimenuitem> |
447 | + </menuchoice>.</para> |
448 | + </listitem> |
449 | + </itemizedlist> |
450 | + <para>To move the map around you have several options:</para> |
451 | + <itemizedlist> |
452 | + <listitem><para>Use the scroll arrows around the white canvas.</para></listitem> |
453 | + <listitem><para>Hold the middle button of your mouse and move the |
454 | + canvas while still holding the button.</para></listitem> |
455 | + </itemizedlist> |
456 | + </sect2> |
457 | + <!-- ================ Usage Subsection ================================ --> |
458 | + <sect2 id="labyrinth-export"> |
459 | + <title>To Export a Map</title> |
460 | + <para>To export the map, you can use the following methods:</para> |
461 | + <itemizedlist> |
462 | + <listitem> |
463 | + <para>To export the map for further loading, choose |
464 | + <menuchoice> |
465 | + <guimenu>File</guimenu> |
466 | + <guimenuitem>Export Map...</guimenuitem> |
467 | + </menuchoice>.</para> |
468 | + </listitem> |
469 | + <listitem> |
470 | + <para>To export the map as an image, choose |
471 | + <menuchoice> |
472 | + <guimenu>File</guimenu> |
473 | + <guimenuitem>Export as Image</guimenuitem> |
474 | + </menuchoice> and use the file dialog to choose the format you would like |
475 | + to save the map and an appropriate file name. At the time of writing you |
476 | + can export the map as PDF, SVG, PNG and JPG.</para> |
477 | + </listitem> |
478 | + </itemizedlist> |
479 | + <tip><para>Use a vector format like PDF or SVG for high quality printing purposes and |
480 | + a bitmap format like PNG or JPG for the web.</para></tip> |
481 | + </sect2> |
482 | + </sect1> |
483 | + |
484 | + <sect1 id="labyrinth-work"> |
485 | + <title>Working with the Map</title> |
486 | + <para>The most common work you will do with &app; are:</para> |
487 | + |
488 | + |
489 | + <sect2 id="labyrinth-create-thoughts"> |
490 | + <title>To Create a Thought</title> |
491 | + <para>In &app; a thought can be:</para> |
492 | + <itemizedlist> |
493 | + <listitem><para>Pure Text</para></listitem> |
494 | + <listitem><para>An Image</para></listitem> |
495 | + <listitem><para>A Drawing</para></listitem> |
496 | + <listitem><para>A Resource like an URL</para></listitem> |
497 | + </itemizedlist> |
498 | + <para>What you are going to create depends on the mode that is activated. |
499 | + The most common mode is the Edit Mode which can be activated by choosing |
500 | + <menuchoice><guimenu>Mode</guimenu><guimenuitem>Edit Mode</guimenuitem></menuchoice>. |
501 | + By clicking anywhere on the white map you create a new thought. You then |
502 | + have to type some text which you can further <link linkend="labyrinth-format-thoughts">format</link> for your thought.</para> |
503 | + |
504 | + <para>An image thought is created in Image Mode which can be activated by |
505 | + choosing <menuchoice><guimenu>Mode</guimenu><guimenuitem>Add Image</guimenuitem></menuchoice>. |
506 | + After creating the thought a dialog pops up asking for the image to load.</para> |
507 | + |
508 | + <para>In Drawing Mode (choose <menuchoice><guimenu>Mode</guimenu> |
509 | + <guimenuitem>Drawing Mode</guimenuitem></menuchoice>.) you can freely |
510 | + make some sketches by dragging the mouse over the canvas.</para> |
511 | + |
512 | + <para>A resource thought can be added by choosing <menuchoice><guimenu>Mode</guimenu> |
513 | + <guimenuitem>Add Resource</guimenuitem></menuchoice>. If you click |
514 | + somewhere on the map you are asked to enter a URL and can then proceed |
515 | + to edit the thought's text. If you double-click on a created |
516 | + resource thought the URL is loaded in a webbrowser.</para> |
517 | + </sect2> |
518 | + |
519 | + <sect2 id="labyrinth-manipulate-thoughts"> |
520 | + <title>To Manipulate Thoughts</title> |
521 | + <para>Selecting a thought for moving or <link linkend="labyrinth-align-thoughts">alignment</link> |
522 | + can be done by:</para> |
523 | + <itemizedlist> |
524 | + <listitem><para>Clicking on individual thoughts</para></listitem> |
525 | + <listitem><para>Shift-Clicking to select more than one thought</para></listitem> |
526 | + <listitem><para>Using a selection box by holding the <mousebutton>left</mousebutton> mouse button and dragging |
527 | + the mouse while holding that button.</para></listitem> |
528 | + </itemizedlist> |
529 | + <para>Selected thoughts can be moved around by left-dragging them with |
530 | + the mouse and <link linkend="labyrinth-format-thoughts">formatted</link> all at once.</para>. |
531 | + </sect2> |
532 | + |
533 | + <sect2 id="labyrinth-working-with-links"> |
534 | + <title>Working with Links</title> |
535 | + <para>Links are created automatically between a selected parent thought and a newly |
536 | + created thought. If there is no thought selected, a connection is made between |
537 | + the last selected - as a last resort the root - and the new thought. You can |
538 | + create new links between existing thoughts by choosing |
539 | + <menuchoice><guimenu>Edit</guimenu><guimenuitem>Link Thoughts</guimenuitem></menuchoice> or |
540 | + pressing <keycombo><keycap>Ctrl</keycap><keycap>L</keycap></keycombo>.</para> |
541 | + <para>A link can be strengthened or weakened by selecting it with the <mousebutton>left</mousebutton> |
542 | + mouse button and using <keycap>+</keycap> respectively <keycap>-</keycap>. The strength |
543 | + of a link is visualised by the thickness of the line. A link with a strength of |
544 | + zero is automatically deleted.</para> |
545 | + <tip><para>You can customize the look of the links by choosing either |
546 | + <menuchoice><guimenu>View</guimenu><guimenuitem>Use Curves</guimenuitem></menuchoice> for |
547 | + curved links or |
548 | + <menuchoice><guimenu>View</guimenu><guimenuitem>Use Lines</guimenuitem></menuchoice> for |
549 | + straight lined links.</para></tip> |
550 | + </sect2> |
551 | + |
552 | + <sect2 id="labyrinth-format-thoughts"> |
553 | + <title>Format a Thought's Text</title> |
554 | + <para>A text and a resource thought can be formatted by using the tools of the lower |
555 | + format toolbar. For editing a thoughts text either:</para> |
556 | + <itemizedlist> |
557 | + <listitem><para>Double-Click on a text thought</para></listitem> |
558 | + <listitem><para>Use <menuchoice><guimenu>context menu</guimenu> |
559 | + <guimenuitem>Edit Text</guimenuitem></menuchoice></para></listitem> |
560 | + </itemizedlist> |
561 | + <para>You can then mark text by:</para> |
562 | + <itemizedlist> |
563 | + <listitem><para>Double-Click on text to select all</para></listitem> |
564 | + <listitem><para>Press <shortcut><keycombo><keycap>Ctrl</keycap><keycap>A</keycap></keycombo></shortcut> |
565 | + to select all</para></listitem> |
566 | + <listitem><para>Drag the mouse to select parts of the text</para></listitem> |
567 | + </itemizedlist> |
568 | + <para>Now you are able to format the selected text just like in a word processor. |
569 | + You can use the tools in the lower toolbar to change:</para> |
570 | + <itemizedlist> |
571 | + <listitem><para>characters to bold letters</para></listitem> |
572 | + <listitem><para>characters to italized letters</para></listitem> |
573 | + <listitem><para>characters to underlined letter</para></listitem> |
574 | + <listitem><para>the font of the letters</para></listitem> |
575 | + <listitem><para>the letter's color</para></listitem> |
576 | + <listitem><para>the background's color</para></listitem> |
577 | + </itemizedlist> |
578 | + </sect2> |
579 | + <sect2 id="labyrinth-align-thoughts"> |
580 | + <title>Align Thoughts</title> |
581 | + <para>In order to organize your maps you can align the |
582 | + <link linkend="labyrinth-manipulate-thoughts">selected</link> thoughts |
583 | + on your map by using the alignment tools in the lower toolbar:</para> |
584 | + <screenshot> |
585 | + <mediaobject> |
586 | + <imageobject><imagedata |
587 | + fileref="figures/lab_alignment.png" format="PNG"/> |
588 | + </imageobject> |
589 | + <textobject> |
590 | + <phrase>Shows &app; alignment window containing tools for aligning vertically, |
591 | + horizontally and all four edges.</phrase> |
592 | + </textobject> |
593 | + </mediaobject> |
594 | + </screenshot> |
595 | + <note><para>All thoughts are aligned to the first selected thought.</para></note> |
596 | + </sect2> |
597 | + </sect1> |
598 | +<!-- ============= Bugs ================================== --> |
599 | +<!-- This section is optional and is commented out by default. |
600 | + You can use it to describe known bugs and limitations of the |
601 | + program if there are any - please be frank and list all |
602 | + problems you know of. |
603 | + |
604 | + <sect1 id="mayapp-bugs"> |
605 | + <title>Known Bugs and Limitations</title> |
606 | + <para> </para> |
607 | + </sect1> |
608 | +--> |
609 | +<!-- ============= About ================================== --> |
610 | + <sect1 id="myapp-about"> |
611 | + <title>About &app;</title> |
612 | + <para> &app; was written by Don Scorgio, Martin Schaaf and Matthias Vogelgesang. |
613 | + To find more information about &app;, please visit the |
614 | + <ulink url="http://www.gnome.org/~dscorgie/labyrinth.html" type="http">Labyrinth Web |
615 | + page</ulink>. </para> |
616 | + <para> |
617 | + To report a bug or make a suggestion regarding this application or |
618 | + this manual, follow the directions in this |
619 | + <ulink url="ghelp:gnome-feedback" type="help">document</ulink>. |
620 | + </para> |
621 | + |
622 | + <para> This program is distributed under the terms of the GNU |
623 | + General Public license as published by the Free Software |
624 | + Foundation; either version 2 of the License, or (at your option) |
625 | + any later version. A copy of this license can be found at this |
626 | + <ulink url="ghelp:gpl" type="help">link</ulink>, or in the file |
627 | + COPYING included with the source code of this program. </para> |
628 | + |
629 | + </sect1> |
630 | +</article> |
631 | |
632 | === added file 'help/C/legal.xml' |
633 | --- help/C/legal.xml 1970-01-01 00:00:00 +0000 |
634 | +++ help/C/legal.xml 2012-11-11 04:19:21 +0000 |
635 | @@ -0,0 +1,76 @@ |
636 | + <legalnotice id="legalnotice"> |
637 | + <para> |
638 | + Permission is granted to copy, distribute and/or modify this |
639 | + document under the terms of the GNU Free Documentation |
640 | + License (GFDL), Version 1.1 or any later version published |
641 | + by the Free Software Foundation with no Invariant Sections, |
642 | + no Front-Cover Texts, and no Back-Cover Texts. You can find |
643 | + a copy of the GFDL at this <ulink type="help" |
644 | + url="ghelp:fdl">link</ulink> or in the file COPYING-DOCS |
645 | + distributed with this manual. |
646 | + </para> |
647 | + <para> This manual is part of a collection of GNOME manuals |
648 | + distributed under the GFDL. If you want to distribute this |
649 | + manual separately from the collection, you can do so by |
650 | + adding a copy of the license to the manual, as described in |
651 | + section 6 of the license. |
652 | + </para> |
653 | + |
654 | + <para> |
655 | + Many of the names used by companies to distinguish their |
656 | + products and services are claimed as trademarks. Where those |
657 | + names appear in any GNOME documentation, and the members of |
658 | + the GNOME Documentation Project are made aware of those |
659 | + trademarks, then the names are in capital letters or initial |
660 | + capital letters. |
661 | + </para> |
662 | + |
663 | + <para> |
664 | + DOCUMENT AND MODIFIED VERSIONS OF THE DOCUMENT ARE PROVIDED |
665 | + UNDER THE TERMS OF THE GNU FREE DOCUMENTATION LICENSE |
666 | + WITH THE FURTHER UNDERSTANDING THAT: |
667 | + |
668 | + <orderedlist> |
669 | + <listitem> |
670 | + <para>DOCUMENT IS PROVIDED ON AN "AS IS" BASIS, |
671 | + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR |
672 | + IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES |
673 | + THAT THE DOCUMENT OR MODIFIED VERSION OF THE |
674 | + DOCUMENT IS FREE OF DEFECTS MERCHANTABLE, FIT FOR |
675 | + A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE |
676 | + RISK AS TO THE QUALITY, ACCURACY, AND PERFORMANCE |
677 | + OF THE DOCUMENT OR MODIFIED VERSION OF THE |
678 | + DOCUMENT IS WITH YOU. SHOULD ANY DOCUMENT OR |
679 | + MODIFIED VERSION PROVE DEFECTIVE IN ANY RESPECT, |
680 | + YOU (NOT THE INITIAL WRITER, AUTHOR OR ANY |
681 | + CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY |
682 | + SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER |
683 | + OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS |
684 | + LICENSE. NO USE OF ANY DOCUMENT OR MODIFIED |
685 | + VERSION OF THE DOCUMENT IS AUTHORIZED HEREUNDER |
686 | + EXCEPT UNDER THIS DISCLAIMER; AND |
687 | + </para> |
688 | + </listitem> |
689 | + <listitem> |
690 | + <para>UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL |
691 | + THEORY, WHETHER IN TORT (INCLUDING NEGLIGENCE), |
692 | + CONTRACT, OR OTHERWISE, SHALL THE AUTHOR, |
693 | + INITIAL WRITER, ANY CONTRIBUTOR, OR ANY |
694 | + DISTRIBUTOR OF THE DOCUMENT OR MODIFIED VERSION |
695 | + OF THE DOCUMENT, OR ANY SUPPLIER OF ANY OF SUCH |
696 | + PARTIES, BE LIABLE TO ANY PERSON FOR ANY |
697 | + DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR |
698 | + CONSEQUENTIAL DAMAGES OF ANY CHARACTER |
699 | + INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS |
700 | + OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR |
701 | + MALFUNCTION, OR ANY AND ALL OTHER DAMAGES OR |
702 | + LOSSES ARISING OUT OF OR RELATING TO USE OF THE |
703 | + DOCUMENT AND MODIFIED VERSIONS OF THE DOCUMENT, |
704 | + EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF |
705 | + THE POSSIBILITY OF SUCH DAMAGES. |
706 | + </para> |
707 | + </listitem> |
708 | + </orderedlist> |
709 | + </para> |
710 | + </legalnotice> |
711 | + |
712 | |
713 | === added file 'help/ChangeLog' |
714 | === added file 'help/Makefile.am' |
715 | --- help/Makefile.am 1970-01-01 00:00:00 +0000 |
716 | +++ help/Makefile.am 2012-11-11 04:19:21 +0000 |
717 | @@ -0,0 +1,11 @@ |
718 | +include $(top_srcdir)/gnome-doc-utils.make |
719 | + |
720 | +DOC_LINGUAS = de |
721 | + |
722 | +DOC_MODULE = labyrinth |
723 | + |
724 | +DOC_ENTITIES = \ |
725 | + legal.xml |
726 | + |
727 | +dist-hook: doc-dist-hook |
728 | + |
729 | |
730 | === added directory 'help/de' |
731 | === added file 'help/de/de.po' |
732 | --- help/de/de.po 1970-01-01 00:00:00 +0000 |
733 | +++ help/de/de.po 2012-11-11 04:19:21 +0000 |
734 | @@ -0,0 +1,327 @@ |
735 | +# |
736 | +msgid "" |
737 | +msgstr "" |
738 | +"Project-Id-Version: Labyrinth 0.5\n" |
739 | +"Report-Msgid-Bugs-To: \n" |
740 | +"POT-Creation-Date: 2008-05-12 00:12+0200\n" |
741 | +"PO-Revision-Date: 2008-05-12 00:17+0200\n" |
742 | +"Last-Translator: Matthias Vogelgesang <matthias.vogelgesang@gmail.com>\n" |
743 | +"Language-Team: Project-Id-Version: gtranslator 1.1.5" |
744 | +"Report-Msgid-Bugs-To: " |
745 | +"POT-Creation-Date: 2004-08-31 20:57+0200" |
746 | +"PO-Revision-Date: 2004-08-31 20:58+0200" |
747 | +"Last-Translator: Hendrik Brandt <eru@gmx.li>" |
748 | +"Language-Team: German <gnome-de@gnome.org>" |
749 | +"MIME-Version: 1.0" |
750 | +"Content-Type: text/plain; charset=UTF-8" |
751 | +"Content-Transfer-Encoding: 8bit" |
752 | +"Plural-Forms: nplurals=2; plural=(n != 1); <Project-Id-Version: gtranslator 1.1.5" |
753 | +"Report-Msgid-Bugs-To: " |
754 | +"POT-Creation-Date: 2004-08-31 20:57+0200" |
755 | +"PO-Revision-Date: 2004-08-31 20:58+0200" |
756 | +"Last-Translator: Hendrik Brandt <eru@gmx.li>" |
757 | +"Language-Team: German <gnome-de@gnome.org>" |
758 | +"MIME-Version: 1.0" |
759 | +"Content-Type: text/plain; charset=UTF-8" |
760 | +"Content-Transfer-Encoding: 8bit" |
761 | +"Plural-Forms: nplurals=2; plural=(n != 1);>\n" |
762 | +"MIME-Version: 1.0\n" |
763 | +"Content-Type: text/plain; charset=UTF-8\n" |
764 | +"Content-Transfer-Encoding: 8bit" |
765 | + |
766 | +#. When image changes, this message will be marked fuzzy or untranslated for you. |
767 | +#. It doesn't matter what you translate it to: it's not used at all. |
768 | +#: labyrinth.xml:206(None) |
769 | +msgid "@@image: 'figures/myapp_start_window.png'; md5=THIS FILE DOESN'T EXIST" |
770 | +msgstr "" |
771 | + |
772 | +#: labyrinth.xml:22(title) |
773 | +msgid "<application>Labyrinth</application> Manual V1.0" |
774 | +msgstr "<application>Labyrinth</application> Manual V1.0" |
775 | + |
776 | +#: labyrinth.xml:25(year) |
777 | +msgid "2008" |
778 | +msgstr "2008" |
779 | + |
780 | +#: labyrinth.xml:26(holder) |
781 | +msgid "Matthias Vogelgesang" |
782 | +msgstr "Matthias Vogelgesang" |
783 | + |
784 | +#: labyrinth.xml:40(publishername) labyrinth.xml:53(orgname) labyrinth.xml:61(orgname) labyrinth.xml:103(para) labyrinth.xml:113(para) |
785 | +msgid "GNOME Documentation Project" |
786 | +msgstr "" |
787 | + |
788 | +#: labyrinth.xml:50(firstname) labyrinth.xml:58(firstname) |
789 | +msgid "Documentation" |
790 | +msgstr "Dokumentation" |
791 | + |
792 | +#: labyrinth.xml:51(surname) |
793 | +msgid "Writer 2" |
794 | +msgstr "" |
795 | + |
796 | +#: labyrinth.xml:54(email) |
797 | +msgid "doc-writer2@gnome.org" |
798 | +msgstr "" |
799 | + |
800 | +#: labyrinth.xml:59(surname) |
801 | +msgid "Writer 1" |
802 | +msgstr "" |
803 | + |
804 | +#: labyrinth.xml:62(email) |
805 | +msgid "doc-writer1@gnome.org" |
806 | +msgstr "" |
807 | + |
808 | +#: labyrinth.xml:97(revnumber) |
809 | +msgid "Labyrinth Manual V1.0" |
810 | +msgstr "Labyrinth Manual V1.0" |
811 | + |
812 | +#: labyrinth.xml:100(para) |
813 | +msgid "Documentation Writer 2 <email>docwriter2@gnome.org</email>" |
814 | +msgstr "" |
815 | + |
816 | +#: labyrinth.xml:107(revnumber) |
817 | +msgid "Full title of previous manual." |
818 | +msgstr "" |
819 | + |
820 | +#: labyrinth.xml:108(date) |
821 | +msgid "Release date of previous manual." |
822 | +msgstr "" |
823 | + |
824 | +#: labyrinth.xml:110(para) |
825 | +msgid "Documentation Writer 1 <email>docwriter1@gnome.org</email>" |
826 | +msgstr "" |
827 | + |
828 | +#: labyrinth.xml:118(releaseinfo) |
829 | +msgid "This manual describes version 0.5.0 of Labyrinth" |
830 | +msgstr "Dieses Manual beschreibt Labyrinth Version 0.5.0" |
831 | + |
832 | +#: labyrinth.xml:121(title) |
833 | +msgid "Feedback" |
834 | +msgstr "" |
835 | + |
836 | +#: labyrinth.xml:122(para) |
837 | +msgid "To report a bug or make a suggestion regarding the <application>Labyrinth</application> application or this manual, follow the directions in the <ulink url=\"ghelp:gnome-feedback\" type=\"help\">GNOME Feedback Page</ulink>." |
838 | +msgstr "" |
839 | + |
840 | +#: labyrinth.xml:131(primary) |
841 | +msgid "MY-GNOME-APP" |
842 | +msgstr "" |
843 | + |
844 | +#: labyrinth.xml:134(primary) |
845 | +msgid "mygnomeapp" |
846 | +msgstr "" |
847 | + |
848 | +#: labyrinth.xml:140(title) |
849 | +msgid "Introduction" |
850 | +msgstr "" |
851 | + |
852 | +#: labyrinth.xml:141(para) |
853 | +msgid "<application>Labyrinth</application> is a light-weight mind-mapping tool to structure one's thoughts. <application>Labyrinth</application> provides the following features:" |
854 | +msgstr "" |
855 | + |
856 | +#: labyrinth.xml:145(para) |
857 | +msgid "Export maps as:" |
858 | +msgstr "Exportiert Maps als:" |
859 | + |
860 | +#: labyrinth.xml:148(para) |
861 | +msgid "PDF - Portable Document Format" |
862 | +msgstr "PDF - Portable Document Format" |
863 | + |
864 | +#: labyrinth.xml:151(para) |
865 | +msgid "SVG - Scalable Vector Graphics" |
866 | +msgstr "SVG - Scalable Vector Graphics" |
867 | + |
868 | +#: labyrinth.xml:154(para) |
869 | +msgid "JPEG - Joint Photographic Experts Group" |
870 | +msgstr "JPEG - Joint Photographic Experts Group" |
871 | + |
872 | +#: labyrinth.xml:157(para) |
873 | +msgid "PNG - Portable Network Graphics" |
874 | +msgstr "PNG - Portable Network Graphics" |
875 | + |
876 | +#: labyrinth.xml:166(title) |
877 | +msgid "Getting Started" |
878 | +msgstr "" |
879 | + |
880 | +#: labyrinth.xml:169(title) |
881 | +msgid "To Start <application>Labyrinth</application>" |
882 | +msgstr "<application>Labyrinth</application> starten" |
883 | + |
884 | +#: labyrinth.xml:170(para) |
885 | +msgid "You can start <application><application>Labyrinth</application></application> in the following ways:" |
886 | +msgstr "" |
887 | + |
888 | +#: labyrinth.xml:174(term) |
889 | +msgid "<guimenu>Applications</guimenu> menu" |
890 | +msgstr "" |
891 | + |
892 | +#: labyrinth.xml:176(para) |
893 | +msgid "Choose <menuchoice><guisubmenu>Office</guisubmenu><guimenuitem>Labyrinth</guimenuitem></menuchoice>." |
894 | +msgstr "" |
895 | + |
896 | +#: labyrinth.xml:184(term) |
897 | +msgid "Command line" |
898 | +msgstr "" |
899 | + |
900 | +#: labyrinth.xml:186(para) |
901 | +msgid "To start <application><application>Labyrinth</application></application> from a command line, type the following command, then press <keycap>Return</keycap>:" |
902 | +msgstr "" |
903 | + |
904 | +#: labyrinth.xml:189(command) |
905 | +msgid "labyrinth" |
906 | +msgstr "" |
907 | + |
908 | +#: labyrinth.xml:197(title) |
909 | +msgid "When You Start <application>Labyrinth</application>" |
910 | +msgstr "" |
911 | + |
912 | +#: labyrinth.xml:198(para) |
913 | +msgid "When you start <application><application>Labyrinth</application></application>, the following window is displayed." |
914 | +msgstr "" |
915 | + |
916 | +#: labyrinth.xml:202(title) |
917 | +msgid "<application>Labyrinth</application> Start Up Window" |
918 | +msgstr "" |
919 | + |
920 | +#: labyrinth.xml:215(phrase) |
921 | +msgid "Shows <application>Labyrinth</application> main window. Contains titlebar, menubar, toolbar, display area, and scrollbars. Menubar contains File, View, Settings, and Help menus." |
922 | +msgstr "" |
923 | + |
924 | +#. for example, the items on the menubar and on the toolbar. This section is optional. |
925 | +#: labyrinth.xml:226(para) |
926 | +msgid "The <application>Labyrinth</application> browser window contains the following elements:" |
927 | +msgstr "" |
928 | + |
929 | +#: labyrinth.xml:230(term) |
930 | +msgid "Menubar." |
931 | +msgstr "" |
932 | + |
933 | +#: labyrinth.xml:232(para) |
934 | +msgid "The menus on the menubar contain all of the commands you need to work with files in <application><application>Labyrinth</application></application>." |
935 | +msgstr "" |
936 | + |
937 | +#: labyrinth.xml:237(term) |
938 | +msgid "Toolbar." |
939 | +msgstr "" |
940 | + |
941 | +#: labyrinth.xml:239(para) |
942 | +msgid "The toolbar contains a subset of the commands that you can access from the menubar." |
943 | +msgstr "" |
944 | + |
945 | +#: labyrinth.xml:249(title) |
946 | +msgid "Usage" |
947 | +msgstr "" |
948 | + |
949 | +#: labyrinth.xml:250(para) |
950 | +msgid "You can use the <application>Labyrinth</application> application to perform the following tasks: <placeholder-1/>" |
951 | +msgstr "" |
952 | + |
953 | +#: labyrinth.xml:272(title) |
954 | +msgid "To Open an Image" |
955 | +msgstr "" |
956 | + |
957 | +#: labyrinth.xml:273(para) |
958 | +msgid "To open an image, choose <menuchoice><guimenu>File</guimenu><guimenuitem>Open Image</guimenuitem></menuchoice>. The <guilabel>Open Image</guilabel> dialog is displayed. Select the file that you want to open, then click <guibutton>OK</guibutton>. To open another image in a new window, choose <menuchoice><guimenu>File</guimenu><guimenuitem>Create New Window</guimenuitem></menuchoice>. Choose <menuchoice><guimenu>File</guimenu><guimenuitem>Open Image</guimenuitem></menuchoice> to select the file that you want to open. You can also drag an image from another application such as a file manager to the <application>Labyrinth</application> window. If the <application>Labyrinth</application> window is empty, the application displays the image in the window. If the window is not empty, the application starts a new window to display the file. The application displays the name of the image file and the size of the image in pixels in the titlebar of the window." |
959 | +msgstr "" |
960 | + |
961 | +#: labyrinth.xml:297(para) |
962 | +msgid "If you try to open an image file format that <application>Labyrinth</application> does not recognize, the application displays an error message." |
963 | +msgstr "" |
964 | + |
965 | +#: labyrinth.xml:303(para) |
966 | +msgid "This is a caution." |
967 | +msgstr "" |
968 | + |
969 | +#: labyrinth.xml:308(title) |
970 | +msgid "To Manipulate the View of an Image" |
971 | +msgstr "" |
972 | + |
973 | +#: labyrinth.xml:309(para) |
974 | +msgid "You can use the following methods to resize the view of an image in the <application>Labyrinth</application> window:" |
975 | +msgstr "" |
976 | + |
977 | +#: labyrinth.xml:313(para) |
978 | +msgid "To enlarge the view of an image, choose <menuchoice><guimenu>View</guimenu><guimenuitem>Zoom In</guimenuitem></menuchoice>." |
979 | +msgstr "" |
980 | + |
981 | +#: labyrinth.xml:320(para) |
982 | +msgid "To shrink the view of an image, choose <menuchoice><guimenu>View</guimenu><guimenuitem>Zoom Out</guimenuitem></menuchoice>." |
983 | +msgstr "" |
984 | + |
985 | +#: labyrinth.xml:327(para) |
986 | +msgid "To view the image at its actual size, choose <menuchoice><guimenu>View</guimenu><guimenuitem>Zoom 1:1</guimenuitem></menuchoice>." |
987 | +msgstr "" |
988 | + |
989 | +#: labyrinth.xml:334(para) |
990 | +msgid "To enlarge or shrink the view of an image so that the image fits the <application>Labyrinth</application> window, choose <menuchoice><guimenu>View</guimenu><guimenuitem>Fit to Window</guimenuitem></menuchoice>." |
991 | +msgstr "" |
992 | + |
993 | +#: labyrinth.xml:342(para) |
994 | +msgid "To enlarge or shrink the image to a specific zoom factor, choose <menuchoice><guimenu>View</guimenu><guimenuitem>Zoom factor</guimenuitem></menuchoice>, then choose the appropriate zoom factor from the drop-down list." |
995 | +msgstr "" |
996 | + |
997 | +#: labyrinth.xml:351(para) |
998 | +msgid "To display the image in full screen mode, choose <menuchoice><guimenu>View</guimenu><guimenuitem>Full Screen</guimenuitem></menuchoice>. Full screen mode displays the image in a window that fills the full screen. The window does not contain a window frame, titlebar, menubar, or toolbar. To exit from this mode, press the <keycap>Esc</keycap> key or <keycombo><keycap>Ctrl</keycap><keycap>W</keycap></keycombo>." |
999 | +msgstr "" |
1000 | + |
1001 | +#: labyrinth.xml:368(para) |
1002 | +msgid "This is a tip." |
1003 | +msgstr "" |
1004 | + |
1005 | +#: labyrinth.xml:373(title) |
1006 | +msgid "To Scroll an Image" |
1007 | +msgstr "" |
1008 | + |
1009 | +#: labyrinth.xml:374(para) |
1010 | +msgid "To scroll around an image that is larger than the image window or full screen window, you can use the following methods:" |
1011 | +msgstr "" |
1012 | + |
1013 | +#: labyrinth.xml:378(para) |
1014 | +msgid "Use the arrow keys on the keyboard." |
1015 | +msgstr "" |
1016 | + |
1017 | +#: labyrinth.xml:381(para) |
1018 | +msgid "Drag the image in the opposite direction to the direction in which you want to scroll. For example, if you want to scroll down the image, drag the image upwards in the window." |
1019 | +msgstr "" |
1020 | + |
1021 | +#: labyrinth.xml:387(para) |
1022 | +msgid "Use the scrollbars on the window." |
1023 | +msgstr "" |
1024 | + |
1025 | +#: labyrinth.xml:393(title) |
1026 | +msgid "To Close an Image" |
1027 | +msgstr "" |
1028 | + |
1029 | +#: labyrinth.xml:394(para) |
1030 | +msgid "To close an image, choose <menuchoice><guimenu>File</guimenu><guimenuitem>Close This Window</guimenuitem></menuchoice>. If the window is the last <application>Labyrinth</application> window open, the application exits." |
1031 | +msgstr "" |
1032 | + |
1033 | +#: labyrinth.xml:400(para) |
1034 | +msgid "To quit <application>Labyrinth</application> and close all of the windows that you opened in the current session, choose <menuchoice><guimenu>File</guimenu><guimenuitem>Exit</guimenuitem></menuchoice>." |
1035 | +msgstr "" |
1036 | + |
1037 | +#: labyrinth.xml:407(para) |
1038 | +msgid "This is a warning." |
1039 | +msgstr "" |
1040 | + |
1041 | +#: labyrinth.xml:424(title) |
1042 | +msgid "About <application>Labyrinth</application>" |
1043 | +msgstr "" |
1044 | + |
1045 | +#: labyrinth.xml:425(para) |
1046 | +msgid "<application>Labyrinth</application> was written by Don Scorgio, Martin Schaaf and Matthias Vogelgesang. (<email>hacker@gnome.org</email>). To find more information about <application>Labyrinth</application>, please visit the <ulink url=\"http://www.gnome.org/~dscorgie/labyrinth.html\" type=\"http\">Labyrinth Web page</ulink>." |
1047 | +msgstr "" |
1048 | + |
1049 | +#: labyrinth.xml:430(para) |
1050 | +msgid "To report a bug or make a suggestion regarding this application or this manual, follow the directions in this <ulink url=\"ghelp:gnome-feedback\" type=\"help\">document</ulink>." |
1051 | +msgstr "" |
1052 | + |
1053 | +#: labyrinth.xml:436(para) |
1054 | +msgid "This program is distributed under the terms of the GNU General Public license as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. A copy of this license can be found at this <ulink url=\"ghelp:gpl\" type=\"help\">link</ulink>, or in the file COPYING included with the source code of this program." |
1055 | +msgstr "" |
1056 | + |
1057 | +#. Put one translator per line, in the form of NAME <EMAIL>, YEAR1, YEAR2. |
1058 | +#: labyrinth.xml:0(None) |
1059 | +msgid "translator-credits" |
1060 | +msgstr "" |
1061 | + |
1062 | |
1063 | === added file 'install_data_files.sh' |
1064 | --- install_data_files.sh 1970-01-01 00:00:00 +0000 |
1065 | +++ install_data_files.sh 2012-11-11 04:19:21 +0000 |
1066 | @@ -0,0 +1,18 @@ |
1067 | +# Run this to install Labyrinth's optional data files - language packs, icons, |
1068 | +# and the .desktop file. |
1069 | +# |
1070 | +# Set the $DESTDIR environment variable to install somewhere other than root. |
1071 | +set -e |
1072 | + |
1073 | +echo "Installing icons" |
1074 | +for size in 16x16 22x22 24x24 scalable; do install -d $DESTDIR/usr/share/icons/hicolor/$size/apps; done |
1075 | +install -m 644 data/labyrinth-16.png $DESTDIR/usr/share/icons/hicolor/16x16/apps/labyrinth.png |
1076 | +install -m 644 data/labyrinth-22.png $DESTDIR/usr/share/icons/hicolor/22x22/apps/labyrinth.png |
1077 | +install -m 644 data/labyrinth-24.png $DESTDIR/usr/share/icons/hicolor/24x24/apps/labyrinth.png |
1078 | +install -m 644 data/labyrinth.svg $DESTDIR/usr/share/icons/hicolor/scalable/apps/labyrinth.svg |
1079 | + |
1080 | +echo "Installing .desktop file" |
1081 | +install -D -m 755 data/labyrinth.desktop $DESTDIR/usr/share/applications/labyrinth.desktop |
1082 | + |
1083 | +echo "Installing translations" |
1084 | +make -C po localedir=$DESTDIR/usr/share/locale install |
1085 | |
1086 | === renamed file 'install_data_files.sh' => 'install_data_files.sh.moved' |
1087 | === added file 'labyrinth' |
1088 | --- labyrinth 1970-01-01 00:00:00 +0000 |
1089 | +++ labyrinth 2012-11-11 04:19:21 +0000 |
1090 | @@ -0,0 +1,93 @@ |
1091 | +#!/usr/bin/python |
1092 | +# labyrinth |
1093 | +# This file is part of Labyrinth |
1094 | +# |
1095 | +# Copyright (C) 2006 - Don Scorgie <Don@Scorgie.org> |
1096 | +# Copyright (C) 2008 - Labyrinth-Dev-Team |
1097 | +# |
1098 | +# Labyrinth is free software; you can redistribute it and/or modify |
1099 | +# it under the terms of the GNU General Public License as published by |
1100 | +# the Free Software Foundation; either version 2 of the License, or |
1101 | +# (at your option) any later version. |
1102 | +# |
1103 | +# Labyrinth is distributed in the hope that it will be useful, |
1104 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1105 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1106 | +# GNU General Public License for more details. |
1107 | +# |
1108 | +# You should have received a copy of the GNU General Public License |
1109 | +# along with Labyrinth; if not, write to the Free Software |
1110 | +# Foundation, Inc., 51 Franklin St, Fifth Floor, |
1111 | +# Boston, MA 02110-1301 USA |
1112 | +# |
1113 | + |
1114 | +import pygtk |
1115 | +import gettext, locale |
1116 | +import optparse |
1117 | +import sys, os |
1118 | +import os.path as osp |
1119 | + |
1120 | +if os.name != 'nt': |
1121 | + pygtk.require('2.0') |
1122 | + |
1123 | +import gtk |
1124 | + |
1125 | +from labyrinth_lib import utils |
1126 | +from labyrinth_lib import Browser |
1127 | +try: |
1128 | + from labyrinth_lib import defs |
1129 | + localedir = osp.abspath(osp.join(defs.DATA_DIR, "locale")) |
1130 | +except: |
1131 | + localedir = "/usr/share/locale" |
1132 | + |
1133 | +gettext.bindtextdomain('labyrinth', localedir) |
1134 | +if hasattr(gettext, 'bind_textdomain_codeset'): |
1135 | + gettext.bind_textdomain_codeset('labyrinth','UTF-8') |
1136 | + |
1137 | +gettext.textdomain('labyrinth') |
1138 | + |
1139 | +if hasattr(locale, 'bindtextdomain'): |
1140 | + if not os.name == 'nt': |
1141 | + locale.bindtextdomain('labyrinth', localedir) |
1142 | + if hasattr(locale, 'bind_textdomain_codeset'): |
1143 | + locale.bind_textdomain_codeset('labyrinth','UTF-8') |
1144 | + locale.textdomain('labyrinth') |
1145 | + |
1146 | + |
1147 | +gtk.glade.bindtextdomain('labyrinth') |
1148 | +gtk.glade.textdomain('labyrinth') |
1149 | + |
1150 | + |
1151 | +def main(): |
1152 | + parser = optparse.OptionParser() |
1153 | + parser.add_option("--use-tray-icon", dest="tray_icon", |
1154 | + action="store_true", default=False) |
1155 | + parser.add_option("--no-tray-icon", dest="tray_icon", action="store_false") |
1156 | + parser.add_option("--hide-main-window", action="store_true", default=False) |
1157 | + parser.add_option("-m", "--map", action="store", type="string", dest="filename", |
1158 | + help="Open a map from a given filename (from internal database)") |
1159 | + parser.add_option("-o", "--open", action="store", type="string", |
1160 | + dest="filepath", help="Open a map from a given filename (including path)") |
1161 | + |
1162 | + (options, args) = parser.parse_args() |
1163 | + if not options.tray_icon: |
1164 | + options.hide_main_window=False |
1165 | + |
1166 | + MapBrowser = Browser.Browser ( |
1167 | + start_hidden = options.hide_main_window, |
1168 | + tray_icon = options.tray_icon |
1169 | + ) |
1170 | + |
1171 | + if options.filename != None: |
1172 | + MapBrowser.open_map_filename (os.path.join (utils.get_save_dir(), options.filename)) |
1173 | + elif options.filepath != None: |
1174 | + MapBrowser.open_map_filename (options.filepath) |
1175 | + |
1176 | + try: |
1177 | + gtk.main() |
1178 | + except: |
1179 | + print "Exception caught while running. Dying a death." |
1180 | + sys.exit(1) |
1181 | + |
1182 | +if __name__ == '__main__': |
1183 | + main() |
1184 | |
1185 | === renamed file 'labyrinth' => 'labyrinth.moved' |
1186 | === added directory 'labyrinth_lib' |
1187 | === renamed directory 'labyrinth_lib' => 'labyrinth_lib.moved' |
1188 | === added file 'labyrinth_lib/BaseThought.py' |
1189 | --- labyrinth_lib/BaseThought.py 1970-01-01 00:00:00 +0000 |
1190 | +++ labyrinth_lib/BaseThought.py 2012-11-11 04:19:21 +0000 |
1191 | @@ -0,0 +1,341 @@ |
1192 | +# BaseThought.py |
1193 | +# This file is part of Labyrinth |
1194 | +# |
1195 | +# Copyright (C) 2006 - Don Scorgie <DonScorgie@Blueyonder.co.uk> |
1196 | +# |
1197 | +# Labyrinth is free software; you can redistribute it and/or modify |
1198 | +# it under the terms of the GNU General Public License as published by |
1199 | +# the Free Software Foundation; either version 2 of the License, or |
1200 | +# (at your option) any later version. |
1201 | +# |
1202 | +# Labyrinth is distributed in the hope that it will be useful, |
1203 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1204 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1205 | +# GNU General Public License for more details. |
1206 | +# |
1207 | +# You should have received a copy of the GNU General Public License |
1208 | +# along with Labyrinth; if not, write to the Free Software |
1209 | +# Foundation, Inc., 51 Franklin St, Fifth Floor, |
1210 | +# Boston, MA 02110-1301 USA |
1211 | +# |
1212 | + |
1213 | +import gobject |
1214 | +import gtk |
1215 | +import utils |
1216 | +import pango |
1217 | + |
1218 | +import TextBufferMarkup |
1219 | + |
1220 | +MODE_EDITING = 0 |
1221 | +MODE_IMAGE = 1 |
1222 | +MODE_DRAW = 2 |
1223 | + |
1224 | +class BaseThought (gobject.GObject): |
1225 | + ''' The basic class to derive other thoughts from. \ |
1226 | + Instructions for creating derivative thought types are \ |
1227 | + given as comments''' |
1228 | + # These are general signals. They are available to all thoughts to |
1229 | + # emit. If you emit other signals, the chances are they'll be ignored |
1230 | + # by the MMapArea. It's you're responsiblity to catch and handle them. |
1231 | + # All these signals are handled correctly by the MMapArea. |
1232 | + __gsignals__ = dict (select_thought = (gobject.SIGNAL_RUN_FIRST, |
1233 | + gobject.TYPE_NONE, |
1234 | + (gobject.TYPE_PYOBJECT,)), |
1235 | + begin_editing = (gobject.SIGNAL_RUN_FIRST, |
1236 | + gobject.TYPE_NONE, |
1237 | + ()), |
1238 | + popup_requested = (gobject.SIGNAL_RUN_FIRST, |
1239 | + gobject.TYPE_NONE, |
1240 | + (gobject.TYPE_PYOBJECT, gobject.TYPE_INT)), |
1241 | + claim_unending_link = (gobject.SIGNAL_RUN_FIRST, |
1242 | + gobject.TYPE_NONE, |
1243 | + ()), |
1244 | + update_view = (gobject.SIGNAL_RUN_LAST, |
1245 | + gobject.TYPE_NONE, |
1246 | + ()), |
1247 | + create_link = (gobject.SIGNAL_RUN_FIRST, |
1248 | + gobject.TYPE_NONE, |
1249 | + (gobject.TYPE_PYOBJECT,)), |
1250 | + title_changed = (gobject.SIGNAL_RUN_LAST, |
1251 | + gobject.TYPE_NONE, |
1252 | + (gobject.TYPE_STRING,)), |
1253 | + finish_editing = (gobject.SIGNAL_RUN_FIRST, |
1254 | + gobject.TYPE_NONE, |
1255 | + ()), |
1256 | + delete_thought = (gobject.SIGNAL_RUN_LAST, |
1257 | + gobject.TYPE_NONE, |
1258 | + ()), |
1259 | + text_selection_changed = (gobject.SIGNAL_RUN_LAST, |
1260 | + gobject.TYPE_NONE, |
1261 | + (gobject.TYPE_INT, gobject.TYPE_INT, gobject.TYPE_STRING)), |
1262 | + change_mouse_cursor = (gobject.SIGNAL_RUN_FIRST, |
1263 | + gobject.TYPE_NONE, |
1264 | + (gobject.TYPE_INT,)), |
1265 | + update_links = (gobject.SIGNAL_RUN_LAST, |
1266 | + gobject.TYPE_NONE, |
1267 | + ()), |
1268 | + grab_focus = (gobject.SIGNAL_RUN_FIRST, |
1269 | + gobject.TYPE_NONE, |
1270 | + (gobject.TYPE_BOOLEAN,)), |
1271 | + update_attrs = (gobject.SIGNAL_RUN_FIRST, |
1272 | + gobject.TYPE_NONE, |
1273 | + (gobject.TYPE_BOOLEAN, gobject.TYPE_BOOLEAN, gobject.TYPE_BOOLEAN, pango.FontDescription))) |
1274 | + |
1275 | + # The first thing that should be called is this constructor |
1276 | + # It sets some basic properties of all thoughts and should be called |
1277 | + # before you start doing you're own thing with thoughts |
1278 | + # save: the save document passed into the derived constructor |
1279 | + # elem_type: a string representing the thought type (e.g. "image_thought") |
1280 | + def __init__ (self, save, elem_type, undo, background_color, foreground_color): |
1281 | + # Note: Once the thought has been successfully initialised (i.e. at the end |
1282 | + # of the constructor) you MUST set all_okay to True |
1283 | + # Otherwise, bad things will happen. |
1284 | + self.all_okay = False |
1285 | + super (BaseThought, self).__init__() |
1286 | + self.ul = self.lr = None |
1287 | + self.am_primary = False |
1288 | + self.am_selected = False |
1289 | + self.sensitive = 5 |
1290 | + self.editing = False |
1291 | + self.identity = -1 |
1292 | + self.index = 0 |
1293 | + self.end_index = 0 |
1294 | + self.text = "" |
1295 | + self.want_move = False |
1296 | + self.undo = undo |
1297 | + self.background_color = background_color |
1298 | + self.foreground_color = foreground_color |
1299 | + self.model_iter = None |
1300 | + extended_elem = save.createElement ("Extended") |
1301 | + self.extended_buffer = TextBufferMarkup.ExtendedBuffer (self.undo, extended_elem, save) |
1302 | + self.extended_buffer.set_text("") |
1303 | + self.extended_buffer.connect ("set_focus", self.focus_buffer) |
1304 | + self.extended_buffer.connect ("set_attrs", self.set_extended_attrs) |
1305 | + self.element = save.createElement (elem_type) |
1306 | + self.element.appendChild (extended_elem) |
1307 | + |
1308 | + # These are self-explanitory. You probably don't want to |
1309 | + # overwrite these methods, unless you have a very good reason |
1310 | + def get_save_element (self): |
1311 | + return self.element |
1312 | + |
1313 | + def make_primary (self): |
1314 | + self.am_primary = True |
1315 | + |
1316 | + def select (self): |
1317 | + self.am_selected = True |
1318 | + |
1319 | + def unselect (self): |
1320 | + self.am_selected = False |
1321 | + |
1322 | + def get_max_area (self): |
1323 | + if not self.ul or not self.lr: |
1324 | + return 999,999,-999,-999 |
1325 | + return self.ul[0], self.ul[1], self.lr[0], self.lr[1] |
1326 | + |
1327 | + def okay (self): |
1328 | + return self.all_okay |
1329 | + |
1330 | + def move_by (self, x, y): |
1331 | + self.ul = (self.ul[0]+x, self.ul[1]+y) |
1332 | + self.recalc_position () |
1333 | + self.emit ("update_links") |
1334 | + self.emit ("update_view") |
1335 | + |
1336 | + def focus_buffer (self, buf): |
1337 | + self.emit ("select_thought", None) |
1338 | + self.emit ("grab_focus", True) |
1339 | + |
1340 | + def set_extended_attrs(self, buf, bold, underline, italics, pango_font): |
1341 | + self.emit("update_attrs", bold, underline, italics, pango_font) |
1342 | + |
1343 | + def can_be_parent (self): |
1344 | + return True |
1345 | + |
1346 | + # This, you may want to change. Though, doing so will only affect |
1347 | + # thoughts that are "parents" |
1348 | + def find_connection (self, other): |
1349 | + if self.editing or other.editing: |
1350 | + return None, None |
1351 | + if not self.ul or not self.lr or not other.ul \ |
1352 | + or not other.lr: |
1353 | + return None, None |
1354 | + |
1355 | + if utils.use_bezier_curves: |
1356 | + if other.ul[0] > self.lr[0]: |
1357 | + xfrom = self.lr[0] |
1358 | + xto = other.ul[0] |
1359 | + else: |
1360 | + xfrom = self.ul[0] |
1361 | + xto = other.lr[0] |
1362 | + else: |
1363 | + xfrom = self.ul[0]-((self.ul[0]-self.lr[0]) / 2.) |
1364 | + xto = other.ul[0]-((other.ul[0]-other.lr[0]) / 2.) |
1365 | + |
1366 | + yfrom = self.ul[1]-((self.ul[1]-self.lr[1]) / 2.) |
1367 | + yto = other.ul[1]-((other.ul[1]-other.lr[1]) / 2.) |
1368 | + return (xfrom, yfrom), (xto, yto) |
1369 | + |
1370 | + # All the rest of these should be handled within your thought |
1371 | + # type, supposing you actually want to handle them. |
1372 | + # You almost certianly do want to ;) |
1373 | + def process_button_down (self, event, mode, transformed): |
1374 | + return False |
1375 | + |
1376 | + def process_button_release (self, event, unending_link, mode, transformed): |
1377 | + return False |
1378 | + |
1379 | + def process_key_press (self, event, mode): |
1380 | + return False |
1381 | + |
1382 | + def handle_motion (self, event, mode, transformed): |
1383 | + pass |
1384 | + |
1385 | + def includes (self, coords, mode): |
1386 | + pass |
1387 | + |
1388 | + def begin_editing (self): |
1389 | + return False |
1390 | + |
1391 | + def finish_editing (self): |
1392 | + pass |
1393 | + |
1394 | + def draw (self, context): |
1395 | + pass |
1396 | + |
1397 | + def load (self, node): |
1398 | + pass |
1399 | + |
1400 | + def update_save (self): |
1401 | + pass |
1402 | + |
1403 | + def copy_text (self, clip): |
1404 | + pass |
1405 | + |
1406 | + def cut_text (self, clip): |
1407 | + pass |
1408 | + |
1409 | + def paste_text (self, clip): |
1410 | + pass |
1411 | + |
1412 | + def export (self, context, move_x, move_y): |
1413 | + pass |
1414 | + |
1415 | + def commit_text (self, im_context, string, mode): |
1416 | + pass |
1417 | + |
1418 | + def want_motion (self): |
1419 | + return False |
1420 | + |
1421 | + def recalc_edges (self): |
1422 | + pass |
1423 | + |
1424 | + def recalc_position (self): |
1425 | + pass |
1426 | + |
1427 | + def delete_surroundings(self, imcontext, offset, n_chars, mode): |
1428 | + pass |
1429 | + |
1430 | + def preedit_changed (self, imcontext, mode): |
1431 | + pass |
1432 | + |
1433 | + def preedit_end (self, imcontext, mode): |
1434 | + pass |
1435 | + |
1436 | + def preedit_start (self, imcontext, mode): |
1437 | + pass |
1438 | + |
1439 | + def retrieve_surroundings (self, imcontext, mode): |
1440 | + pass |
1441 | + |
1442 | + def set_bold (self, active): |
1443 | + pass |
1444 | + |
1445 | + def get_popup_menu_items(self): |
1446 | + pass |
1447 | + |
1448 | + |
1449 | +class ResizableThought (BaseThought): |
1450 | + ''' A resizable thought base class. This allows the sides and corners \ |
1451 | + of the thought to be dragged around. It only provides the very basic \ |
1452 | + functionality. Other stuff must be done within the derived classes''' |
1453 | + |
1454 | + # Possible types of resizing - where the user selected to resize |
1455 | + RESIZE_NONE = 0 |
1456 | + RESIZE_LEFT = 1 |
1457 | + RESIZE_RIGHT = 2 |
1458 | + RESIZE_TOP = 3 |
1459 | + RESIZE_BOTTOM = 4 |
1460 | + RESIZE_UL = 5 |
1461 | + RESIZE_UR = 6 |
1462 | + RESIZE_LL = 7 |
1463 | + RESIZE_LR = 8 |
1464 | + |
1465 | + def __init__ (self, save, elem_type, undo, background_color, foreground_color): |
1466 | + super (ResizableThought, self).__init__(save, elem_type, undo, background_color, foreground_color) |
1467 | + self.resizing = False |
1468 | + self.button_down = False |
1469 | + |
1470 | + def includes (self, coords, mode): |
1471 | + if not self.ul or not self.lr or not coords: |
1472 | + return False |
1473 | + |
1474 | + inside = (coords[0] < self.lr[0] + self.sensitive) and \ |
1475 | + (coords[0] > self.ul[0] - self.sensitive) and \ |
1476 | + (coords[1] < self.lr[1] + self.sensitive) and \ |
1477 | + (coords[1] > self.ul[1] - self.sensitive) |
1478 | + |
1479 | + self.resizing = self.RESIZE_NONE |
1480 | + self.motion_coords = coords |
1481 | + |
1482 | + if inside and (mode != MODE_EDITING or self.button_down): |
1483 | + self.emit ("change_mouse_cursor", gtk.gdk.LEFT_PTR) |
1484 | + return inside |
1485 | + |
1486 | + if inside: |
1487 | + # 2 cases: 1. The click was within the main area |
1488 | + # 2. The click was near the border |
1489 | + # In the first case, we handle as normal |
1490 | + # In the second case, we want to intercept all the fun thats |
1491 | + # going to happen so we can resize the thought |
1492 | + if abs (coords[0] - self.ul[0]) < self.sensitive: |
1493 | + # its near the top edge somewhere |
1494 | + if abs (coords[1] - self.ul[1]) < self.sensitive: |
1495 | + # Its in the ul corner |
1496 | + self.resizing = self.RESIZE_UL |
1497 | + self.emit ("change_mouse_cursor", gtk.gdk.TOP_LEFT_CORNER) |
1498 | + elif abs (coords[1] - self.lr[1]) < self.sensitive: |
1499 | + # Its in the ll corner |
1500 | + self.resizing = self.RESIZE_LL |
1501 | + self.emit ("change_mouse_cursor", gtk.gdk.BOTTOM_LEFT_CORNER) |
1502 | + elif coords[1] < self.lr[1] and coords[1] > self.ul[1]: |
1503 | + #anywhere else along the left edge |
1504 | + self.resizing = self.RESIZE_LEFT |
1505 | + self.emit ("change_mouse_cursor", gtk.gdk.LEFT_SIDE) |
1506 | + elif abs (coords[0] - self.lr[0]) < self.sensitive: |
1507 | + if abs (coords[1] - self.ul[1]) < self.sensitive: |
1508 | + # Its in the UR corner |
1509 | + self.resizing = self.RESIZE_UR |
1510 | + self.emit ("change_mouse_cursor", gtk.gdk.TOP_RIGHT_CORNER) |
1511 | + elif abs (coords[1] - self.lr[1]) < self.sensitive: |
1512 | + # Its in the lr corner |
1513 | + self.resizing = self.RESIZE_LR |
1514 | + self.emit ("change_mouse_cursor", gtk.gdk.BOTTOM_RIGHT_CORNER) |
1515 | + elif coords[1] < self.lr[1] and coords[1] > self.ul[1]: |
1516 | + #anywhere else along the right edge |
1517 | + self.resizing = self.RESIZE_RIGHT |
1518 | + self.emit ("change_mouse_cursor", gtk.gdk.RIGHT_SIDE) |
1519 | + elif abs (coords[1] - self.ul[1]) < self.sensitive and \ |
1520 | + (coords[0] < self.lr[0] and coords[0] > self.ul[0]): |
1521 | + # Along the top edge somewhere |
1522 | + self.resizing = self.RESIZE_TOP |
1523 | + self.emit ("change_mouse_cursor", gtk.gdk.TOP_SIDE) |
1524 | + elif abs (coords[1] - self.lr[1]) < self.sensitive and \ |
1525 | + (coords[0] < self.lr[0] and coords[0] > self.ul[0]): |
1526 | + # Along the bottom edge somewhere |
1527 | + self.resizing = self.RESIZE_BOTTOM |
1528 | + self.emit ("change_mouse_cursor", gtk.gdk.BOTTOM_SIDE) |
1529 | + else: |
1530 | + self.emit ("change_mouse_cursor", gtk.gdk.LEFT_PTR) |
1531 | + self.want_move = (self.resizing != self.RESIZE_NONE) |
1532 | + return inside |
1533 | |
1534 | === added file 'labyrinth_lib/Browser.py' |
1535 | --- labyrinth_lib/Browser.py 1970-01-01 00:00:00 +0000 |
1536 | +++ labyrinth_lib/Browser.py 2012-11-11 04:19:21 +0000 |
1537 | @@ -0,0 +1,340 @@ |
1538 | +# Browser.py |
1539 | +# This file is part of Labyrinth |
1540 | +# |
1541 | +# Copyright (C) 2006 - Don Scorgie <Don@Scorgie.org> |
1542 | +# - Andreas Sliwka <andreas.sliwka@gmail.com> |
1543 | +# |
1544 | +# Labyrinth is free software; you can redistribute it and/or modify |
1545 | +# it under the terms of the GNU General Public License as published by |
1546 | +# the Free Software Foundation; either version 2 of the License, or |
1547 | +# (at your option) any later version. |
1548 | +# |
1549 | +# Labyrinth is distributed in the hope that it will be useful, |
1550 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1551 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1552 | +# GNU General Public License for more details. |
1553 | +# |
1554 | +# You should have received a copy of the GNU General Public License |
1555 | +# along with Labyrinth; if not, write to the Free Software |
1556 | +# Foundation, Inc., 51 Franklin St, Fifth Floor, |
1557 | +# Boston, MA 02110-1301 USA |
1558 | +# |
1559 | + |
1560 | +# Standard library |
1561 | +import tarfile |
1562 | +import os |
1563 | +import gettext |
1564 | +_ = gettext.gettext |
1565 | + |
1566 | +# Gtk stuff |
1567 | +import gtk |
1568 | +if os.name != 'nt': |
1569 | + import gconf |
1570 | +import gtk.glade |
1571 | +import pango |
1572 | +import gobject |
1573 | + |
1574 | +# Local imports |
1575 | +import utils |
1576 | +import MainWindow |
1577 | +from MapList import MapList |
1578 | +import TrayIcon |
1579 | +from . import __version__ |
1580 | + |
1581 | +AUTHORS = ['Don Scorgie <Don@Scorgie.org>', |
1582 | + 'Martin Schaaf <mascha@ma-scha.de>', |
1583 | + 'Matthias Vogelgesang <matthias.vogelgesang@gmail.com>', |
1584 | + 'Andreas Sliwka <andreas.sliwka@gmail.com>'] |
1585 | + |
1586 | +class Browser (gtk.Window): |
1587 | + COL_ID = 0 |
1588 | + COL_TITLE = 1 |
1589 | + COL_MODTIME = 2 |
1590 | + |
1591 | + def __init__(self, start_hidden, tray_icon): |
1592 | + super(Browser, self).__init__() |
1593 | + self.glade=gtk.glade.XML(utils.get_data_file_name('labyrinth.glade')) |
1594 | + self.view = self.glade.get_widget ('MainView') |
1595 | + self.populate_view () |
1596 | + self.view.connect ('row-activated', self.open_row_cb) |
1597 | + self.view.connect ('cursor-changed', self.cursor_change_cb) |
1598 | + |
1599 | + self.view_dependants = [] |
1600 | + |
1601 | + self.open_button = self.glade.get_widget('OpenButton') |
1602 | + self.delete_button = self.glade.get_widget('DeleteButton') |
1603 | + self.open_menu = self.glade.get_widget('open1') |
1604 | + self.delete_menu = self.glade.get_widget('delete1') |
1605 | + |
1606 | + self.view_dependants.append (self.open_button) |
1607 | + self.view_dependants.append (self.delete_button) |
1608 | + self.view_dependants.append (self.open_menu) |
1609 | + self.view_dependants.append (self.delete_menu) |
1610 | + |
1611 | + self.open_button.connect ('clicked', self.open_clicked) |
1612 | + self.glade.get_widget('NewButton').connect ('clicked', self.new_clicked) |
1613 | + self.delete_button.connect ('clicked', self.delete_clicked) |
1614 | + |
1615 | + self.open_menu.connect ('activate', self.open_clicked) |
1616 | + self.glade.get_widget('new1').connect ('activate', self.new_clicked) |
1617 | + self.delete_menu.connect ('activate', self.delete_clicked) |
1618 | + self.glade.get_widget('import1').connect ('activate', self.import_clicked) |
1619 | + self.glade.get_widget('quit1').connect ('activate', self.quit_clicked) |
1620 | + self.glade.get_widget('about1').connect ('activate', self.about_clicked) |
1621 | + self.glade.get_widget('showhelp').connect ('activate', self.show_help_clicked) |
1622 | + |
1623 | + for x in self.view_dependants: |
1624 | + x.set_sensitive(False) |
1625 | + |
1626 | + self.main_window = self.glade.get_widget ('MapBrowser') |
1627 | + |
1628 | + # set remembered size |
1629 | + if os.name != 'nt': |
1630 | + self.config_client = gconf.client_get_default() |
1631 | + self.config_client.add_dir ("/apps/labyrinth", gconf.CLIENT_PRELOAD_NONE) |
1632 | + |
1633 | + width = self.config_client.get_int ('/apps/labyrinth/width') |
1634 | + height = self.config_client.get_int ('/apps/labyrinth/height') |
1635 | + utils.use_bezier_curves = self.config_client.get_bool ('/apps/labyrinth/curves') |
1636 | + if width == 0 or height == 0: |
1637 | + width = 400 |
1638 | + height = 300 |
1639 | + else: |
1640 | + width = 400 |
1641 | + height = 300 |
1642 | + |
1643 | + view_sortable = self.view.get_model () |
1644 | + view_sortable.connect ('sort-column-changed', self.sort_column_changed_cb) |
1645 | + if os.name != 'nt': |
1646 | + sort_order = self.config_client.get_int('/apps/labyrinth/map_sort_order') |
1647 | + column_id = self.config_client.get_int('/apps/labyrinth/map_sort_order_column') |
1648 | + view_sortable.set_sort_column_id (column_id, sort_order) |
1649 | + |
1650 | + self.main_window.resize (width, height) |
1651 | + |
1652 | + if os.name != 'nt': |
1653 | + try: |
1654 | + self.main_window.set_icon_name ('labyrinth') |
1655 | + except: |
1656 | + self.main_window.set_icon_from_file(utils.get_data_file_name('labyrinth.svg')) |
1657 | + else: |
1658 | + self.main_window.set_icon_from_file('images\\labyrinth-24.png') |
1659 | + if tray_icon: |
1660 | + self.main_window.connect ('delete_event', self.toggle_main_window, None) |
1661 | + traymenu = gtk.Menu() |
1662 | + quit_item = gtk.MenuItem("Quit") |
1663 | + quit_item.connect("activate",self.quit_clicked) |
1664 | + traymenu.add(quit_item) |
1665 | + traymenu.show_all() |
1666 | + self.traymenu = traymenu |
1667 | + self.trayicon = TrayIcon.TrayIcon( |
1668 | + icon_name="labyrinth", |
1669 | + menu=traymenu, |
1670 | + activate=self.toggle_main_window) |
1671 | + else: |
1672 | + self.main_window.connect('delete_event', self.quit_clicked, None) |
1673 | + if start_hidden: |
1674 | + self.main_window.hide () |
1675 | + else: |
1676 | + self.main_window.show_all () |
1677 | + |
1678 | + def toggle_main_window(self,*args): |
1679 | + if self.main_window.get_property("visible"): |
1680 | + self.main_window.hide() |
1681 | + else: |
1682 | + self.main_window.show() |
1683 | + return True |
1684 | + |
1685 | + def map_title_cb (self, mobj, new_title, mobj1): |
1686 | + map = MapList.get_by_window(mobj) |
1687 | + if not map: |
1688 | + raise AttributeError ("What a mess, can't find the map") |
1689 | + map.title = new_title |
1690 | + |
1691 | + def get_selected_map(self): |
1692 | + sel = self.view.get_selection () |
1693 | + (model, it) = sel.get_selected () |
1694 | + if it: |
1695 | + (num,) = MapList.tree_view_model.get (it, self.COL_ID) |
1696 | + return MapList.get_by_index(num) |
1697 | + return None |
1698 | + |
1699 | + def cursor_change_cb (self, treeview): |
1700 | + sensitive = bool(self.get_selected_map()) |
1701 | + for x in self.view_dependants: |
1702 | + x.set_sensitive(sensitive) |
1703 | + |
1704 | + def open_map_filename (self, fname): |
1705 | + win = MainWindow.LabyrinthWindow (fname) |
1706 | + win.show () |
1707 | + |
1708 | + def open_map (self, map, imported=False): |
1709 | + win = MainWindow.LabyrinthWindow (map.filename, imported) |
1710 | + win.connect ("title-changed", self.map_title_cb) |
1711 | + win.connect ("window_closed", self.remove_map_cb) |
1712 | + win.connect ("file_saved", self.file_save_cb) |
1713 | + win.show () |
1714 | + map.window = win |
1715 | + return (MapList.index(map), win) |
1716 | + |
1717 | + def open_selected_map(self): |
1718 | + map = self.get_selected_map() |
1719 | + if map is None: |
1720 | + raise ValueError("you clicked the 'open' button but had no map selected") |
1721 | + if map.window: |
1722 | + print "Window for map '%s' is already open" % map.title |
1723 | + # may be the window should be raised? |
1724 | + else: |
1725 | + self.open_map (map) |
1726 | + |
1727 | + def show_help_clicked(self, arg): |
1728 | + try: |
1729 | + gtk.show_uri(None, 'help:labyrinth', 0) |
1730 | + except gobject.GError, e: |
1731 | + print _('Unable to display help: %s') % str(e) |
1732 | + |
1733 | + def about_clicked (self, arg): |
1734 | + about_dialog = gtk.AboutDialog () |
1735 | + about_dialog.set_name ("Labyrinth") |
1736 | + about_dialog.set_version (__version__) |
1737 | + if os.name != 'nt': |
1738 | + try: |
1739 | + about_dialog.set_logo_icon_name("labyrinth") |
1740 | + except: |
1741 | + pass |
1742 | + else: |
1743 | + about_dialog.set_logo (gtk.gdk.pixbuf_new_from_file("images\\labyrinth-24.png")) |
1744 | + about_dialog.set_license ( |
1745 | +"Labyrinth is free software; you can redistribute it and/or modify " |
1746 | +"it under the terms of the GNU General Public Licence as published by " |
1747 | +"the Free Software Foundation; either version 2 of the Licence, or " |
1748 | +"(at your option) any later version." |
1749 | +"\n\n" |
1750 | +"Labyrinth is distributed in the hope that it will be useful, " |
1751 | +"but WITHOUT ANY WARRANTY; without even the implied warranty of " |
1752 | +"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the " |
1753 | +"GNU General Public Licence for more details." |
1754 | +"\n\n" |
1755 | +"You should have received a copy of the GNU General Public Licence " |
1756 | +"along with Labyrinth; if not, write to the Free Software Foundation, Inc., " |
1757 | +"59 Temple Place, Suite 330, Boston, MA 02111-1307 USA") |
1758 | + about_dialog.set_wrap_license (True) |
1759 | + about_dialog.set_copyright ("2006-2008 Don Scorgie et. al") |
1760 | + about_dialog.set_authors (AUTHORS) |
1761 | + about_dialog.set_website ("http://code.google.com/p/labyrinth") |
1762 | + about_dialog.set_translator_credits (_("Translation by Don Scorgie")) |
1763 | + about_dialog.run () |
1764 | + about_dialog.hide () |
1765 | + del (about_dialog) |
1766 | + return |
1767 | + |
1768 | + def open_clicked (self, button): |
1769 | + self.open_selected_map() |
1770 | + |
1771 | + def open_row_cb (self, view, path, col): |
1772 | + self.open_selected_map () |
1773 | + |
1774 | + def new_clicked (self, button): |
1775 | + map = MapList.create_empty_map() |
1776 | + self.open_map(map) |
1777 | + |
1778 | + def delete_clicked (self, button): |
1779 | + map = self.get_selected_map () |
1780 | + if not map: |
1781 | + raise ValueError("You clicked on delete but had no map selected") |
1782 | + error_message = "" |
1783 | + if map.window: |
1784 | + error_message = _("The map cannot be deleted right now. Is it open?") |
1785 | + elif not map.filename: |
1786 | + error_message = _("The map has no associated filename.") |
1787 | + if error_message: |
1788 | + dialog = gtk.MessageDialog (self, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, gtk.BUTTONS_OK, |
1789 | + _("Cannot delete this map")) |
1790 | + dialog.format_secondary_text (error_message) |
1791 | + dialog.run () |
1792 | + dialog.hide () |
1793 | + del (dialog) |
1794 | + return |
1795 | + dialog = gtk.MessageDialog (self, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, gtk.BUTTONS_YES_NO, |
1796 | + _("Do you really want to delete this Map?")) |
1797 | + resp = dialog.run () |
1798 | + dialog.hide () |
1799 | + del (dialog) |
1800 | + if resp != gtk.RESPONSE_YES: |
1801 | + return |
1802 | + MapList.delete (map) |
1803 | + self.view.emit ('cursor-changed') |
1804 | + |
1805 | + def remove_map_cb (self, mobj, a): |
1806 | + map = MapList.get_by_window(mobj) |
1807 | + if map: |
1808 | + MapList.delete(map) |
1809 | + self.view.emit ('cursor-changed') |
1810 | + return |
1811 | + raise ValueError("Cant remove map of window %s" % mobj) |
1812 | + |
1813 | + def file_save_cb (self, mobj, new_fname, mobj1): |
1814 | + map = MapList.get_by_window(mobj) |
1815 | + if map: |
1816 | + map.window = None |
1817 | + map.filename = new_fname |
1818 | + return |
1819 | + |
1820 | + def import_clicked(self, button, other=None, *data): |
1821 | + chooser = gtk.FileChooserDialog(title=_("Open File"), action=gtk.FILE_CHOOSER_ACTION_OPEN, \ |
1822 | + buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) |
1823 | + |
1824 | + filtr = gtk.FileFilter () |
1825 | + filtr.set_name(_('MAPZ Compressed Map (*.mapz)')) |
1826 | + filtr.add_pattern('*.mapz') |
1827 | + chooser.add_filter(filtr) |
1828 | + |
1829 | + response = chooser.run() |
1830 | + if response == gtk.RESPONSE_OK: |
1831 | + filename = chooser.get_filename() |
1832 | + tf = tarfile.open(filename) |
1833 | + mapname = os.path.join (utils.get_save_dir (), tf.getnames()[0]) |
1834 | + tf.extractall(utils.get_save_dir()) |
1835 | + tf.close() |
1836 | + map = MapList.new_from_file(mapname) |
1837 | + map.filename = mapname |
1838 | + |
1839 | + chooser.destroy() |
1840 | + |
1841 | + def quit_clicked (self, button, other=None, *data): |
1842 | + for map in MapList.get_open_windows(): |
1843 | + map.window.close_window_cb (None) |
1844 | + |
1845 | + width, height = self.main_window.get_size() |
1846 | + |
1847 | + if os.name != 'nt': |
1848 | + self.config_client.set_int('/apps/labyrinth/width', width) |
1849 | + self.config_client.set_int('/apps/labyrinth/height', height) |
1850 | + |
1851 | + gtk.main_quit () |
1852 | + |
1853 | + def populate_view (self): |
1854 | + cellrenderer = gtk.CellRendererText() |
1855 | + cellrenderer.set_property("ellipsize", pango.ELLIPSIZE_END) |
1856 | + column = gtk.TreeViewColumn(_("Map Name"), cellrenderer, |
1857 | + text=self.COL_TITLE) |
1858 | + column.set_resizable(True) |
1859 | + column.set_expand (True) |
1860 | + column.set_sort_column_id (1) |
1861 | + self.view.append_column(column) |
1862 | + |
1863 | + col1 = gtk.TreeViewColumn (_("Last Modified"), gtk.CellRendererText(), |
1864 | + text=self.COL_MODTIME) |
1865 | + col1.set_resizable(True) |
1866 | + col1.set_sort_column_id (2) |
1867 | + self.view.append_column(col1) |
1868 | + |
1869 | + self.view.set_model (MapList.get_TreeViewModel()) |
1870 | + self.view.set_search_column(self.COL_TITLE) |
1871 | + self.view.set_enable_search (True) |
1872 | + |
1873 | + def sort_column_changed_cb (self, data): |
1874 | + column_id, sort_order = data.get_sort_column_id () |
1875 | + if os.name != 'nt': |
1876 | + self.config_client.set_int('/apps/labyrinth/map_sort_order', sort_order) |
1877 | + self.config_client.set_int('/apps/labyrinth/map_sort_order_column', column_id) |
1878 | |
1879 | === added file 'labyrinth_lib/DrawingThought.py' |
1880 | --- labyrinth_lib/DrawingThought.py 1970-01-01 00:00:00 +0000 |
1881 | +++ labyrinth_lib/DrawingThought.py 2012-11-11 04:19:21 +0000 |
1882 | @@ -0,0 +1,619 @@ |
1883 | +# DrawingThought.py |
1884 | +# This file is part of Labyrinth |
1885 | +# |
1886 | +# Copyright (C) 2006 - Don Scorgie <Don@Scorgieorg> |
1887 | +# |
1888 | +# Labyrinth is free software; you can redistribute it and/or modify |
1889 | +# it under the terms of the GNU General Public License as published by |
1890 | +# the Free Software Foundation; either version 2 of the License, or |
1891 | +# (at your option) any later version. |
1892 | +# |
1893 | +# Labyrinth is distributed in the hope that it will be useful, |
1894 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1895 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1896 | +# GNU General Public License for more details. |
1897 | +# |
1898 | +# You should have received a copy of the GNU General Public License |
1899 | +# along with Labyrinth; if not, write to the Free Software |
1900 | +# Foundation, Inc., 51 Franklin St, Fifth Floor, |
1901 | +# Boston, MA 02110-1301 USA |
1902 | +# |
1903 | + |
1904 | +import gtk |
1905 | +import xml.dom |
1906 | +import gettext |
1907 | +_ = gettext.gettext |
1908 | +import math |
1909 | + |
1910 | +import BaseThought |
1911 | +import utils |
1912 | +import UndoManager |
1913 | + |
1914 | +STYLE_CONTINUE=0 |
1915 | +STYLE_END=1 |
1916 | +STYLE_BEGIN=2 |
1917 | +ndraw =0 |
1918 | + |
1919 | +MODE_EDITING = 0 |
1920 | +MODE_IMAGE = 1 |
1921 | +MODE_DRAW = 2 |
1922 | + |
1923 | +UNDO_RESIZE = 0 |
1924 | +UNDO_DRAW = 1 |
1925 | +UNDO_ERASE = 2 |
1926 | + |
1927 | +class DrawingThought (BaseThought.ResizableThought): |
1928 | + class DrawingPoint (object): |
1929 | + def __init__ (self, coords, style=STYLE_CONTINUE, color = gtk.gdk.Color(0,0,0), width = 2): |
1930 | + self.x, self.y = coords |
1931 | + self.style = style |
1932 | + if color == None: |
1933 | + color = gtk.gdk.Color(0,0,0) |
1934 | + self.color = color |
1935 | + self.width = 1 |
1936 | + def move_by (self, x, y): |
1937 | + self.x += x |
1938 | + self.y += y |
1939 | + |
1940 | + def __init__ (self, coords, pango_context, thought_number, save, undo, loading, background_color, foreground_color): |
1941 | + global ndraw |
1942 | + super (DrawingThought, self).__init__(save, "drawing_thought", undo, background_color, foreground_color) |
1943 | + ndraw+=1 |
1944 | + self.identity = thought_number |
1945 | + self.want_move = False |
1946 | + self.points = [] |
1947 | + self.text = _("Drawing #%d" % ndraw) |
1948 | + self.drawing = 0 |
1949 | + if not loading: |
1950 | + margin = utils.margin_required (utils.STYLE_NORMAL) |
1951 | + self.ul = (coords[0]-margin[0], coords[1]-margin[1]) |
1952 | + self.lr = (coords[0]+100+margin[2], coords[1]+100+margin[3]) |
1953 | + self.min_x = coords[0]+90 |
1954 | + self.max_x = coords[0]+15 |
1955 | + self.min_y = coords[1]+90 |
1956 | + self.max_y = coords[1]+15 |
1957 | + self.width = 100 |
1958 | + self.height = 100 |
1959 | + |
1960 | + self.all_okay = True |
1961 | + |
1962 | + def draw (self, context): |
1963 | + if len (self.extended_buffer.get_text()) == 0: |
1964 | + utils.draw_thought_outline (context, self.ul, self.lr, self.background_color, self.am_selected, self.am_primary, utils.STYLE_NORMAL) |
1965 | + else: |
1966 | + utils.draw_thought_outline (context, self.ul, self.lr, self.background_color, self.am_selected, self.am_primary, utils.STYLE_EXTENDED_CONTENT) |
1967 | + cwidth = context.get_line_width () |
1968 | + context.set_line_width (2) |
1969 | + if len (self.points) > 0: |
1970 | + for p in self.points: |
1971 | + if p.style == STYLE_BEGIN: |
1972 | + context.move_to (p.x, p.y) |
1973 | + r,g,b = utils.gtk_to_cairo_color(self.foreground_color) |
1974 | + context.set_source_rgb (r, g, b) |
1975 | + elif p.style == STYLE_END: |
1976 | + context.line_to (p.x, p.y) |
1977 | + context.stroke() |
1978 | + else: |
1979 | + context.line_to (p.x, p.y) |
1980 | + |
1981 | + context.set_line_width (cwidth) |
1982 | + context.stroke () |
1983 | + return |
1984 | + |
1985 | + def want_motion (self): |
1986 | + return self.want_move |
1987 | + |
1988 | + def recalc_edges (self): |
1989 | + self.lr = (self.ul[0]+self.width, self.ul[1]+self.height) |
1990 | + |
1991 | + def undo_resize (self, action, mode): |
1992 | + self.undo.block () |
1993 | + choose = 1 |
1994 | + if mode == UndoManager.UNDO: |
1995 | + choose = 0 |
1996 | + self.ul = action.args[choose][0] |
1997 | + self.width = action.args[choose][1] |
1998 | + self.height = action.args[choose][2] |
1999 | + self.recalc_edges () |
2000 | + self.emit ("update_links") |
2001 | + self.emit ("update_view") |
2002 | + self.undo.unblock () |
2003 | + |
2004 | + def undo_drawing (self, action, mode): |
2005 | + self.undo.block () |
2006 | + if mode == UndoManager.UNDO: |
2007 | + choose = 1 |
2008 | + for p in action.args[0]: |
2009 | + self.points.remove (p) |
2010 | + else: |
2011 | + choose = 2 |
2012 | + for p in action.args[0]: |
2013 | + self.points.append (p) |
2014 | + |
2015 | + self.ul = action.args[choose][0] |
2016 | + self.width = action.args[choose][1] |
2017 | + self.height = action.args[choose][2] |
2018 | + self.recalc_edges () |
2019 | + self.emit ("update_links") |
2020 | + self.emit ("update_view") |
2021 | + self.undo.unblock () |
2022 | + |
2023 | + def process_button_down (self, event, mode, transformed): |
2024 | + modifiers = gtk.accelerator_get_default_mod_mask () |
2025 | + self.button_down = True |
2026 | + if event.button == 1: |
2027 | + if event.type == gtk.gdk.BUTTON_PRESS: |
2028 | + self.emit ("select_thought", event.state & modifiers) |
2029 | + self.emit ("update_view") |
2030 | + if mode == MODE_EDITING and self.resizing != self.RESIZE_NONE: |
2031 | + self.want_move = True |
2032 | + self.drawing = 0 |
2033 | + self.orig_size = (self.ul, self.width, self.height) |
2034 | + return True |
2035 | + elif mode == MODE_DRAW: |
2036 | + self.want_move = True |
2037 | + self.drawing = 2 |
2038 | + if not event.state & gtk.gdk.SHIFT_MASK: |
2039 | + self.drawing = 1 |
2040 | + self.orig_size = (self.ul, self.width, self.height) |
2041 | + self.ins_points = [] |
2042 | + self.del_points = [] |
2043 | + return True |
2044 | + elif event.button == 3: |
2045 | + self.emit ("popup_requested", event, 1) |
2046 | + self.emit ("update_view") |
2047 | + |
2048 | + def process_button_release (self, event, unending_link, mode, transformed): |
2049 | + self.button_down = False |
2050 | + if unending_link: |
2051 | + unending_link.set_child (self) |
2052 | + self.emit ("claim_unending_link") |
2053 | + if len(self.points) > 0: |
2054 | + self.points[-1].style=STYLE_END |
2055 | + self.emit ("update_view") |
2056 | + if self.want_move and self.drawing == 0: |
2057 | + self.undo.add_undo (UndoManager.UndoAction (self, UNDO_RESIZE, self.undo_resize, \ |
2058 | + self.orig_size, (self.ul, self.width, self.height))) |
2059 | + elif self.want_move and self.drawing == 1: |
2060 | + self.undo.add_undo (UndoManager.UndoAction (self, UNDO_DRAW, self.undo_drawing, \ |
2061 | + self.ins_points, self.orig_size, \ |
2062 | + (self.ul, self.width, self.height))) |
2063 | + elif self.want_move and self.drawing == 2: |
2064 | + self.undo.add_undo (UndoManager.UndoAction (self, UNDO_ERASE, self.undo_erase, \ |
2065 | + self.ins_points)) |
2066 | + self.drawing = 0 |
2067 | + self.want_move = False |
2068 | + |
2069 | + def undo_erase (self, action, mode): |
2070 | + self.undo.block () |
2071 | + action.args[0].reverse () |
2072 | + if mode == UndoManager.UNDO: |
2073 | + for x in action.args[0]: |
2074 | + if x[0] == 0: |
2075 | + self.points.remove (x[2]) |
2076 | + else: |
2077 | + self.points.insert (x[1],x[2]) |
2078 | + else: |
2079 | + for x in action.args[0]: |
2080 | + if x[0] == 0: |
2081 | + self.points.insert (x[1], x[2]) |
2082 | + else: |
2083 | + self.points.remove (x[2]) |
2084 | + self.undo.unblock () |
2085 | + self.emit ("update_view") |
2086 | + |
2087 | + def handle_motion (self, event, mode, transformed): |
2088 | + if (self.resizing == self.RESIZE_NONE or not self.want_move or not event.state & gtk.gdk.BUTTON1_MASK) \ |
2089 | + and mode != MODE_DRAW: |
2090 | + if not event.state & gtk.gdk.BUTTON1_MASK or mode != MODE_EDITING: |
2091 | + return False |
2092 | + else: |
2093 | + self.emit ("create_link", \ |
2094 | + (self.ul[0]-((self.ul[0]-self.lr[0]) / 2.), self.ul[1]-((self.ul[1]-self.lr[1]) / 2.))) |
2095 | + diffx = transformed[0] - self.motion_coords[0] |
2096 | + diffy = transformed[1] - self.motion_coords[1] |
2097 | + change = (len(self.points) == 0) |
2098 | + tmp = self.motion_coords |
2099 | + self.motion_coords = transformed |
2100 | + if self.resizing != self.RESIZE_NONE: |
2101 | + if self.resizing == self.RESIZE_LEFT: |
2102 | + if self.ul[0] + diffx > self.min_x: |
2103 | + self.motion_coords = tmp |
2104 | + return True |
2105 | + self.ul = (self.ul[0]+diffx, self.ul[1]) |
2106 | + if change: |
2107 | + self.max_x += diffx |
2108 | + elif self.resizing == self.RESIZE_RIGHT: |
2109 | + if self.lr[0] + diffx < self.max_x: |
2110 | + self.motion_coords = tmp |
2111 | + return True |
2112 | + self.lr = (self.lr[0]+diffx, self.lr[1]) |
2113 | + if change: |
2114 | + self.min_x += diffx |
2115 | + elif self.resizing == self.RESIZE_TOP: |
2116 | + if self.ul[1] + diffy > self.min_y: |
2117 | + self.motion_coords = tmp |
2118 | + return True |
2119 | + self.ul = (self.ul[0], self.ul[1]+diffy) |
2120 | + if change: |
2121 | + self.max_y += diffy |
2122 | + elif self.resizing == self.RESIZE_BOTTOM: |
2123 | + if self.lr[1] + diffy < self.max_y: |
2124 | + self.motion_coords = tmp |
2125 | + return True |
2126 | + self.lr = (self.lr[0], self.lr[1]+diffy) |
2127 | + if change: |
2128 | + self.min_y += diffy |
2129 | + elif self.resizing == self.RESIZE_UL: |
2130 | + if self.ul[1] + diffy > self.min_y or self.ul[0] + diffx > self.min_x: |
2131 | + self.motion_coords = tmp |
2132 | + return True |
2133 | + self.ul = (self.ul[0]+diffx, self.ul[1]+diffy) |
2134 | + if change: |
2135 | + self.max_x += diffx |
2136 | + self.max_y += diffy |
2137 | + elif self.resizing == self.RESIZE_UR: |
2138 | + if self.ul[1] + diffy > self.min_y or self.lr[0] + diffx < self.max_x: |
2139 | + self.motion_coords = tmp |
2140 | + return True |
2141 | + self.ul = (self.ul[0], self.ul[1]+diffy) |
2142 | + self.lr = (self.lr[0]+diffx, self.lr[1]) |
2143 | + if change: |
2144 | + self.min_x += diffx |
2145 | + self.max_y += diffy |
2146 | + elif self.resizing == self.RESIZE_LL: |
2147 | + if self.lr[1] + diffy < self.max_y or self.ul[0] + diffx > self.min_x: |
2148 | + self.motion_coords = tmp |
2149 | + return True |
2150 | + self.ul = (self.ul[0]+diffx, self.ul[1]) |
2151 | + self.lr = (self.lr[0], self.lr[1]+diffy) |
2152 | + if change: |
2153 | + self.max_x += diffx |
2154 | + self.min_y += diffy |
2155 | + elif self.resizing == self.RESIZE_LR: |
2156 | + if self.lr[1] + diffy < self.max_y: |
2157 | + self.motion_coords = tmp |
2158 | + return True |
2159 | + if self.lr[0] + diffx < self.max_x: |
2160 | + self.motion_coords = tmp |
2161 | + return True |
2162 | + self.lr = (self.lr[0]+diffx, self.lr[1]+diffy) |
2163 | + if change: |
2164 | + self.min_x += diffx |
2165 | + self.min_y += diffy |
2166 | + self.width = self.lr[0] - self.ul[0] |
2167 | + self.height = self.lr[1] - self.ul[1] |
2168 | + self.emit ("update_links") |
2169 | + self.emit ("update_view") |
2170 | + return True |
2171 | + |
2172 | + elif self.drawing == 1: |
2173 | + if transformed[0] < self.ul[0]+5: |
2174 | + self.ul = (transformed[0]-5, self.ul[1]) |
2175 | + elif transformed[0] > self.lr[0]-5: |
2176 | + self.lr = (transformed[0]+5, self.lr[1]) |
2177 | + if transformed[1] < self.ul[1]+5: |
2178 | + self.ul = (self.ul[0], transformed[1]-5) |
2179 | + elif transformed[1] > self.lr[1]-5: |
2180 | + self.lr = (self.lr[0], transformed[1]+5) |
2181 | + |
2182 | + if transformed[0] < self.min_x: |
2183 | + self.min_x = transformed[0]-10 |
2184 | + elif transformed[0] > self.max_x: |
2185 | + self.max_x = transformed[0]+5 |
2186 | + if transformed[1] < self.min_y: |
2187 | + self.min_y = transformed[1]-10 |
2188 | + elif transformed[1] > self.max_y: |
2189 | + self.max_y = transformed[1]+5 |
2190 | + self.width = self.lr[0] - self.ul[0] |
2191 | + self.height = self.lr[1] - self.ul[1] |
2192 | + if len(self.points) == 0 or self.points[-1].style == STYLE_END: |
2193 | + p = self.DrawingPoint (transformed, STYLE_BEGIN, self.foreground_color) |
2194 | + else: |
2195 | + p = self.DrawingPoint (transformed, STYLE_CONTINUE) |
2196 | + self.points.append (p) |
2197 | + self.ins_points.append (p) |
2198 | + elif self.drawing == 2 and len (self.points) > 0: |
2199 | + out = self.points[0] |
2200 | + loc = [] |
2201 | + handle = [] |
2202 | + ins_point = -1 |
2203 | + |
2204 | + for x in self.points: |
2205 | + ins_point += 1 |
2206 | + dist = (x.x - transformed[0])**2 + (x.y - transformed[1])**2 |
2207 | + |
2208 | + if dist < 16: |
2209 | + if x == self.points[0]: |
2210 | + out = None |
2211 | + loc.append ((ins_point, x, dist)) |
2212 | + else: |
2213 | + if len(loc) != 0: |
2214 | + handle.append ((loc, out, x)) |
2215 | + loc = [] |
2216 | + elif x.style != STYLE_BEGIN: |
2217 | + x1 = x.x - out.x |
2218 | + y1 = x.y - out.y |
2219 | + d_rsqr = x1**2 + y1 **2 |
2220 | + d = ((out.x-transformed[0])*(x.y-transformed[1]) - (x.x-transformed[0])*(out.y-transformed[1])) |
2221 | + det = (d_rsqr*16) - d**2 |
2222 | + if det > 0: |
2223 | + xt = -99999 |
2224 | + yt = -99999 |
2225 | + xalt = -99999 |
2226 | + yalt = -99999 |
2227 | + if y1 < 0: |
2228 | + sgn = -1 |
2229 | + else: |
2230 | + sgn = 1 |
2231 | + xt = (((d*y1) + sgn*x1 * math.sqrt (det)) / d_rsqr) +transformed[0] |
2232 | + xalt = (((d*y1) - sgn*x1 * math.sqrt (det)) / d_rsqr) +transformed[0] |
2233 | + yt = (((-d*x1) + abs(y1)*math.sqrt(det)) / d_rsqr) + transformed[1] |
2234 | + yalt = (((-d*x1) - abs(y1)*math.sqrt(det)) / d_rsqr) +transformed[1] |
2235 | + x1_inside = (xt > x.x and xt < out.x) or (xt > out.x and xt < x.x) |
2236 | + x2_inside = (xalt > x.x and xalt < out.x) or (xalt > out.x and xalt < x.x) |
2237 | + y1_inside = (yt > x.y and yt < out.y) or (yt > out.y and yt < x.y) |
2238 | + y2_inside = (yalt > x.y and yalt < out.y) or (yalt > out.y and yalt < x.y) |
2239 | + |
2240 | + |
2241 | + if (x1_inside and x2_inside and y1_inside and y2_inside): |
2242 | + if abs (xalt - x.x) < abs (xt - x.x): |
2243 | + handle.append ((None, out, x, ins_point, xt, xalt, yt, yalt)) |
2244 | + else: |
2245 | + handle.append ((None, out, x, ins_point, xalt, xt, yalt, yt)) |
2246 | + elif x.x == out.x and y1_inside and y2_inside: |
2247 | + if abs (yalt - x.y) < abs (yt - x.y): |
2248 | + handle.append ((None, out, x, ins_point, xt, xalt, yt, yalt)) |
2249 | + else: |
2250 | + handle.append ((None, out, x, ins_point, xalt, xt, yalt, yt)) |
2251 | + elif x.y == out.y and x1_inside and x2_inside: |
2252 | + if abs (xalt - x.x) < abs (xt - x.x): |
2253 | + handle.append ((None, out, x, ins_point, xt, xalt, yt, yalt)) |
2254 | + else: |
2255 | + handle.append ((None, out, x, ins_point, xalt, xt, yalt, yt)) |
2256 | + |
2257 | + out = x |
2258 | + if loc: |
2259 | + handle.append ((loc, out, None)) |
2260 | + appends = [] |
2261 | + dels = [] |
2262 | + for l in handle: |
2263 | + inside = l[0] |
2264 | + prev = l[1] |
2265 | + next = l[2] |
2266 | + if not inside: |
2267 | + ins = l[3] |
2268 | + x1 = l[4] |
2269 | + x2 = l[5] |
2270 | + y1 = l[6] |
2271 | + y2 = l[7] |
2272 | + p1 = self.DrawingPoint ((x1,y1), STYLE_END) |
2273 | + p2 = self.DrawingPoint ((x2,y2), STYLE_BEGIN) |
2274 | + appends.append ((p1, ins)) |
2275 | + appends.append ((p2, ins)) |
2276 | + else: |
2277 | + first = inside[0][1] |
2278 | + last = inside[-1][1] |
2279 | + done_ins = 0 |
2280 | + if last.style != STYLE_END: |
2281 | + end_dist = math.sqrt (inside[-1][2]) - 4 |
2282 | + alpha = math.atan2 ((last.y-next.y), (last.x-next.x)) |
2283 | + new_x = end_dist * math.cos(alpha) + last.x |
2284 | + new_y = end_dist * math.sin(alpha) + last.y |
2285 | + p = self.DrawingPoint ((new_x, new_y), STYLE_BEGIN) |
2286 | + appends.append ((p, inside[-1][0])) |
2287 | + done_ins = 1 |
2288 | + if first.style != STYLE_BEGIN: |
2289 | + start_dist = math.sqrt (inside[0][2]) - 4 |
2290 | + alpha = math.atan2 ((first.y-prev.y),(first.x-prev.x)) |
2291 | + new_x = start_dist * math.cos (alpha) + first.x |
2292 | + new_y = start_dist * math.sin (alpha) + first.y |
2293 | + p = self.DrawingPoint ((new_x, new_y), STYLE_END) |
2294 | + appends.append ((p, inside[0][0]-done_ins)) |
2295 | + for i in inside: |
2296 | + dels.append (i[1]) |
2297 | + inserts = 0 |
2298 | + for x in appends: |
2299 | + self.points.insert (x[1]+inserts, x[0]) |
2300 | + self.ins_points.append ((0, x[1]+inserts, x[0])) |
2301 | + inserts+=1 |
2302 | + for x in dels: |
2303 | + self.ins_points.append ((1, self.points.index (x), x)) |
2304 | + self.points.remove (x) |
2305 | + |
2306 | + self.emit ("update_links") |
2307 | + self.emit ("update_view") |
2308 | + return True |
2309 | + |
2310 | + def move_by (self, x, y): |
2311 | + self.ul = (self.ul[0]+x, self.ul[1]+y) |
2312 | + self.min_x += x |
2313 | + self.min_y += y |
2314 | + self.max_x += x |
2315 | + self.max_y += y |
2316 | + for p in self.points: |
2317 | + p.move_by(x, y) |
2318 | + self.recalc_edges () |
2319 | + self.emit ("update_links") |
2320 | + |
2321 | + def update_save (self): |
2322 | + next = self.element.firstChild |
2323 | + while next: |
2324 | + m = next.nextSibling |
2325 | + if next.nodeName == "point": |
2326 | + self.element.removeChild (next) |
2327 | + next.unlink () |
2328 | + next = m |
2329 | + text = self.extended_buffer.get_text () |
2330 | + if text: |
2331 | + self.extended_buffer.update_save() |
2332 | + else: |
2333 | + try: |
2334 | + self.element.removeChild(self.extended_buffer.element) |
2335 | + except xml.dom.NotFoundErr: |
2336 | + pass |
2337 | + self.element.setAttribute ("ul-coords", str(self.ul)) |
2338 | + self.element.setAttribute ("lr-coords", str(self.lr)) |
2339 | + self.element.setAttribute ("identity", str(self.identity)) |
2340 | + self.element.setAttribute ("background-color", self.background_color.to_string()) |
2341 | + self.element.setAttribute ("foreground-color", self.foreground_color.to_string()) |
2342 | + self.element.setAttribute ("min_x", str(self.min_x)) |
2343 | + self.element.setAttribute ("min_y", str(self.min_y)) |
2344 | + self.element.setAttribute ("max_x", str(self.max_x)) |
2345 | + self.element.setAttribute ("max_y", str(self.max_y)) |
2346 | + |
2347 | + if self.am_selected: |
2348 | + self.element.setAttribute ("current_root", "true") |
2349 | + else: |
2350 | + try: |
2351 | + self.element.removeAttribute ("current_root") |
2352 | + except xml.dom.NotFoundErr: |
2353 | + pass |
2354 | + if self.am_primary: |
2355 | + self.element.setAttribute ("primary_root", "true"); |
2356 | + else: |
2357 | + try: |
2358 | + self.element.removeAttribute ("primary_root") |
2359 | + except xml.dom.NotFoundErr: |
2360 | + pass |
2361 | + doc = self.element.ownerDocument |
2362 | + for p in self.points: |
2363 | + elem = doc.createElement ("point") |
2364 | + self.element.appendChild (elem) |
2365 | + elem.setAttribute ("coords", str((p.x,p.y))) |
2366 | + elem.setAttribute ("type", str(p.style)) |
2367 | + elem.setAttribute ("color", p.color.to_string()) |
2368 | + return |
2369 | + |
2370 | + def load (self, node): |
2371 | + tmp = node.getAttribute ("ul-coords") |
2372 | + self.ul = utils.parse_coords (tmp) |
2373 | + tmp = node.getAttribute ("lr-coords") |
2374 | + self.lr = utils.parse_coords (tmp) |
2375 | + self.identity = int (node.getAttribute ("identity")) |
2376 | + try: |
2377 | + tmp = node.getAttribute ("background-color") |
2378 | + self.background_color = gtk.gdk.color_parse(tmp) |
2379 | + tmp = node.getAttribute ("foreground-color") |
2380 | + self.foreground_color = gtk.gdk.color_parse(tmp) |
2381 | + except ValueError: |
2382 | + pass |
2383 | + self.min_x = float(node.getAttribute ("min_x")) |
2384 | + self.min_y = float(node.getAttribute ("min_y")) |
2385 | + self.max_x = float(node.getAttribute ("max_x")) |
2386 | + self.max_y = float(node.getAttribute ("max_y")) |
2387 | + |
2388 | + self.width = self.lr[0] - self.ul[0] |
2389 | + self.height = self.lr[1] - self.ul[1] |
2390 | + |
2391 | + self.am_selected = node.hasAttribute ("current_root") |
2392 | + self.am_primary = node.hasAttribute ("primary_root") |
2393 | + |
2394 | + for n in node.childNodes: |
2395 | + if n.nodeName == "Extended": |
2396 | + self.extended_buffer.load(n) |
2397 | + elif n.nodeName == "point": |
2398 | + style = int (n.getAttribute ("type")) |
2399 | + tmp = n.getAttribute ("coords") |
2400 | + c = utils.parse_coords (tmp) |
2401 | + col = None |
2402 | + try: |
2403 | + tmp = n.getAttribute ("color") |
2404 | + col = gtk.gdk.color_parse (tmp) |
2405 | + except ValueError: |
2406 | + pass |
2407 | + self.points.append (self.DrawingPoint (c, style, col)) |
2408 | + else: |
2409 | + print "Unknown node type: "+str(n.nodeName) |
2410 | + |
2411 | + def export (self, context, move_x, move_y): |
2412 | + utils.export_thought_outline (context, self.ul, self.lr, self.background_color, self.am_selected, self.am_primary, utils.STYLE_NORMAL, |
2413 | + (move_x, move_y)) |
2414 | + cwidth = context.get_line_width () |
2415 | + context.set_line_width (1) |
2416 | + if len (self.points) > 0: |
2417 | + for p in self.points: |
2418 | + if p.style == STYLE_BEGIN: |
2419 | + context.move_to (p.x+move_x, p.y+move_y) |
2420 | + else: |
2421 | + context.line_to (p.x+move_x,p.y+move_y) |
2422 | + |
2423 | + context.set_line_width (cwidth) |
2424 | + r,g,b = utils.gtk_to_cairo_color(self.foreground_color) |
2425 | + context.set_source_rgb (r, g, b) |
2426 | + context.stroke () |
2427 | + return |
2428 | + |
2429 | + def includes (self, coords, mode): |
2430 | + if not self.ul or not self.lr or not coords: |
2431 | + return False |
2432 | + |
2433 | + if self.want_move and mode == MODE_DRAW: |
2434 | + self.emit ("change_mouse_cursor", gtk.gdk.PENCIL) |
2435 | + return True |
2436 | + |
2437 | + inside = (coords[0] < self.lr[0] + self.sensitive) and \ |
2438 | + (coords[0] > self.ul[0] - self.sensitive) and \ |
2439 | + (coords[1] < self.lr[1] + self.sensitive) and \ |
2440 | + (coords[1] > self.ul[1] - self.sensitive) |
2441 | + |
2442 | + self.resizing = self.RESIZE_NONE |
2443 | + self.motion_coords = coords |
2444 | + |
2445 | + if inside and (mode != MODE_EDITING or self.button_down): |
2446 | + if mode == MODE_DRAW: |
2447 | + self.emit ("change_mouse_cursor", gtk.gdk.PENCIL) |
2448 | + else: |
2449 | + self.emit ("change_mouse_cursor", gtk.gdk.LEFT_PTR) |
2450 | + return inside |
2451 | + |
2452 | + if inside: |
2453 | + # 2 cases: 1. The click was within the main area |
2454 | + # 2. The click was near the border |
2455 | + # In the first case, we handle as normal |
2456 | + # In the second case, we want to intercept all the fun thats |
2457 | + # going to happen so we can resize the thought |
2458 | + if abs (coords[0] - self.ul[0]) < self.sensitive: |
2459 | + # its near the top edge somewhere |
2460 | + if abs (coords[1] - self.ul[1]) < self.sensitive: |
2461 | + # Its in the ul corner |
2462 | + self.resizing = self.RESIZE_UL |
2463 | + self.emit ("change_mouse_cursor", gtk.gdk.TOP_LEFT_CORNER) |
2464 | + elif abs (coords[1] - self.lr[1]) < self.sensitive: |
2465 | + # Its in the ll corner |
2466 | + self.resizing = self.RESIZE_LL |
2467 | + self.emit ("change_mouse_cursor", gtk.gdk.BOTTOM_LEFT_CORNER) |
2468 | + elif coords[1] < self.lr[1] and coords[1] > self.ul[1]: |
2469 | + #anywhere else along the left edge |
2470 | + self.resizing = self.RESIZE_LEFT |
2471 | + self.emit ("change_mouse_cursor", gtk.gdk.LEFT_SIDE) |
2472 | + elif abs (coords[0] - self.lr[0]) < self.sensitive: |
2473 | + if abs (coords[1] - self.ul[1]) < self.sensitive: |
2474 | + # Its in the UR corner |
2475 | + self.resizing = self.RESIZE_UR |
2476 | + self.emit ("change_mouse_cursor", gtk.gdk.TOP_RIGHT_CORNER) |
2477 | + elif abs (coords[1] - self.lr[1]) < self.sensitive: |
2478 | + # Its in the lr corner |
2479 | + self.resizing = self.RESIZE_LR |
2480 | + self.emit ("change_mouse_cursor", gtk.gdk.BOTTOM_RIGHT_CORNER) |
2481 | + elif coords[1] < self.lr[1] and coords[1] > self.ul[1]: |
2482 | + #anywhere else along the right edge |
2483 | + self.resizing = self.RESIZE_RIGHT |
2484 | + self.emit ("change_mouse_cursor", gtk.gdk.RIGHT_SIDE) |
2485 | + elif abs (coords[1] - self.ul[1]) < self.sensitive and \ |
2486 | + (coords[0] < self.lr[0] and coords[0] > self.ul[0]): |
2487 | + # Along the top edge somewhere |
2488 | + self.resizing = self.RESIZE_TOP |
2489 | + self.emit ("change_mouse_cursor", gtk.gdk.TOP_SIDE) |
2490 | + elif abs (coords[1] - self.lr[1]) < self.sensitive and \ |
2491 | + (coords[0] < self.lr[0] and coords[0] > self.ul[0]): |
2492 | + # Along the bottom edge somewhere |
2493 | + self.resizing = self.RESIZE_BOTTOM |
2494 | + self.emit ("change_mouse_cursor", gtk.gdk.BOTTOM_SIDE) |
2495 | + else: |
2496 | + self.emit ("change_mouse_cursor", gtk.gdk.LEFT_PTR) |
2497 | + self.want_move = (self.resizing != self.RESIZE_NONE) |
2498 | + return inside |
2499 | + |
2500 | + def get_popup_menu_items(self): |
2501 | + return [] |
2502 | |
2503 | === added file 'labyrinth_lib/ImageThought.py' |
2504 | --- labyrinth_lib/ImageThought.py 1970-01-01 00:00:00 +0000 |
2505 | +++ labyrinth_lib/ImageThought.py 2012-11-11 04:19:21 +0000 |
2506 | @@ -0,0 +1,342 @@ |
2507 | +# ImageThought.py |
2508 | +# This file is part of labyrinth |
2509 | +# |
2510 | +# Copyright (C) 2006 - Don Scorgie <Don@Scorgie.org> |
2511 | +# |
2512 | +# labyrinth is free software; you can redistribute it and/or modify |
2513 | +# it under the terms of the GNU General Public License as published by |
2514 | +# the Free Software Foundation; either version 2 of the License, or |
2515 | +# (at your option) any later version. |
2516 | +# |
2517 | +# labyrinth is distributed in the hope that it will be useful, |
2518 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2519 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2520 | +# GNU General Public License for more details. |
2521 | +# |
2522 | +# You should have received a copy of the GNU General Public License |
2523 | +# along with labyrinth; if not, write to the Free Software |
2524 | +# Foundation, Inc., 51 Franklin St, Fifth Floor, |
2525 | +# Boston, MA 02110-1301 USA |
2526 | +# |
2527 | + |
2528 | +# Standard library |
2529 | +import os.path |
2530 | +import xml.dom |
2531 | +import gettext |
2532 | +_ = gettext.gettext |
2533 | + |
2534 | +# Gtk stuff |
2535 | +import gtk |
2536 | +import cairo |
2537 | + |
2538 | +# Local imports |
2539 | +import BaseThought |
2540 | +import utils |
2541 | +import UndoManager |
2542 | + |
2543 | +MODE_EDITING = 0 |
2544 | +MODE_IMAGE = 1 |
2545 | +MODE_DRAW = 2 |
2546 | + |
2547 | +UNDO_RESIZE = 0 |
2548 | + |
2549 | +class ImageThought (BaseThought.ResizableThought): |
2550 | + def __init__ (self, coords, pango_context, thought_number, save, undo, loading, background_color): |
2551 | + super (ImageThought, self).__init__(save, "image_thought", undo, background_color, None) |
2552 | + |
2553 | + self.identity = thought_number |
2554 | + margin = utils.margin_required (utils.STYLE_NORMAL) |
2555 | + self.want_move = False |
2556 | + if coords: |
2557 | + self.ul = (coords[0]-margin[0], coords[1] - margin[1]) |
2558 | + self.pic_location = coords |
2559 | + else: |
2560 | + self.ul = None |
2561 | + self.button_press = False |
2562 | + |
2563 | + if not loading: |
2564 | + self.all_okay = self.open_image () |
2565 | + else: |
2566 | + self.all_okay = True |
2567 | + |
2568 | + def open_image (self, filename = None): |
2569 | + # Present a dialog for the user to choose an image here |
2570 | + if not filename: |
2571 | + fil = gtk.FileFilter () |
2572 | + fil.set_name("Images") |
2573 | + fil.add_pixbuf_formats () |
2574 | + dialog = gtk.FileChooserDialog (_("Choose image to insert"), None, gtk.FILE_CHOOSER_ACTION_OPEN, \ |
2575 | + (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) |
2576 | + dialog.add_filter (fil) |
2577 | + res = dialog.run () |
2578 | + dialog.hide () |
2579 | + if res != gtk.RESPONSE_OK: |
2580 | + return False |
2581 | + else: |
2582 | + fname = dialog.get_filename() |
2583 | + else: |
2584 | + fname = filename |
2585 | + |
2586 | + try: |
2587 | + self.orig_pic = gtk.gdk.pixbuf_new_from_file (fname) |
2588 | + except: |
2589 | + try: |
2590 | + # lets see if file was imported and is already extracted |
2591 | + fname = os.path.join (utils.get_images_dir (), + os.path.basename(filename)) |
2592 | + self.orig_pic = gtk.gdk.pixbuf_new_from_file (fname) |
2593 | + except: |
2594 | + return False |
2595 | + |
2596 | + self.filename = fname |
2597 | + |
2598 | + if not filename: |
2599 | + self.width = self.orig_pic.get_width () |
2600 | + self.height = self.orig_pic.get_height () |
2601 | + margin = utils.margin_required (utils.STYLE_NORMAL) |
2602 | + |
2603 | + self.lr = (self.pic_location[0]+self.width+margin[2], self.pic_location[1]+self.height+margin[3]) |
2604 | + self.pic = self.orig_pic |
2605 | + self.text = fname[fname.rfind('/')+1:fname.rfind('.')] |
2606 | + return True |
2607 | + |
2608 | + def draw (self, context): |
2609 | + if len (self.extended_buffer.get_text()) == 0: |
2610 | + utils.draw_thought_outline (context, self.ul, self.lr, self.background_color, self.am_selected, self.am_primary, utils.STYLE_NORMAL) |
2611 | + else: |
2612 | + utils.draw_thought_outline (context, self.ul, self.lr, self.background_color, self.am_selected, self.am_primary, utils.STYLE_EXTENDED_CONTENT) |
2613 | + |
2614 | + if self.pic: |
2615 | + context.set_source_pixbuf (self.pic, self.pic_location[0], self.pic_location[1]) |
2616 | + context.rectangle (self.pic_location[0], self.pic_location[1], self.width, self.height) |
2617 | + context.fill () |
2618 | + context.set_source_rgb (0,0,0) |
2619 | + |
2620 | + def export (self, context, move_x, move_y): |
2621 | + utils.export_thought_outline (context, self.ul, self.lr, self.background_color, self.am_selected, self.am_primary, utils.STYLE_NORMAL, |
2622 | + (move_x, move_y)) |
2623 | + if self.pic: |
2624 | + if hasattr(context, "set_source_pixbuf"): |
2625 | + context.set_source_pixbuf (self.pic, self.pic_location[0]+move_x, self.pic_location[1]+move_y) |
2626 | + elif hasattr(context, "set_source_surface"): |
2627 | + pixel_array = utils.pixbuf_to_cairo (self.pic.get_pixels_array()) |
2628 | + image_surface = cairo.ImageSurface.create_for_data(pixel_array, cairo.FORMAT_ARGB32, self.width, self.height, -1) |
2629 | + context.set_source_surface (image_surface, self.pic_location[0]+move_x, self.pic_location[1]+move_y) |
2630 | + context.rectangle (self.pic_location[0]+move_x, self.pic_location[1]+move_y, self.width, self.height) |
2631 | + context.fill () |
2632 | + context.set_source_rgb (0,0,0) |
2633 | + |
2634 | + def want_motion (self): |
2635 | + return self.want_move |
2636 | + |
2637 | + def recalc_edges (self): |
2638 | + margin = utils.margin_required (utils.STYLE_NORMAL) |
2639 | + self.pic_location = (self.ul[0]+margin[0], self.ul[1]+margin[1]) |
2640 | + self.lr = (self.pic_location[0]+self.width+margin[2], self.pic_location[1]+self.height+margin[3]) |
2641 | + |
2642 | + def recalc_position (self): |
2643 | + self.recalc_edges () |
2644 | + |
2645 | + def undo_resize (self, action, mode): |
2646 | + self.undo.block () |
2647 | + if mode == UndoManager.UNDO: |
2648 | + choose = 0 |
2649 | + else: |
2650 | + choose = 1 |
2651 | + self.ul = action.args[choose][0] |
2652 | + self.width = action.args[choose][1] |
2653 | + self.height = action.args[choose][2] |
2654 | + self.pic = self.orig_pic.scale_simple (int(self.width), int(self.height), gtk.gdk.INTERP_HYPER) |
2655 | + self.recalc_edges () |
2656 | + self.emit ("update_links") |
2657 | + self.emit ("update_view") |
2658 | + self.undo.unblock () |
2659 | + |
2660 | + def process_button_down (self, event, mode, transformed): |
2661 | + modifiers = gtk.accelerator_get_default_mod_mask () |
2662 | + self.button_down = True |
2663 | + if event.button == 1: |
2664 | + if event.type == gtk.gdk.BUTTON_PRESS: |
2665 | + self.emit ("select_thought", event.state & modifiers) |
2666 | + self.emit ("update_view") |
2667 | + if mode == MODE_EDITING and self.resizing != self.RESIZE_NONE: |
2668 | + self.orig_size = (self.ul, self.width, self.height) |
2669 | + self.want_move = True |
2670 | + return True |
2671 | + elif event.button == 3: |
2672 | + self.emit ("popup_requested", event, 1) |
2673 | + self.emit ("update_view") |
2674 | + |
2675 | + def process_button_release (self, event, unending_link, mode, transformed): |
2676 | + self.button_down = False |
2677 | + if unending_link: |
2678 | + unending_link.set_child (self) |
2679 | + self.emit ("claim_unending_link") |
2680 | + if self.orig_pic: |
2681 | + self.pic = self.orig_pic.scale_simple (int(self.width), int(self.height), gtk.gdk.INTERP_HYPER) |
2682 | + self.emit ("update_view") |
2683 | + if self.want_move: |
2684 | + self.undo.add_undo (UndoManager.UndoAction (self, UNDO_RESIZE, self.undo_resize, \ |
2685 | + self.orig_size, (self.ul, self.width, self.height))) |
2686 | + self.want_move = False |
2687 | + |
2688 | + def handle_motion (self, event, mode, transformed): |
2689 | + if self.resizing == self.RESIZE_NONE or not self.want_move or not event.state & gtk.gdk.BUTTON1_MASK: |
2690 | + if not event.state & gtk.gdk.BUTTON1_MASK: |
2691 | + return False |
2692 | + elif mode == MODE_EDITING: |
2693 | + self.emit ("create_link", \ |
2694 | + (self.ul[0]-((self.ul[0]-self.lr[0]) / 2.), self.ul[1]-((self.ul[1]-self.lr[1]) / 2.))) |
2695 | + return True |
2696 | + diffx = transformed[0] - self.motion_coords[0] |
2697 | + diffy = transformed[1] - self.motion_coords[1] |
2698 | + tmp = self.motion_coords |
2699 | + self.motion_coords = transformed |
2700 | + if self.resizing == self.RESIZE_LEFT: |
2701 | + if self.width - diffx < 10: |
2702 | + self.motion_coords = tmp |
2703 | + return True |
2704 | + self.ul = (self.ul[0]+diffx, self.ul[1]) |
2705 | + self.pic_location = (self.pic_location[0]+diffx, self.pic_location[1]) |
2706 | + self.width -= diffx |
2707 | + elif self.resizing == self.RESIZE_RIGHT: |
2708 | + if self.width + diffx < 10: |
2709 | + self.motion_coords = tmp |
2710 | + return True |
2711 | + self.lr = (self.lr[0]+diffx, self.lr[1]) |
2712 | + self.width += diffx |
2713 | + elif self.resizing == self.RESIZE_TOP: |
2714 | + if self.height - diffy < 10: |
2715 | + self.motion_coords = tmp |
2716 | + return True |
2717 | + self.ul = (self.ul[0], self.ul[1]+diffy) |
2718 | + self.pic_location = (self.pic_location[0], self.pic_location[1]+diffy) |
2719 | + self.height -= diffy |
2720 | + elif self.resizing == self.RESIZE_BOTTOM: |
2721 | + if self.height + diffy < 10: |
2722 | + self.motion_coords = tmp |
2723 | + return True |
2724 | + self.lr = (self.lr[0], self.lr[1]+diffy) |
2725 | + self.height += diffy |
2726 | + elif self.resizing == self.RESIZE_UL: |
2727 | + if self.height - diffy < 10 or self.width - diffx < 10: |
2728 | + self.motion_coords = tmp |
2729 | + return True |
2730 | + self.ul = (self.ul[0]+diffx, self.ul[1]+diffy) |
2731 | + self.pic_location = (self.pic_location[0]+diffx, self.pic_location[1]+diffy) |
2732 | + self.width -= diffx |
2733 | + self.height -= diffy |
2734 | + elif self.resizing == self.RESIZE_UR: |
2735 | + if self.height - diffy < 10 or self.width + diffx < 10: |
2736 | + self.motion_coords = tmp |
2737 | + return True |
2738 | + self.ul = (self.ul[0], self.ul[1]+diffy) |
2739 | + self.lr = (self.lr[0]+diffx, self.lr[1]) |
2740 | + self.pic_location = (self.pic_location[0], self.pic_location[1]+diffy) |
2741 | + self.width += diffx |
2742 | + self.height -= diffy |
2743 | + elif self.resizing == self.RESIZE_LL: |
2744 | + if self.height + diffy < 10 or self.width - diffx < 10: |
2745 | + self.motion_coords = tmp |
2746 | + return True |
2747 | + self.ul = (self.ul[0]+diffx, self.ul[1]) |
2748 | + self.lr = (self.lr[0], self.lr[1]+diffy) |
2749 | + self.pic_location = (self.pic_location[0]+diffx, self.pic_location[1]) |
2750 | + self.width -= diffx |
2751 | + self.height += diffy |
2752 | + elif self.resizing == self.RESIZE_LR: |
2753 | + if self.height + diffy < 10: |
2754 | + self.motion_coords = tmp |
2755 | + return True |
2756 | + if self.width + diffx < 10: |
2757 | + self.motion_coords = tmp |
2758 | + return True |
2759 | + self.lr = (self.lr[0]+diffx, self.lr[1]+diffy) |
2760 | + self.width += diffx |
2761 | + self.height += diffy |
2762 | + if self.orig_pic: |
2763 | + self.pic = self.orig_pic.scale_simple (int(self.width), int(self.height), gtk.gdk.INTERP_NEAREST) |
2764 | + self.emit ("update_links") |
2765 | + self.emit ("update_view") |
2766 | + return True |
2767 | + |
2768 | + def update_save (self): |
2769 | + text = self.extended_buffer.get_text () |
2770 | + if text: |
2771 | + self.extended_buffer.update_save() |
2772 | + else: |
2773 | + try: |
2774 | + self.element.removeChild(self.extended_buffer.element) |
2775 | + except xml.dom.NotFoundErr: |
2776 | + pass |
2777 | + self.element.setAttribute ("ul-coords", str(self.ul)) |
2778 | + self.element.setAttribute ("lr-coords", str(self.lr)) |
2779 | + self.element.setAttribute ("identity", str(self.identity)) |
2780 | + self.element.setAttribute ("background-color", self.background_color.to_string()) |
2781 | + self.element.setAttribute ("file", str(self.filename)) |
2782 | + self.element.setAttribute ("image_width", str(self.width)) |
2783 | + self.element.setAttribute ("image_height", str(self.height)) |
2784 | + if self.am_selected: |
2785 | + self.element.setAttribute ("current_root", "true") |
2786 | + else: |
2787 | + try: |
2788 | + self.element.removeAttribute ("current_root") |
2789 | + except xml.dom.NotFoundErr: |
2790 | + pass |
2791 | + if self.am_primary: |
2792 | + self.element.setAttribute ("primary_root", "true") |
2793 | + else: |
2794 | + try: |
2795 | + self.element.removeAttribute ("primary_root") |
2796 | + except xml.dom.NotFoundErr: |
2797 | + pass |
2798 | + return |
2799 | + |
2800 | + def load (self, node): |
2801 | + tmp = node.getAttribute ("ul-coords") |
2802 | + self.ul = utils.parse_coords (tmp) |
2803 | + tmp = node.getAttribute ("lr-coords") |
2804 | + self.lr = utils.parse_coords (tmp) |
2805 | + self.filename = node.getAttribute ("file") |
2806 | + self.identity = int (node.getAttribute ("identity")) |
2807 | + try: |
2808 | + tmp = node.getAttribute ("background-color") |
2809 | + self.background_color = gtk.gdk.color_parse(tmp) |
2810 | + except ValueError: |
2811 | + pass |
2812 | + self.width = float(node.getAttribute ("image_width")) |
2813 | + self.height = float(node.getAttribute ("image_height")) |
2814 | + self.am_selected = node.hasAttribute ("current_root") |
2815 | + self.am_primary = node.hasAttribute ("primary_root") |
2816 | + |
2817 | + for n in node.childNodes: |
2818 | + if n.nodeName == "Extended": |
2819 | + self.extended_buffer.load(n) |
2820 | + else: |
2821 | + print "Unknown: "+n.nodeName |
2822 | + margin = utils.margin_required (utils.STYLE_NORMAL) |
2823 | + self.pic_location = (self.ul[0]+margin[0], self.ul[1]+margin[1]) |
2824 | + self.okay = self.open_image (self.filename) |
2825 | + self.lr = (self.pic_location[0]+self.width+margin[2], self.pic_location[1]+self.height+margin[3]) |
2826 | + if not self.okay: |
2827 | + dialog = gtk.MessageDialog (None, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, |
2828 | + gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE, |
2829 | + _("Error loading file")) |
2830 | + dialog.format_secondary_text (_("%s could not be found. Associated thought will be empty."%self.filename)) |
2831 | + dialog.run () |
2832 | + dialog.hide () |
2833 | + self.pic = None |
2834 | + self.orig_pic = None |
2835 | + else: |
2836 | + self.pic = self.orig_pic.scale_simple (int(self.width), int(self.height), gtk.gdk.INTERP_HYPER) |
2837 | + return |
2838 | + |
2839 | + def change_image_cb(self, widget): |
2840 | + self.open_image() |
2841 | + |
2842 | + def get_popup_menu_items(self): |
2843 | + image = gtk.Image() |
2844 | + image.set_from_stock(gtk.STOCK_OPEN, gtk.ICON_SIZE_MENU) |
2845 | + item = gtk.ImageMenuItem(_('Change Image')) |
2846 | + item.set_image(image) |
2847 | + item.connect('activate', self.change_image_cb) |
2848 | + return [item] |
2849 | |
2850 | === added file 'labyrinth_lib/Links.py' |
2851 | --- labyrinth_lib/Links.py 1970-01-01 00:00:00 +0000 |
2852 | +++ labyrinth_lib/Links.py 2012-11-11 04:19:21 +0000 |
2853 | @@ -0,0 +1,281 @@ |
2854 | +# Link.py |
2855 | +# This file is part of Labyrinth |
2856 | +# |
2857 | +# Copyright (C) 2006 - Don Scorgie <Don@Scorgie.org> |
2858 | +# |
2859 | +# Labyrinth is free software; you can redistribute it and/or modify |
2860 | +# it under the terms of the GNU General Public License as published by |
2861 | +# the Free Software Foundation; either version 2 of the License, or |
2862 | +# (at your option) any later version. |
2863 | +# |
2864 | +# Labyrinth is distributed in the hope that it will be useful, |
2865 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2866 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2867 | +# GNU General Public License for more details. |
2868 | +# |
2869 | +# You should have received a copy of the GNU General Public License |
2870 | +# along with Labyrinth; if not, write to the Free Software |
2871 | +# Foundation, Inc., 51 Franklin St, Fifth Floor, |
2872 | +# Boston, MA 02110-1301 USA |
2873 | +# |
2874 | + |
2875 | +import math |
2876 | +import gettext |
2877 | +_ = gettext.gettext |
2878 | + |
2879 | +import gobject |
2880 | +import gtk |
2881 | + |
2882 | +import BaseThought |
2883 | +import utils |
2884 | + |
2885 | + |
2886 | +def norm(x, y): |
2887 | + mod = math.sqrt(abs((x[0]**2 - y[0]**2) + (x[1]**2 - y[1]**2))) |
2888 | + return [abs(x[0]-y[0]) / (mod), abs(x[1] - y[1]) / (mod)] |
2889 | + |
2890 | +class Link (gobject.GObject): |
2891 | + __gsignals__ = dict (select_link = (gobject.SIGNAL_RUN_FIRST, |
2892 | + gobject.TYPE_NONE, |
2893 | + (gobject.TYPE_PYOBJECT,)), |
2894 | + update_view = (gobject.SIGNAL_RUN_LAST, |
2895 | + gobject.TYPE_NONE, |
2896 | + ()), |
2897 | + popup_requested = (gobject.SIGNAL_RUN_FIRST, |
2898 | + gobject.TYPE_NONE, |
2899 | + (gobject.TYPE_PYOBJECT, gobject.TYPE_INT))) |
2900 | + |
2901 | + def __init__ (self, save, parent = None, child = None, start_coords = None, end_coords = None, strength = 2): |
2902 | + super (Link, self).__init__() |
2903 | + self.parent = parent |
2904 | + self.child = child |
2905 | + self.end = end_coords |
2906 | + self.start = start_coords |
2907 | + self.strength = strength |
2908 | + self.element = save.createElement ("link") |
2909 | + self.selected = False |
2910 | + self.color = utils.gtk_to_cairo_color(gtk.gdk.color_parse("black")) |
2911 | + |
2912 | + if not self.start and parent and parent.lr: |
2913 | + self.start = (parent.ul[0]-((parent.ul[0]-parent.lr[0]) / 2.), \ |
2914 | + parent.ul[1]-((parent.ul[1]-parent.lr[1]) / 2.)) |
2915 | + |
2916 | + if parent and child: |
2917 | + self.find_ends () |
2918 | + |
2919 | + def get_save_element (self): |
2920 | + return self.element |
2921 | + |
2922 | + def includes (self, coords, mode): |
2923 | + # TODO: Change this to make link selection work. Also needs |
2924 | + # some fairly large changes in MMapArea |
2925 | + if not self.start or not self.end or not coords: |
2926 | + return False |
2927 | + |
2928 | + mag = (math.sqrt(((self.end[0] - self.start[0]) ** 2) + \ |
2929 | + ((self.end[1] - self.start[1]) ** 2))) |
2930 | + |
2931 | + U = (((coords[0] - self.start[0]) * (self.end[0] - self.start[0])) + \ |
2932 | + ((coords[1] - self.start[1]) * (self.end[1] - self.start[1]))) / \ |
2933 | + (mag**2) |
2934 | + |
2935 | + inter = [self.start[0] + U*(self.end[0] - self.start[0]), |
2936 | + self.start[1] + U*(self.end[1] - self.start[1])] |
2937 | + dist = math.sqrt(((coords[0] - inter[0]) ** 2) + \ |
2938 | + ((coords[1] - inter[1]) ** 2)) |
2939 | + if dist < (3+self.strength) and dist > -(3+self.strength): |
2940 | + if self.start[0] < self.end[0] and coords[0] > self.start[0] and coords[0] < self.end[0]: |
2941 | + return True |
2942 | + elif coords[0] < self.start[0] and coords[0] > self.end[0]: |
2943 | + return True |
2944 | + |
2945 | + return False |
2946 | + |
2947 | + def connects (self, thought, thought2): |
2948 | + return (self.parent == thought and self.child == thought2) or \ |
2949 | + (self.child == thought and self.parent == thought2) |
2950 | + |
2951 | + def set_end (self, coords): |
2952 | + self.end = coords |
2953 | + |
2954 | + def set_strength (self, strength): |
2955 | + self.strength = strength |
2956 | + |
2957 | + def change_strength (self, thought, thought2): |
2958 | + if not self.connects (thought, thought2): |
2959 | + return False |
2960 | + if self.parent == thought: |
2961 | + self.strength += 1 |
2962 | + else: |
2963 | + self.strength -= 1 |
2964 | + return self.strength != 0 |
2965 | + |
2966 | + def set_child (self, child): |
2967 | + self.child = child |
2968 | + self.find_ends () |
2969 | + |
2970 | + def uses (self, thought): |
2971 | + return self.parent == thought or self.child == thought |
2972 | + |
2973 | + def find_ends (self): |
2974 | + (self.start, self.end) = self.parent.find_connection (self.child) |
2975 | + |
2976 | + def draw (self, context): |
2977 | + if not self.start or not self.end: |
2978 | + return |
2979 | + cwidth = context.get_line_width () |
2980 | + context.set_line_width (self.strength) |
2981 | + context.move_to (self.start[0], self.start[1]) |
2982 | + |
2983 | + if utils.use_bezier_curves: |
2984 | + dx = self.end[0] - self.start[0] |
2985 | + x2 = self.start[0] + dx / 2.0 |
2986 | + x3 = self.end[0] - dx / 2.0 |
2987 | + context.curve_to(x2, self.start[1], x3, self.end[1], self.end[0], self.end[1]) |
2988 | + else: |
2989 | + context.line_to (self.end[0], self.end[1]) |
2990 | + |
2991 | + if self.selected: |
2992 | + color = utils.selected_colors["bg"] |
2993 | + context.set_source_rgb (color[0], color[1], color[2]) |
2994 | + else: |
2995 | + context.set_source_rgb (self.color[0], self.color[1], self.color[2]) |
2996 | + context.stroke () |
2997 | + context.set_line_width (cwidth) |
2998 | + context.set_source_rgb (0.0, 0.0, 0.0) |
2999 | + |
3000 | + def export (self, context, move_x, move_y): |
3001 | + rem = False |
3002 | + if not self.start or not self.end: |
3003 | + # Probably shouldn't do this, but its safe now |
3004 | + self.start = (self.parent.ul[0]-((self.parent.ul[0]-self.parent.lr[0]) / 2.), \ |
3005 | + self.parent.ul[1]-((self.parent.ul[1]-self.parent.lr[1]) / 2.)) |
3006 | + self.end = (self.child.ul[0]-((self.child.ul[0]-self.child.lr[0]) / 2.), \ |
3007 | + self.child.ul[1]-((self.child.ul[1]-self.child.lr[1]) / 2.)) |
3008 | + rem = True |
3009 | + cwidth = context.get_line_width () |
3010 | + context.set_line_width (self.strength) |
3011 | + context.move_to (self.start[0]+move_x, self.start[1]+move_y) |
3012 | + context.line_to (self.end[0]+move_x, self.end[1]+move_y) |
3013 | + context.stroke () |
3014 | + context.set_line_width (cwidth) |
3015 | + if rem: |
3016 | + self.start = self.end = None |
3017 | + |
3018 | + def set_parent_child (self, parent, child): |
3019 | + self.parent = parent |
3020 | + self.child = child |
3021 | + if self.parent and self.child: |
3022 | + self.find_ends () |
3023 | + |
3024 | + def update_save (self): |
3025 | + self.element.setAttribute ("start", str(self.start)) |
3026 | + self.element.setAttribute ("end", str(self.end)) |
3027 | + self.element.setAttribute ("strength", str(self.strength)) |
3028 | + self.element.setAttribute ("color", str(self.color)) |
3029 | + if self.child: |
3030 | + self.element.setAttribute ("child", str(self.child.identity)) |
3031 | + else: |
3032 | + self.element.setAttribute ("child", "None") |
3033 | + if self.parent: |
3034 | + self.element.setAttribute ("parent", str(self.parent.identity)) |
3035 | + else: |
3036 | + self.element.setAttribute ("parent", "None") |
3037 | + |
3038 | + def load (self, node): |
3039 | + self.parent_number = self.child_number = -1 |
3040 | + tmp = node.getAttribute ("end") |
3041 | + if not tmp: |
3042 | + print "No tmp found" |
3043 | + return |
3044 | + self.end = utils.parse_coords (tmp) |
3045 | + tmp = node.getAttribute ("start") |
3046 | + if not tmp: |
3047 | + print "No start found" |
3048 | + return |
3049 | + self.start = utils.parse_coords (tmp) |
3050 | + self.strength = int(node.getAttribute ("strength")) |
3051 | + try: |
3052 | + colors = node.getAttribute ("color").split() |
3053 | + self.color = (float(colors[0].strip('(,)')), float(colors[1].strip('(,)')), float(colors[2].strip('(,)'))) |
3054 | + except: |
3055 | + pass |
3056 | + if node.hasAttribute ("parent"): |
3057 | + tmp = node.getAttribute ("parent") |
3058 | + if tmp == "None": |
3059 | + self.parent_number = -1 |
3060 | + else: |
3061 | + self.parent_number = int (tmp) |
3062 | + if node.hasAttribute ("child"): |
3063 | + tmp = node.getAttribute ("child") |
3064 | + if tmp == "None": |
3065 | + self.child_number = -1 |
3066 | + else: |
3067 | + self.child_number = int (tmp) |
3068 | + |
3069 | + def process_button_down (self, event, mode, transformed): |
3070 | + modifiers = gtk.accelerator_get_default_mod_mask () |
3071 | + self.button_down = True |
3072 | + if event.button == 1: |
3073 | + if event.type == gtk.gdk.BUTTON_PRESS: |
3074 | + self.emit ("select_link", event.state & modifiers) |
3075 | + self.emit ("update_view") |
3076 | + elif event.button == 3: |
3077 | + self.emit ("popup_requested", event, 2) |
3078 | + self.emit ("update_view") |
3079 | + return False |
3080 | + |
3081 | + def process_button_release (self, event, unending_link, mode, transformed): |
3082 | + return False |
3083 | + |
3084 | + def process_key_press (self, event, mode): |
3085 | + if mode != BaseThought.MODE_EDITING or event.keyval == gtk.keysyms.Delete: |
3086 | + return False |
3087 | + if event.keyval == gtk.keysyms.plus or \ |
3088 | + event.keyval == gtk.keysyms.KP_Add: |
3089 | + self.strength += 1 |
3090 | + elif (event.keyval == gtk.keysyms.minus or \ |
3091 | + event.keyval == gtk.keysyms.KP_Subtract) and \ |
3092 | + self.strength > 1: |
3093 | + self.strength -= 1 |
3094 | + elif event.keyval == gtk.keysyms.Escape: |
3095 | + self.unselect() |
3096 | + self.emit("update_view") |
3097 | + return True |
3098 | + |
3099 | + def handle_motion (self, event, mode, transformed): |
3100 | + pass |
3101 | + |
3102 | + def want_motion (self): |
3103 | + return False |
3104 | + |
3105 | + def select(self): |
3106 | + self.selected = True |
3107 | + |
3108 | + def unselect(self): |
3109 | + self.selected = False |
3110 | + |
3111 | + def move_by (self, x,y): |
3112 | + pass |
3113 | + |
3114 | + def can_be_parent (self): |
3115 | + return False |
3116 | + |
3117 | + def set_color_cb(self, widget): |
3118 | + dialog = gtk.ColorSelectionDialog(_('Choose Color')) |
3119 | + dialog.connect('response', self.color_selection_ok_cb) |
3120 | + self.color_sel = dialog.colorsel |
3121 | + dialog.run() |
3122 | + |
3123 | + def color_selection_ok_cb(self, dialog, response_id): |
3124 | + if response_id == gtk.RESPONSE_OK: |
3125 | + self.color = utils.gtk_to_cairo_color(self.color_sel.get_current_color()) |
3126 | + dialog.destroy() |
3127 | + |
3128 | + def get_popup_menu_items(self): |
3129 | + image = gtk.Image() |
3130 | + image.set_from_stock(gtk.STOCK_COLOR_PICKER, gtk.ICON_SIZE_MENU) |
3131 | + item = gtk.ImageMenuItem(_('Set Color')) |
3132 | + item.set_image(image) |
3133 | + item.connect('activate', self.set_color_cb) |
3134 | + return [item] |
3135 | |
3136 | === added file 'labyrinth_lib/MMapArea.py' |
3137 | --- labyrinth_lib/MMapArea.py 1970-01-01 00:00:00 +0000 |
3138 | +++ labyrinth_lib/MMapArea.py 2012-11-11 04:19:21 +0000 |
3139 | @@ -0,0 +1,1368 @@ |
3140 | +#! /usr/bin/env python |
3141 | +# MMapArea.py |
3142 | +# This file is part of Labyrinth |
3143 | +# |
3144 | +# Copyright (C) 2006 - Don Scorgie <Don@Scorgie.org> |
3145 | +# |
3146 | +# Labyrinth is free software; you can redistribute it and/or modify |
3147 | +# it under the terms of the GNU General Public License as published by |
3148 | +# the Free Software Foundation; either version 2 of the License, or |
3149 | +# (at your option) any later version. |
3150 | +# |
3151 | +# Labyrinth is distributed in the hope that it will be useful, |
3152 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3153 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3154 | +# GNU General Public License for more details. |
3155 | +# |
3156 | +# You should have received a copy of the GNU General Public License |
3157 | +# along with Labyrinth; if not, write to the Free Software |
3158 | +# Foundation, Inc., 51 Franklin St, Fifth Floor, |
3159 | +# Boston, MA 02110-1301 USA |
3160 | +# |
3161 | + |
3162 | +import math |
3163 | + |
3164 | +import gtk |
3165 | +import pango |
3166 | +import gobject |
3167 | +import gettext |
3168 | +import copy |
3169 | +_ = gettext.gettext |
3170 | + |
3171 | +import xml.dom.minidom as dom |
3172 | + |
3173 | +import Links |
3174 | +import TextThought |
3175 | +import ImageThought |
3176 | +import DrawingThought |
3177 | +import ResourceThought |
3178 | +import UndoManager |
3179 | +import utils |
3180 | + |
3181 | +RAD_UP = (- math.pi / 2.) |
3182 | +RAD_DOWN = (math.pi / 2.) |
3183 | +RAD_LEFT = (math.pi) |
3184 | +RAD_RIGHT = (0) |
3185 | + |
3186 | +MODE_EDITING = 0 |
3187 | +MODE_IMAGE = 1 |
3188 | +MODE_DRAW = 2 |
3189 | +MODE_RESOURCE = 3 |
3190 | +# Until all references of MODE_MOVING are removed... |
3191 | +MODE_MOVING = 999 |
3192 | + |
3193 | +VIEW_LINES = 0 |
3194 | +VIEW_BEZIER = 1 |
3195 | + |
3196 | +TYPE_TEXT = 0 |
3197 | +TYPE_IMAGE = 1 |
3198 | +TYPE_DRAWING = 2 |
3199 | +TYPE_RESOURCE = 3 |
3200 | + |
3201 | +# TODO: Need to expand to support popup menus |
3202 | +MENU_EMPTY_SPACE = 0 |
3203 | + |
3204 | +# UNDO actions |
3205 | +UNDO_MOVE = 0 |
3206 | +UNDO_CREATE = 1 |
3207 | +UNDO_DELETE = 2 |
3208 | +UNDO_DELETE_SINGLE = 3 |
3209 | +UNDO_COMBINE_DELETE_NEW = 4 |
3210 | +UNDO_DELETE_LINK = 5 |
3211 | +UNDO_STRENGTHEN_LINK = 6 |
3212 | +UNDO_CREATE_LINK = 7 |
3213 | +UNDO_ALIGN = 8 |
3214 | + |
3215 | +# Note: This is (atm) very broken. It will allow you to create new canvases, but not |
3216 | +# create new thoughts or load existing maps. |
3217 | +# To get it working either fix the TODO list at the bottom of the class, implement the |
3218 | +# necessary features within all the thought types. If you do, please send a patch ;) |
3219 | +# OR: Change this class to MMapAreaNew and MMapAreaOld to MMapArea |
3220 | + |
3221 | +class MMapArea (gtk.DrawingArea): |
3222 | + '''A MindMapArea Widget. A blank canvas with a collection of child thoughts.\ |
3223 | + It is responsible for processing signals and such from the whole area and \ |
3224 | + passing these on to the correct child. It also informs things when to draw''' |
3225 | + |
3226 | + __gsignals__ = dict (title_changed = (gobject.SIGNAL_RUN_FIRST, |
3227 | + gobject.TYPE_NONE, |
3228 | + (gobject.TYPE_STRING, )), |
3229 | + doc_save = (gobject.SIGNAL_RUN_FIRST, |
3230 | + gobject.TYPE_NONE, |
3231 | + (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)), |
3232 | + doc_delete = (gobject.SIGNAL_RUN_FIRST, |
3233 | + gobject.TYPE_NONE, |
3234 | + ()), |
3235 | + change_mode = (gobject.SIGNAL_RUN_LAST, |
3236 | + gobject.TYPE_NONE, |
3237 | + (gobject.TYPE_INT, )), |
3238 | + change_buffer = (gobject.SIGNAL_RUN_LAST, |
3239 | + gobject.TYPE_NONE, |
3240 | + (gobject.TYPE_OBJECT, )), |
3241 | + text_selection_changed = (gobject.SIGNAL_RUN_FIRST, |
3242 | + gobject.TYPE_NONE, |
3243 | + (gobject.TYPE_INT, gobject.TYPE_INT, gobject.TYPE_STRING)), |
3244 | + thought_selection_changed = (gobject.SIGNAL_RUN_FIRST, |
3245 | + gobject.TYPE_NONE, |
3246 | + (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)), |
3247 | + set_focus = (gobject.SIGNAL_RUN_FIRST, |
3248 | + gobject.TYPE_NONE, |
3249 | + (gobject.TYPE_PYOBJECT, gobject.TYPE_BOOLEAN)), |
3250 | + set_attrs = (gobject.SIGNAL_RUN_LAST, |
3251 | + gobject.TYPE_NONE, |
3252 | + (gobject.TYPE_BOOLEAN, gobject.TYPE_BOOLEAN, gobject.TYPE_BOOLEAN, pango.FontDescription))) |
3253 | + |
3254 | + def __init__(self, undo): |
3255 | + super (MMapArea, self).__init__() |
3256 | + |
3257 | + self.thoughts = [] |
3258 | + self.links = [] |
3259 | + self.selected = [] |
3260 | + self.num_selected = 0 |
3261 | + self.primary = None |
3262 | + self.editing = None |
3263 | + self.pango_context = self.create_pango_context() |
3264 | + self.undo = undo |
3265 | + self.scale_fac = 1.0 |
3266 | + self.translate = False |
3267 | + self.translation = [0.0,0.0] |
3268 | + self.timeout = -1 |
3269 | + self.current_cursor = None |
3270 | + self.do_filter = True |
3271 | + self.is_bbox_selecting = False |
3272 | + |
3273 | + self.unending_link = None |
3274 | + self.nthoughts = 0 |
3275 | + |
3276 | + impl = dom.getDOMImplementation() |
3277 | + self.save = impl.createDocument("http://www.donscorgie.blueyonder.co.uk/labns", "MMap", None) |
3278 | + self.element = self.save.documentElement |
3279 | + self.im_context = gtk.IMMulticontext () |
3280 | + |
3281 | + self.mode = MODE_EDITING |
3282 | + self.old_mode = MODE_EDITING |
3283 | + |
3284 | + self.connect ("expose_event", self.expose) |
3285 | + self.connect ("button_release_event", self.button_release) |
3286 | + self.connect ("button_press_event", self.button_down) |
3287 | + self.connect ("motion_notify_event", self.motion_event) |
3288 | + self.connect ("key_press_event", self.key_press) |
3289 | + self.connect ("key_release_event", self.key_release) |
3290 | + self.connect ("scroll_event", self.scroll) |
3291 | + self.commit_handler = None |
3292 | + self.title_change_handler = None |
3293 | + self.moving = False |
3294 | + self.move_origin = None |
3295 | + self.move_origin_new = None |
3296 | + self.motion = None |
3297 | + self.move_action = None |
3298 | + self.current_root = [] |
3299 | + self.rotation = 0 |
3300 | + |
3301 | + self.set_events (gtk.gdk.KEY_PRESS_MASK | |
3302 | + gtk.gdk.KEY_RELEASE_MASK | |
3303 | + gtk.gdk.BUTTON_PRESS_MASK | |
3304 | + gtk.gdk.BUTTON_RELEASE_MASK | |
3305 | + gtk.gdk.POINTER_MOTION_MASK | |
3306 | + gtk.gdk.SCROLL_MASK) |
3307 | + |
3308 | + self.set_flags (gtk.CAN_FOCUS) |
3309 | + |
3310 | + # set theme colors |
3311 | + w = gtk.Window() |
3312 | + w.realize() |
3313 | + style = w.get_style() |
3314 | + self.pango_context.set_font_description(style.font_desc) |
3315 | + self.font_name = style.font_desc.to_string() |
3316 | + utils.default_font = self.font_name |
3317 | + |
3318 | + utils.default_colors["text"] = utils.gtk_to_cairo_color(style.text[gtk.STATE_NORMAL]) |
3319 | + utils.default_colors["base"] = utils.gtk_to_cairo_color(style.base[gtk.STATE_NORMAL]) |
3320 | + self.background_color = style.base[gtk.STATE_NORMAL] |
3321 | + self.foreground_color = style.text[gtk.STATE_NORMAL] |
3322 | + utils.default_colors["bg"] = utils.gtk_to_cairo_color(style.bg[gtk.STATE_NORMAL]) |
3323 | + utils.default_colors["fg"] = utils.gtk_to_cairo_color(style.fg[gtk.STATE_NORMAL]) |
3324 | + |
3325 | + utils.selected_colors["text"] = utils.gtk_to_cairo_color(style.text[gtk.STATE_SELECTED]) |
3326 | + utils.selected_colors["bg"] = utils.gtk_to_cairo_color(style.bg[gtk.STATE_SELECTED]) |
3327 | + utils.selected_colors["fg"] = utils.gtk_to_cairo_color(style.fg[gtk.STATE_SELECTED]) |
3328 | + utils.selected_colors["fill"] = utils.gtk_to_cairo_color(style.base[gtk.STATE_SELECTED]) |
3329 | + |
3330 | + def transform_coords(self, loc_x, loc_y): |
3331 | + if hasattr(self, "transform"): |
3332 | + return self.transform.transform_point(loc_x, loc_y) |
3333 | + |
3334 | + def untransform_coords(self, loc_x, loc_y): |
3335 | + if hasattr(self, "untransform"): |
3336 | + return self.untransform.transform_point(loc_x, loc_y) |
3337 | + |
3338 | + def button_down (self, widget, event): |
3339 | + coords = self.transform_coords (event.get_coords()[0], event.get_coords()[1]) |
3340 | + |
3341 | + ret = False |
3342 | + obj = self.find_object_at (coords) |
3343 | + if event.button == 2: |
3344 | + self.set_cursor (gtk.gdk.FLEUR) |
3345 | + self.original_translation = self.translation |
3346 | + self.origin_x = event.x |
3347 | + self.origin_y = event.y |
3348 | + return |
3349 | + if obj and obj.want_motion (): |
3350 | + self.motion = obj |
3351 | + ret = obj.process_button_down (event, self.mode, coords) |
3352 | + if event.button == 1 and self.mode == MODE_EDITING: |
3353 | + self.moving = not (event.state & gtk.gdk.CONTROL_MASK) |
3354 | + self.move_origin = (coords[0], coords[1]) |
3355 | + self.move_origin_new = self.move_origin |
3356 | + return ret |
3357 | + if obj: |
3358 | + if event.button == 1 and self.mode == MODE_EDITING: |
3359 | + self.moving = not (event.state & gtk.gdk.CONTROL_MASK) |
3360 | + self.move_origin = (coords[0], coords[1]) |
3361 | + self.move_origin_new = self.move_origin |
3362 | + ret = obj.process_button_down (event, self.mode, coords) |
3363 | + elif event.button == 1 and self.mode == MODE_EDITING and not self.editing: |
3364 | + self.bbox_origin = coords |
3365 | + self.is_bbox_selecting = True |
3366 | + elif event.button == 3: |
3367 | + ret = self.create_popup_menu (None, event, MENU_EMPTY_SPACE) |
3368 | + return ret |
3369 | + |
3370 | + def undo_move (self, action, mode): |
3371 | + self.undo.block () |
3372 | + move_thoughts = action.args[1] |
3373 | + old_coords = action.args[0] |
3374 | + new_coords = action.args[2] |
3375 | + move_x = old_coords[0] - new_coords[0] |
3376 | + move_y = old_coords[1] - new_coords[1] |
3377 | + if mode == UndoManager.REDO: |
3378 | + move_x = -move_x |
3379 | + move_y = -move_y |
3380 | + self.unselect_all () |
3381 | + for t in move_thoughts: |
3382 | + self.select_thought (t, -1) |
3383 | + t.move_by (move_x, move_y) |
3384 | + self.undo.unblock () |
3385 | + self.invalidate ((old_coords[0], old_coords[1], new_coords[0], new_coords[1])) |
3386 | + |
3387 | + def button_release (self, widget, event): |
3388 | + coords = self.transform_coords (event.get_coords()[0], event.get_coords()[1]) |
3389 | + |
3390 | + if self.mode == MODE_EDITING: |
3391 | + self.set_cursor(gtk.gdk.LEFT_PTR) |
3392 | + else: |
3393 | + self.set_cursor(gtk.gdk.CROSSHAIR) |
3394 | + |
3395 | + ret = False |
3396 | + if self.is_bbox_selecting: |
3397 | + self.is_bbox_selecting = False |
3398 | + self.invalidate () |
3399 | + try: |
3400 | + if abs(self.bbox_origin[0] - coords[0]) > 2.0: |
3401 | + return True |
3402 | + except AttributeError: # no bbox_current |
3403 | + pass |
3404 | + |
3405 | + if self.translate: |
3406 | + self.translate = False |
3407 | + return True |
3408 | + if self.moving and self.move_action: |
3409 | + self.move_action.add_arg (coords) |
3410 | + self.undo.add_undo (self.move_action) |
3411 | + self.move_action = None |
3412 | + |
3413 | + self.motion = None |
3414 | + self.moving = False |
3415 | + self.move_origin = None |
3416 | + |
3417 | + obj = self.find_object_at (coords) |
3418 | + if event.button == 2: |
3419 | + self.undo.add_undo (UndoManager.UndoAction (self, UndoManager.TRANSFORM_CANVAS, |
3420 | + self.undo_transform_cb, |
3421 | + self.scale_fac, self.scale_fac, |
3422 | + self.original_translation, |
3423 | + self.translation)) |
3424 | + |
3425 | + if obj: |
3426 | + ret = obj.process_button_release (event, self.unending_link, self.mode, coords) |
3427 | + if len(self.selected) != 1: |
3428 | + self.invalidate() # does not invalidate correctly with obj.get_max_area() |
3429 | + return ret |
3430 | + elif self.unending_link or event.button == 1: |
3431 | + sel = self.selected |
3432 | + thought = self.create_new_thought (coords) |
3433 | + if not thought: |
3434 | + return True |
3435 | + if not self.primary: |
3436 | + self.make_primary (thought) |
3437 | + self.select_thought (thought, None) |
3438 | + else: |
3439 | + self.emit ("change_buffer", thought.extended_buffer) |
3440 | + self.hookup_im_context (thought) |
3441 | + # Creating links adds an undo action. Block it here |
3442 | + self.undo.block () |
3443 | + for x in self.current_root: |
3444 | + self.create_link (x, None, thought) |
3445 | + for x in self.selected: |
3446 | + x.unselect () |
3447 | + self.selected = [thought] |
3448 | + thought.select () |
3449 | + |
3450 | + if self.unending_link: |
3451 | + self.unending_link.set_child (thought) |
3452 | + self.links.append (self.unending_link) |
3453 | + element = self.unending_link.get_save_element () |
3454 | + self.element.appendChild (element) |
3455 | + self.unending_link = None |
3456 | + |
3457 | + self.undo.unblock () |
3458 | + thought.foreground_color = self.foreground_color |
3459 | + thought.background_color = self.background_color |
3460 | + act = UndoManager.UndoAction (self, UNDO_CREATE, self.undo_create_cb, thought, sel, \ |
3461 | + self.mode, self.old_mode, event.get_coords()) |
3462 | + for l in self.links: |
3463 | + if l.uses (thought): |
3464 | + act.add_arg (l) |
3465 | + if self.undo.peak ().undo_type == UNDO_DELETE_SINGLE: |
3466 | + last_action = self.undo.pop () |
3467 | + action = UndoManager.UndoAction (self, UNDO_COMBINE_DELETE_NEW, self.undo_joint_cb, \ |
3468 | + last_action, act) |
3469 | + self.undo.add_undo (action) |
3470 | + else: |
3471 | + self.undo.add_undo (act) |
3472 | + self.begin_editing (thought) |
3473 | + |
3474 | + self.invalidate () |
3475 | + return ret |
3476 | + |
3477 | + def undo_transform_cb (self, action, mode): |
3478 | + if mode == UndoManager.UNDO: |
3479 | + self.scale_fac = action.args[0] |
3480 | + self.translation = action.args[2] |
3481 | + else: |
3482 | + self.scale_fac = action.args[1] |
3483 | + self.translation = action.args[3] |
3484 | + self.invalidate () |
3485 | + |
3486 | + def scroll (self, widget, event): |
3487 | + scale = self.scale_fac |
3488 | + if event.direction == gtk.gdk.SCROLL_UP: |
3489 | + if self.scale_fac > 10: |
3490 | + return # Limit zoom in to 10x |
3491 | + self.scale_fac*=1.2 |
3492 | + |
3493 | + # Zoom based on where cursor is located. |
3494 | + coords = self.transform_coords(event.x, event.y) |
3495 | + geom = self.window.get_geometry() |
3496 | + middle = self.transform_coords(geom[2]/2.0, geom[3]/2.0) |
3497 | + |
3498 | + # Without the /4.0, the window jumps to where the cursor is |
3499 | + # centred, which is very awkward and hard to use. This method makes |
3500 | + # the centre move smoothly towards the cursor's location. |
3501 | + self.translation[0] -= (coords[0] - middle[0])/4.0 |
3502 | + self.translation[1] -= (coords[1] - middle[1])/4.0 |
3503 | + elif event.direction == gtk.gdk.SCROLL_DOWN: |
3504 | + if self.scale_fac <= 0.1: |
3505 | + return # Limit zoom out to 1/10th scale |
3506 | + self.scale_fac/=1.2 |
3507 | + |
3508 | + self.undo.add_undo (UndoManager.UndoAction (self, UndoManager.TRANSFORM_CANVAS, \ |
3509 | + self.undo_transform_cb, |
3510 | + scale, self.scale_fac, self.translation, |
3511 | + self.translation)) |
3512 | + self.invalidate() |
3513 | + |
3514 | + def undo_joint_cb (self, action, mode): |
3515 | + delete = action.args[0] |
3516 | + create = action.args[1] |
3517 | + |
3518 | + if mode == UndoManager.UNDO: |
3519 | + self.undo_create_cb (create, mode) |
3520 | + self.undo_deletion (delete, mode) |
3521 | + else: |
3522 | + self.undo_deletion (delete, mode) |
3523 | + self.undo_create_cb (create, mode) |
3524 | + self.invalidate () |
3525 | + |
3526 | + def key_press (self, widget, event): |
3527 | + if not self.do_filter or not self.im_context.filter_keypress (event): |
3528 | + if self.editing: |
3529 | + if not self.editing.process_key_press (event, self.mode): |
3530 | + return self.global_key_handler (event) |
3531 | + return True |
3532 | + if len(self.selected) != 1 or not self.selected[0].process_key_press (event, self.mode): |
3533 | + return self.global_key_handler (event) |
3534 | + return True |
3535 | + |
3536 | + def key_release (self, widget, event): |
3537 | + self.im_context.filter_keypress (event) |
3538 | + return True |
3539 | + |
3540 | + def motion_event(self, widget, event): |
3541 | + coords = self.transform_coords (event.get_coords()[0], event.get_coords()[1]) |
3542 | + |
3543 | + if self.motion: |
3544 | + if self.motion.handle_motion (event, self.mode, coords): |
3545 | + return True |
3546 | + obj = self.find_object_at (coords) |
3547 | + if self.unending_link and not self.is_bbox_selecting: |
3548 | + self.unending_link.set_end (coords) |
3549 | + self.invalidate () |
3550 | + return True |
3551 | + elif event.state & gtk.gdk.BUTTON1_MASK and self.is_bbox_selecting: |
3552 | + self.bbox_current = coords |
3553 | + self.invalidate() |
3554 | + |
3555 | + ul = [ self.bbox_origin[0], self.bbox_origin[1] ] |
3556 | + lr = [ coords[0], coords[1] ] |
3557 | + if self.bbox_origin[0] > coords[0]: |
3558 | + if self.bbox_origin[1] < coords[1]: |
3559 | + ul[0] = coords[0] |
3560 | + ul[1] = self.bbox_origin[1] |
3561 | + lr[0] = self.bbox_origin[0] |
3562 | + lr[1] = coords[1] |
3563 | + else: |
3564 | + ul = coords |
3565 | + lr = self.bbox_origin |
3566 | + elif self.bbox_origin[1] > coords[1]: |
3567 | + ul[0] = self.bbox_origin[0] |
3568 | + ul[1] = coords[1] |
3569 | + lr[0] = coords[0] |
3570 | + lr[1] = self.bbox_origin[1] |
3571 | + |
3572 | + # FIXME: O(n) runtime is bad |
3573 | + for t in self.thoughts: |
3574 | + if t.lr[0] > ul[0] and t.ul[1] < lr[1] and t.ul[0] < lr[0] and t.lr[1] > ul[1] : |
3575 | + if t not in self.selected: |
3576 | + self.select_thought(t, gtk.gdk.SHIFT_MASK) |
3577 | + else: |
3578 | + if t in self.selected: |
3579 | + t.unselect() |
3580 | + self.selected.remove(t) |
3581 | + |
3582 | + return True |
3583 | + elif self.moving and not self.editing and not self.unending_link: |
3584 | + self.set_cursor(gtk.gdk.FLEUR) |
3585 | + if not self.move_action: |
3586 | + self.move_action = UndoManager.UndoAction (self, UNDO_MOVE, self.undo_move, self.move_origin, |
3587 | + self.selected) |
3588 | + for t in self.selected: |
3589 | + t.move_by (coords[0] - self.move_origin_new[0], coords[1] - self.move_origin_new[1]) |
3590 | + self.move_origin_new = (coords[0], coords[1]) |
3591 | + self.invalidate () |
3592 | + return True |
3593 | + elif self.editing and event.state & gtk.gdk.BUTTON1_MASK and not obj and not self.is_bbox_selecting: |
3594 | + # We were too quick with the movement. We really actually want to |
3595 | + # create the unending link |
3596 | + self.create_link (self.editing) |
3597 | + self.finish_editing () |
3598 | + elif event.state & gtk.gdk.BUTTON2_MASK: |
3599 | + self.translate = True |
3600 | + self.translation[0] -= (self.origin_x - event.x) / self.scale_fac |
3601 | + self.translation[1] -= (self.origin_y - event.y) / self.scale_fac |
3602 | + self.origin_x = event.x |
3603 | + self.origin_y = event.y |
3604 | + self.invalidate() |
3605 | + return True |
3606 | + |
3607 | + if obj: |
3608 | + obj.handle_motion (event, self.mode, coords) |
3609 | + elif self.mode == MODE_IMAGE or self.mode == MODE_DRAW: |
3610 | + self.set_cursor(gtk.gdk.CROSSHAIR) |
3611 | + else: |
3612 | + self.set_cursor(gtk.gdk.LEFT_PTR) |
3613 | + |
3614 | + def find_object_at (self, coords): |
3615 | + for x in reversed(self.thoughts): |
3616 | + if x.includes (coords, self.mode): |
3617 | + return x |
3618 | + for x in self.links: |
3619 | + if x.includes (coords, self.mode): |
3620 | + return x |
3621 | + return None |
3622 | + |
3623 | + def realize_cb (self, widget): |
3624 | + self.disconnect (self.realize_handle) |
3625 | + if self.mode == MODE_IMAGE or self.mode == MODE_DRAW: |
3626 | + self.set_cursor (gtk.gdk.CROSSHAIR) |
3627 | + else: |
3628 | + self.set_cursor (gtk.gdk.LEFT_PTR) |
3629 | + return False |
3630 | + |
3631 | + def set_cursor(self, kind): |
3632 | + new_cursor = CursorFactory().get_cursor(kind) |
3633 | + if self.current_cursor != new_cursor: |
3634 | + self.current_cursor = new_cursor |
3635 | + self.window.set_cursor(self.current_cursor) |
3636 | + |
3637 | + def set_mode (self, mode): |
3638 | + if mode == self.mode: |
3639 | + return |
3640 | + self.old_mode = self.mode |
3641 | + self.mode = mode |
3642 | + self.finish_editing () |
3643 | + self.hookup_im_context () |
3644 | + |
3645 | + if self.window: |
3646 | + if mode == MODE_IMAGE or mode == MODE_DRAW: |
3647 | + self.set_cursor (gtk.gdk.CROSSHAIR) |
3648 | + else: |
3649 | + self.set_cursor (gtk.gdk.LEFT_PTR) |
3650 | + else: |
3651 | + self.realize_handle = self.connect ("realize", self.realize_cb) |
3652 | + self.mode = mode |
3653 | + if self.window: |
3654 | + self.invalidate () |
3655 | + |
3656 | + def title_changed_cb (self, widget, new_title): |
3657 | + self.emit ("title_changed", new_title) |
3658 | + |
3659 | + def make_primary (self, thought): |
3660 | + if self.primary: |
3661 | + print "Warning: Already have a primary root" |
3662 | + if self.title_change_handler: |
3663 | + self.primary.disconnect (self.title_change_handler) |
3664 | + self.title_change_handler = thought.connect ("title_changed", self.title_changed_cb) |
3665 | + self.emit ("title_changed", thought.text) |
3666 | + self.primary = thought |
3667 | + thought.make_primary () |
3668 | + |
3669 | + def hookup_im_context (self, thought = None): |
3670 | + if self.commit_handler: |
3671 | + self.im_context.disconnect (self.commit_handler) |
3672 | + self.im_context.disconnect (self.delete_handler) |
3673 | + self.im_context.disconnect (self.preedit_changed_handler) |
3674 | + self.im_context.disconnect (self.preedit_end_handler) |
3675 | + self.im_context.disconnect (self.preedit_start_handler) |
3676 | + self.im_context.disconnect (self.retrieve_handler) |
3677 | + self.commit_handler = None |
3678 | + if thought: |
3679 | + try: |
3680 | + self.commit_handler = self.im_context.connect ("commit", thought.commit_text, self.mode, self.font_name) |
3681 | + self.delete_handler = self.im_context.connect ("delete-surrounding", thought.delete_surroundings, self.mode) |
3682 | + self.preedit_changed_handler = self.im_context.connect ("preedit-changed", thought.preedit_changed, self.mode) |
3683 | + self.preedit_end_handler = self.im_context.connect ("preedit-end", thought.preedit_end, self.mode) |
3684 | + self.preedit_start_handler = self.im_context.connect ("preedit-start", thought.preedit_start, self.mode) |
3685 | + self.retrieve_handler = self.im_context.connect ("retrieve-surrounding", thought.retrieve_surroundings, \ |
3686 | + self.mode) |
3687 | + self.do_filter = True |
3688 | + except AttributeError: |
3689 | + self.do_filter = False |
3690 | + else: |
3691 | + self.do_filter = False |
3692 | + |
3693 | + def unselect_all (self): |
3694 | + self.hookup_im_context () |
3695 | + for t in self.selected: |
3696 | + t.unselect () |
3697 | + self.selected = [] |
3698 | + |
3699 | + def select_link (self, link, modifiers): |
3700 | + if modifiers and modifiers & gtk.gdk.SHIFT_MASK and len (self.selected) > 1 and self.selected.count (link) > 0: |
3701 | + self.selected.remove (link) |
3702 | + link.unselect () |
3703 | + return |
3704 | + |
3705 | + self.hookup_im_context() |
3706 | + if self.editing: |
3707 | + self.finish_editing () |
3708 | + if modifiers and (modifiers & gtk.gdk.SHIFT_MASK or modifiers == -1): |
3709 | + if self.selected.count (link) == 0: |
3710 | + self.selected.append (link) |
3711 | + else: |
3712 | + for t in self.selected: |
3713 | + t.unselect() |
3714 | + self.selected = [link] |
3715 | + link.select() |
3716 | + self.emit("change_buffer", None) |
3717 | + |
3718 | + def select_thought (self, thought, modifiers): |
3719 | + self.hookup_im_context () |
3720 | + if self.editing: |
3721 | + self.finish_editing () |
3722 | + |
3723 | + if thought in self.selected and self.moving: |
3724 | + return |
3725 | + |
3726 | + if thought not in self.thoughts: |
3727 | + self.thoughts.append(thought) |
3728 | + |
3729 | + if modifiers and (modifiers & gtk.gdk.SHIFT_MASK or modifiers == -1): |
3730 | + if self.selected.count (thought) == 0: |
3731 | + self.selected.append (thought) |
3732 | + else: |
3733 | + for x in self.selected: |
3734 | + x.unselect() |
3735 | + self.selected = [thought] |
3736 | + self.current_root = [] |
3737 | + for x in self.selected: |
3738 | + if x.can_be_parent(): |
3739 | + self.current_root.append(x) |
3740 | + thought.select () |
3741 | + if len(self.selected) == 1: |
3742 | + self.emit ("thought_selection_changed", thought.background_color, thought.foreground_color) |
3743 | + self.background_color = thought.background_color |
3744 | + # Image thoughts don't have a foreground colour, so we shouldn't |
3745 | + # copy it. |
3746 | + if thought.foreground_color is not None: |
3747 | + self.foreground_color = thought.foreground_color |
3748 | + try: |
3749 | + self.emit ("change_buffer", thought.extended_buffer) |
3750 | + except AttributeError: |
3751 | + self.emit ("change_buffer", None) |
3752 | + self.hookup_im_context (thought) |
3753 | + else: |
3754 | + self.emit ("change_buffer", None) |
3755 | + |
3756 | + def begin_editing (self, thought): |
3757 | + if self.editing and thought != self.editing: |
3758 | + self.finish_editing () |
3759 | + if thought.begin_editing (): |
3760 | + self.editing = thought |
3761 | + self.invalidate() |
3762 | + |
3763 | + def undo_link_action (self, action, mode): |
3764 | + self.undo.block () |
3765 | + if self.editing: |
3766 | + self.finish_editing () |
3767 | + link = action.args[0] |
3768 | + if action.undo_type == UNDO_CREATE_LINK: |
3769 | + if mode == UndoManager.REDO: |
3770 | + self.element.appendChild (link.element) |
3771 | + self.links.append (link) |
3772 | + else: |
3773 | + self.delete_link (link) |
3774 | + elif action.undo_type == UNDO_DELETE_LINK: |
3775 | + if mode == UndoManager.UNDO: |
3776 | + self.element.appendChild (link.element) |
3777 | + self.links.append (link) |
3778 | + else: |
3779 | + self.delete_link (link) |
3780 | + elif action.undo_type == UNDO_STRENGTHEN_LINK: |
3781 | + if mode == UndoManager.UNDO: |
3782 | + link.set_strength (action.args[1]) |
3783 | + else: |
3784 | + link.set_strength (action.args[2]) |
3785 | + |
3786 | + self.undo.unblock () |
3787 | + self.invalidate () |
3788 | + |
3789 | + def connect_link (self, link): |
3790 | + link.connect ("select_link", self.select_link) |
3791 | + link.connect ("update_view", self.update_view) |
3792 | + link.connect ("popup_requested", self.create_popup_menu) |
3793 | + |
3794 | + def create_link (self, thought, thought_coords = None, child = None, child_coords = None, strength = 2): |
3795 | + if child: |
3796 | + for x in self.links: |
3797 | + if x.connects (thought, child): |
3798 | + if x.change_strength (thought, child): |
3799 | + self.delete_link (x) |
3800 | + return |
3801 | + link = Links.Link (self.save, parent = thought, child = child, strength = strength) |
3802 | + self.connect_link (link) |
3803 | + element = link.get_save_element () |
3804 | + self.element.appendChild (element) |
3805 | + self.links.append (link) |
3806 | + return link |
3807 | + else: |
3808 | + if self.unending_link: |
3809 | + del self.unending_link |
3810 | + self.unending_link = Links.Link (self.save, parent = thought, start_coords = thought_coords, |
3811 | + end_coords = child_coords, strength = strength) |
3812 | + |
3813 | + def set_mouse_cursor_cb (self, thought, cursor_type): |
3814 | + if not self.moving: |
3815 | + self.set_cursor (cursor_type) |
3816 | + |
3817 | + def update_all_links(self): |
3818 | + for l in self.links: |
3819 | + l.find_ends() |
3820 | + |
3821 | + def update_links_cb (self, thought): |
3822 | + for x in self.links: |
3823 | + if x.uses (thought): |
3824 | + x.find_ends () |
3825 | + |
3826 | + def claim_unending_link (self, thought): |
3827 | + if not self.unending_link: |
3828 | + return |
3829 | + |
3830 | + if self.unending_link.parent == thought: |
3831 | + del self.unending_link |
3832 | + self.unending_link = None |
3833 | + return |
3834 | + for x in self.links: |
3835 | + if x.connects (self.unending_link.parent, thought): |
3836 | + old_strength = x.strength |
3837 | + x.change_strength (self.unending_link.parent, thought) |
3838 | + new_strength = x.strength |
3839 | + self.undo.add_undo (UndoManager.UndoAction (self, UNDO_STRENGTHEN_LINK, self.undo_link_action, x, \ |
3840 | + old_strength, new_strength)) |
3841 | + del self.unending_link |
3842 | + self.unending_link = None |
3843 | + return |
3844 | + |
3845 | + self.undo.add_undo (UndoManager.UndoAction (self, UNDO_CREATE_LINK, self.undo_link_action, self.unending_link)) |
3846 | + self.unending_link.set_child (thought) |
3847 | + self.links.append (self.unending_link) |
3848 | + self.connect_link(self.unending_link) |
3849 | + element = self.unending_link.get_save_element () |
3850 | + self.element.appendChild (element) |
3851 | + self.unending_link = None |
3852 | + |
3853 | + def create_popup_menu (self, thought, event, menu_type): |
3854 | + menu = gtk.Menu() |
3855 | + undo_item = gtk.ImageMenuItem(gtk.STOCK_UNDO) |
3856 | + undo_item.connect('activate', self.undo.undo_action) |
3857 | + undo_item.set_sensitive(self.undo.exists_undo_action()) |
3858 | + redo_item = gtk.ImageMenuItem(gtk.STOCK_REDO) |
3859 | + redo_item.connect('activate', self.undo.redo_action) |
3860 | + redo_item.set_sensitive(self.undo.exists_redo_action()) |
3861 | + sep_item = gtk.SeparatorMenuItem() |
3862 | + menu.append(undo_item) |
3863 | + menu.append(redo_item) |
3864 | + menu.append(sep_item) |
3865 | + undo_item.show() |
3866 | + redo_item.show() |
3867 | + sep_item.show() |
3868 | + |
3869 | + if thought: |
3870 | + for item in thought.get_popup_menu_items(): |
3871 | + menu.append(item) |
3872 | + item.show() |
3873 | + |
3874 | + menu.popup(None, None, None, event.button, event.get_time()) |
3875 | + |
3876 | + def finish_editing (self, thought = None): |
3877 | + if not self.editing or (thought and thought != self.editing): |
3878 | + return |
3879 | + |
3880 | + self.editing.finish_editing () |
3881 | + thought = self.editing |
3882 | + self.editing = None |
3883 | + |
3884 | + def update_view (self, thought): |
3885 | + if not self.editing: |
3886 | + self.invalidate () |
3887 | + else: |
3888 | + x,y,w,h = thought.get_max_area() |
3889 | + w += 10 |
3890 | + h += 10 |
3891 | + self.invalidate ((x,y,w,h)) |
3892 | + |
3893 | + def invalidate (self, transformed_area = None): |
3894 | + '''Helper function to invalidate the entire screen, forcing a redraw''' |
3895 | + rect = None |
3896 | + if not transformed_area: |
3897 | + alloc = self.get_allocation () |
3898 | + rect = gtk.gdk.Rectangle (0, 0, alloc.width, alloc.height) |
3899 | + else: |
3900 | + ul = self.untransform_coords(transformed_area[0], transformed_area[1]) |
3901 | + lr = self.untransform_coords(transformed_area[2], transformed_area[3]) |
3902 | + rect = gtk.gdk.Rectangle (int(ul[0]), int(ul[1]), int(lr[0]-ul[0]), int(lr[1]-ul[1])) |
3903 | + if self.window: |
3904 | + self.window.invalidate_rect (rect, True) |
3905 | + |
3906 | + def expose (self, widget, event): |
3907 | + '''Expose event. Calls the draw function''' |
3908 | + context = self.window.cairo_create () |
3909 | + self.draw (event, context) |
3910 | + return False |
3911 | + |
3912 | + def draw (self, event, context): |
3913 | + '''Draw the map and all the associated thoughts''' |
3914 | + area = event.area |
3915 | + context.rectangle (area.x, area.y, area.width, area.height) |
3916 | + context.clip () |
3917 | + context.set_source_rgb (1.0,1.0,1.0) |
3918 | + context.move_to (area.x, area.y) |
3919 | + context.paint () |
3920 | + context.set_source_rgb (0.0,0.0,0.0) |
3921 | + |
3922 | + alloc = self.get_allocation () |
3923 | + context.translate(alloc.width/2., alloc.height/2.) |
3924 | + context.scale(self.scale_fac, self.scale_fac) |
3925 | + context.translate(-alloc.width/2., -alloc.height/2.) |
3926 | + context.translate(self.translation[0], self.translation[1]) |
3927 | + |
3928 | + for l in self.links: |
3929 | + l.draw (context) |
3930 | + |
3931 | + if self.unending_link: |
3932 | + self.unending_link.draw (context) |
3933 | + |
3934 | + self.untransform = context.get_matrix() |
3935 | + self.transform = context.get_matrix() |
3936 | + self.transform.invert() |
3937 | + |
3938 | + ax, ay = self.transform_coords(area.x, area.y) |
3939 | + width = area.width / self.scale_fac |
3940 | + height = area.height / self.scale_fac |
3941 | + for t in self.thoughts: |
3942 | + try: |
3943 | + if max(t.ul[0],ax)<=min(t.lr[0],ax+width) and max(t.ul[1],ay)<=min(t.lr[1],ay+height): |
3944 | + t.draw (context) |
3945 | + except: |
3946 | + t.draw(context) |
3947 | + |
3948 | + if self.is_bbox_selecting: |
3949 | + xs = self.bbox_origin[0] |
3950 | + ys = self.bbox_origin[1] |
3951 | + xe = self.bbox_current[0] - xs |
3952 | + ye = self.bbox_current[1] - ys |
3953 | + |
3954 | + xs,ys = context.user_to_device(xs, ys) |
3955 | + xe,ye = context.user_to_device_distance(xe, ye) |
3956 | + xs = int(xs) + 0.5 |
3957 | + ys = int(ys) + 0.5 |
3958 | + xe = int(xe) |
3959 | + ye = int(ye) |
3960 | + xs,ys = context.device_to_user(xs, ys) |
3961 | + xe,ye = context.device_to_user_distance(xe, ye) |
3962 | + |
3963 | + color = utils.selected_colors["border"] |
3964 | + context.set_line_width(1.0) |
3965 | + context.set_source_rgb(color[0], color[1], color[2]) |
3966 | + context.rectangle(xs, ys, xe, ye) |
3967 | + context.stroke() |
3968 | + |
3969 | + color = utils.selected_colors["fill"] |
3970 | + context.set_source_rgba(color[0], color[1], color[2], 0.3) |
3971 | + context.rectangle(xs, ys, xe, ye) |
3972 | + context.fill() |
3973 | + context.set_line_width(2.0) |
3974 | + context.set_source_rgba(0.0, 0.0, 0.0, 1.0) |
3975 | + |
3976 | + def undo_create_cb (self, action, mode): |
3977 | + self.undo.block () |
3978 | + if mode == UndoManager.UNDO: |
3979 | + if action.args[0] == self.editing: |
3980 | + self.editing = None |
3981 | + self.unselect_all () |
3982 | + for t in action.args[1]: |
3983 | + self.select_thought (t, -1) |
3984 | + self.delete_thought (action.args[0]) |
3985 | + self.emit ("change_mode", action.args[3]) |
3986 | + else: |
3987 | + self.emit ("change_mode", action.args[2]) |
3988 | + thought = action.args[0] |
3989 | + self.thoughts.append (thought) |
3990 | + for t in action.args[1]: |
3991 | + self.unselect_all () |
3992 | + self.select_thought (t, -1) |
3993 | + self.hookup_im_context (thought) |
3994 | + self.emit ("change_buffer", thought.extended_buffer) |
3995 | + self.element.appendChild (thought.element) |
3996 | + for l in action.args[5:]: |
3997 | + self.links.append (l) |
3998 | + self.element.appendChild (l.element) |
3999 | + |
4000 | + self.begin_editing (thought) |
4001 | + self.emit ("set_focus", None, False) |
4002 | + self.undo.unblock () |
4003 | + self.invalidate () |
4004 | + |
4005 | + def create_new_thought (self, coords, thought_type = None, loading = False): |
4006 | + if self.editing: |
4007 | + self.editing.finish_editing () |
4008 | + if thought_type != None: |
4009 | + type = thought_type |
4010 | + else: |
4011 | + type = self.mode |
4012 | + |
4013 | + if type == TYPE_TEXT: |
4014 | + thought = TextThought.TextThought (coords, self.pango_context, self.nthoughts, self.save, self.undo, loading, self.background_color, self.foreground_color) |
4015 | + elif type == TYPE_IMAGE: |
4016 | + thought = ImageThought.ImageThought (coords, self.pango_context, self.nthoughts, self.save, self.undo, loading, self.background_color) |
4017 | + elif type == TYPE_DRAWING: |
4018 | + thought = DrawingThought.DrawingThought (coords, self.pango_context, self.nthoughts, self.save, self.undo, \ |
4019 | + loading,self.background_color, self.foreground_color) |
4020 | + elif type == TYPE_RESOURCE: |
4021 | + thought = ResourceThought.ResourceThought (coords, self.pango_context, self.nthoughts, self.save, self.undo, loading, self.background_color, self.foreground_color) |
4022 | + if not thought.okay (): |
4023 | + return None |
4024 | + |
4025 | + if type == TYPE_IMAGE: |
4026 | + self.emit ("change_mode", self.old_mode) |
4027 | + self.nthoughts += 1 |
4028 | + self.element.appendChild (thought.element) |
4029 | + thought.connect ("select_thought", self.select_thought) |
4030 | + thought.connect ("begin_editing", self.begin_editing) |
4031 | + thought.connect ("popup_requested", self.create_popup_menu) |
4032 | + thought.connect ("create_link", self.create_link) |
4033 | + thought.connect ("claim_unending_link", self.claim_unending_link) |
4034 | + thought.connect ("update_view", self.update_view) |
4035 | + thought.connect ("finish_editing", self.finish_editing) |
4036 | + thought.connect ("delete_thought", self.delete_thought) |
4037 | + thought.connect ("text_selection_changed", self.text_selection_cb) |
4038 | + thought.connect ("change_mouse_cursor", self.set_mouse_cursor_cb) |
4039 | + thought.connect ("update_links", self.update_links_cb) |
4040 | + thought.connect ("grab_focus", self.regain_focus_cb) |
4041 | + thought.connect ("update-attrs", self.update_attr_cb) |
4042 | + self.thoughts.append (thought) |
4043 | + return thought |
4044 | + |
4045 | + def regain_focus_cb (self, thought, ext): |
4046 | + self.emit ("set_focus", None, ext) |
4047 | + |
4048 | + def update_attr_cb (self, widget, bold, italics, underline, pango_font): |
4049 | + self.emit ("set_attrs", bold, italics, underline, pango_font) |
4050 | + |
4051 | + def delete_thought (self, thought): |
4052 | + action = UndoManager.UndoAction (self, UNDO_DELETE_SINGLE, self.undo_deletion, [thought]) |
4053 | + if thought.element in self.element.childNodes: |
4054 | + self.element.removeChild (thought.element) |
4055 | + self.thoughts.remove (thought) |
4056 | + try: |
4057 | + self.selected.remove (thought) |
4058 | + except: |
4059 | + pass |
4060 | + if self.editing == thought: |
4061 | + self.hookup_im_context () |
4062 | + self.editing = None |
4063 | + if self.primary == thought: |
4064 | + thought.disconnect (self.title_change_handler) |
4065 | + self.title_change_handler = None |
4066 | + self.primary = None |
4067 | + if self.thoughts: |
4068 | + self.make_primary (self.thoughts[0]) |
4069 | + rem_links = [] |
4070 | + for l in self.links: |
4071 | + if l.uses (thought): |
4072 | + action.add_arg (l) |
4073 | + rem_links.append (l) |
4074 | + for l in rem_links: |
4075 | + self.delete_link (l) |
4076 | + self.undo.add_undo (action) |
4077 | + return True |
4078 | + |
4079 | + def undo_deletion (self, action, mode): |
4080 | + self.undo.block () |
4081 | + if mode == UndoManager.UNDO: |
4082 | + self.unselect_all () |
4083 | + for l in action.args[1:]: |
4084 | + self.links.append (l) |
4085 | + self.element.appendChild (l.element) |
4086 | + for t in action.args[0]: |
4087 | + self.thoughts.append (t) |
4088 | + self.select_thought (t, -1) |
4089 | + self.element.appendChild (t.element) |
4090 | + if action.undo_type == UNDO_DELETE_SINGLE: |
4091 | + self.begin_editing (action.args[0][0]) |
4092 | + self.emit ("change_buffer", action.args[0][0].extended_buffer) |
4093 | + if not self.primary: |
4094 | + self.make_primary (action.args[0][0]) |
4095 | + else: |
4096 | + self.emit ("change_buffer", None) |
4097 | + else: |
4098 | + for t in action.args[0]: |
4099 | + self.delete_thought (t) |
4100 | + for l in action.args[1:]: |
4101 | + self.delete_link (l) |
4102 | + self.emit ("set_focus", None, False) |
4103 | + self.undo.unblock () |
4104 | + self.invalidate () |
4105 | + |
4106 | + def delete_selected_elements (self): |
4107 | + if len(self.selected) == 0: |
4108 | + return |
4109 | + action = UndoManager.UndoAction (self, UNDO_DELETE, self.undo_deletion, copy.copy(self.selected)) |
4110 | + # delete_thought as a callback adds it's own undo action. Block that here |
4111 | + self.undo.block () |
4112 | + tmp = self.selected |
4113 | + t = tmp.pop() |
4114 | + while t: |
4115 | + if t in self.thoughts: |
4116 | + for l in self.links: |
4117 | + if l.uses (t): |
4118 | + action.add_arg (l) |
4119 | + self.delete_thought (t) |
4120 | + if t in self.links: |
4121 | + self.delete_link (t) |
4122 | + if len (tmp) == 0: |
4123 | + t = None |
4124 | + else: |
4125 | + t = tmp.pop() |
4126 | + self.undo.unblock () |
4127 | + self.undo.add_undo (action) |
4128 | + self.invalidate () |
4129 | + |
4130 | + def delete_link (self, link): |
4131 | + if link.element in self.element.childNodes: |
4132 | + self.element.removeChild (link.element) |
4133 | + #link.element.unlink () |
4134 | + self.links.remove (link) |
4135 | + |
4136 | + def popup_menu_key (self, event): |
4137 | + print "Popup Menu Key" |
4138 | + |
4139 | + def find_related_thought (self, radians): |
4140 | + # Find thought within angle |
4141 | + best = None |
4142 | + bestangle = 1000. |
4143 | + bestdist = 10000. |
4144 | + def do_find (one, two, currentangle, curdist, sensitivity): |
4145 | + init_x = (one.ul[0] + one.lr[0]) / 2. |
4146 | + init_y = (one.ul[1] + one.lr[1]) / 2. |
4147 | + other_x = (two.ul[0] + two.lr[0]) / 2. |
4148 | + other_y = (two.ul[1] + two.lr[1]) / 2. |
4149 | + angle = math.atan2 ((other_y - init_y), (other_x - init_x)) |
4150 | + while angle > math.pi: |
4151 | + angle -= math.pi |
4152 | + while angle < -math.pi: |
4153 | + angle += math.pi |
4154 | + # We have to special-case left due to stupidity of tan's |
4155 | + # We shift it by pi radians |
4156 | + if radians == RAD_LEFT: |
4157 | + relangle = abs((angle+math.pi) - (radians+math.pi)) |
4158 | + if relangle > math.pi*2.: |
4159 | + relangle -= math.pi*2. |
4160 | + else: |
4161 | + relangle = abs(angle - radians) |
4162 | + newdist = math.sqrt ((init_x - other_x)**2 + (init_y - other_y)**2) |
4163 | + magicnum = newdist + (50. * relangle) |
4164 | +# Used for debugging. Spits out lots of useful info |
4165 | +# to determine interesting things about the thought relations |
4166 | +#print "angle: "+str(angle)+" rel: "+str(magicnum)+" rads: "+str(radians), |
4167 | +#print " , "+str(math.pi / 3.0)+" , "+str(currentangle)+"\n: "+str(relangle) |
4168 | + if (relangle < sensitivity) and \ |
4169 | + (magicnum < currentangle): |
4170 | + return (magicnum, newdist) |
4171 | + return (currentangle, curdist) |
4172 | + |
4173 | + if len(self.selected) != 1: |
4174 | + return None |
4175 | + initial = self.selected[0] |
4176 | + for x in self.links: |
4177 | + if x.parent == initial: |
4178 | + other = x.child |
4179 | + elif x.child == initial: |
4180 | + other = x.parent |
4181 | + else: |
4182 | + continue |
4183 | + (curr, dist) = do_find (initial, other, bestangle, bestdist, math.pi/3.) |
4184 | + if curr < bestangle: |
4185 | + bestangle = curr |
4186 | + best = other |
4187 | + bestdist = dist |
4188 | + if not best: |
4189 | + for x in self.thoughts: |
4190 | + if x == self.selected[0]: |
4191 | + continue |
4192 | + (curr, dist) = do_find (initial, x, bestangle, bestdist, math.pi/4.) |
4193 | + if curr < bestangle: |
4194 | + best = x |
4195 | + bestangle = curr |
4196 | + bestdist = dist |
4197 | + return best |
4198 | + |
4199 | + def undo_align(self, action, mode): |
4200 | + self.undo.block () |
4201 | + dic = action.args[0] |
4202 | + if mode == UndoManager.UNDO: |
4203 | + for t in dic: |
4204 | + t.move_by(-dic[t][0], -dic[t][1]) |
4205 | + else: |
4206 | + for t in dic: |
4207 | + t.move_by(dic[t][0], dic[t][1]) |
4208 | + self.undo.unblock () |
4209 | + |
4210 | + def align_top_left(self, vertical=True): |
4211 | + dic = {} |
4212 | + if len(self.selected) != 0: |
4213 | + x = self.selected[0].ul[0] |
4214 | + y = self.selected[0].ul[1] |
4215 | + for t in self.selected: |
4216 | + if vertical: |
4217 | + vec = (-(t.ul[0]-x), 0) |
4218 | + else: |
4219 | + vec = (0, -(t.ul[1]-y)) |
4220 | + t.move_by(vec[0], vec[1]) |
4221 | + dic[t] = vec |
4222 | + self.undo.add_undo (UndoManager.UndoAction (self, UNDO_ALIGN, self.undo_align, dic)) |
4223 | + |
4224 | + def align_bottom_right(self, vertical=True): |
4225 | + dic = {} |
4226 | + if len(self.selected) != 0: |
4227 | + x = self.selected[0].lr[0] |
4228 | + y = self.selected[0].lr[1] |
4229 | + for t in self.selected: |
4230 | + if vertical: |
4231 | + vec = (-(t.lr[0]-x), 0) |
4232 | + else: |
4233 | + vec = (0, -(t.lr[1]-y)) |
4234 | + t.move_by(vec[0], vec[1]) |
4235 | + dic[t] = vec |
4236 | + self.undo.add_undo (UndoManager.UndoAction (self, UNDO_ALIGN, self.undo_align, dic)) |
4237 | + |
4238 | + def align_centered(self, vertical=True): |
4239 | + dic = {} |
4240 | + if len(self.selected) != 0: |
4241 | + x = self.selected[0].ul[0] + (self.selected[0].lr[0] - self.selected[0].ul[0]) / 2.0 |
4242 | + y = self.selected[0].ul[1] + (self.selected[0].lr[1] - self.selected[0].ul[1]) / 2.0 |
4243 | + for t in self.selected: |
4244 | + if vertical: |
4245 | + vec = (-((t.ul[0] + (t.lr[0]-t.ul[0])/2.0)-x), 0) |
4246 | + else: |
4247 | + vec = (0, -((t.ul[1] + (t.lr[1]-t.ul[1])/2.0)-y)) |
4248 | + t.move_by(vec[0], vec[1]) |
4249 | + dic[t] = vec |
4250 | + self.undo.add_undo (UndoManager.UndoAction (self, UNDO_ALIGN, self.undo_align, dic)) |
4251 | + |
4252 | + def global_key_handler (self, event): |
4253 | + thought = None |
4254 | + if event.keyval == gtk.keysyms.Up: |
4255 | + thought = self.find_related_thought (RAD_UP) |
4256 | + elif event.keyval == gtk.keysyms.Down: |
4257 | + thought = self.find_related_thought (RAD_DOWN) |
4258 | + elif event.keyval == gtk.keysyms.Left: |
4259 | + thought = self.find_related_thought (RAD_LEFT) |
4260 | + elif event.keyval == gtk.keysyms.Right: |
4261 | + thought = self.find_related_thought (RAD_RIGHT) |
4262 | + elif event.keyval == gtk.keysyms.Delete: |
4263 | + self.delete_selected_elements () |
4264 | + elif event.keyval == gtk.keysyms.BackSpace: |
4265 | + self.delete_selected_elements () |
4266 | + elif event.keyval == gtk.keysyms.Menu: |
4267 | + self.popup_menu_key (event) |
4268 | + elif event.keyval == gtk.keysyms.Escape: |
4269 | + self.unselect_all () |
4270 | + elif event.keyval == gtk.keysyms.a and event.state & gtk.gdk.CONTROL_MASK: |
4271 | + self.unselect_all () |
4272 | + for t in self.thoughts: |
4273 | + t.select () |
4274 | + self.selected.append (t) |
4275 | + else: |
4276 | + return False |
4277 | + |
4278 | + if thought: |
4279 | + self.select_thought (thought, None) |
4280 | + self.invalidate () |
4281 | + return True |
4282 | + |
4283 | + def load_thought (self, node, type): |
4284 | + thought = self.create_new_thought (None, type, loading = True) |
4285 | + thought.load (node) |
4286 | + |
4287 | + def load_link (self, node): |
4288 | + link = Links.Link (self.save) |
4289 | + self.connect_link (link) |
4290 | + link.load (node) |
4291 | + self.links.append (link) |
4292 | + element = link.get_save_element () |
4293 | + self.element.appendChild (element) |
4294 | + |
4295 | + def load_thyself (self, top_element, doc): |
4296 | + for node in top_element.childNodes: |
4297 | + if node.nodeName == "thought": |
4298 | + self.load_thought (node, TYPE_TEXT) |
4299 | + elif node.nodeName == "image_thought": |
4300 | + self.load_thought (node, TYPE_IMAGE) |
4301 | + elif node.nodeName == "drawing_thought": |
4302 | + self.load_thought (node, TYPE_DRAWING) |
4303 | + elif node.nodeName == "res_thought": |
4304 | + self.load_thought (node, TYPE_RESOURCE) |
4305 | + elif node.nodeName == "link": |
4306 | + self.load_link (node) |
4307 | + else: |
4308 | + print "Warning: Unknown element type. Ignoring: "+node.nodeName |
4309 | + |
4310 | + self.finish_loading () |
4311 | + |
4312 | + def finish_loading (self): |
4313 | + # Possible TODO: This all assumes we've been given a proper, |
4314 | + # consistant file. It should fallback nicely, but... |
4315 | + # First, find the primary root: |
4316 | + for t in self.thoughts: |
4317 | + if t.am_primary: |
4318 | + self.make_primary (t) |
4319 | + if t.am_selected: |
4320 | + self.selected.append (t) |
4321 | + t.select () |
4322 | + if t.editing: |
4323 | + self.begin_editing (t) |
4324 | + if t.identity >= self.nthoughts: |
4325 | + self.nthoughts = t.identity + 1 |
4326 | + if self.selected: |
4327 | + self.current_root = self.selected |
4328 | + else: |
4329 | + self.current_root = [self.primary] |
4330 | + if len(self.selected) == 1: |
4331 | + self.emit ("change_buffer", self.selected[0].extended_buffer) |
4332 | + self.hookup_im_context (self.selected[0]) |
4333 | + self.emit ("thought_selection_changed", self.selected[0].background_color, \ |
4334 | + self.selected[0].foreground_color) |
4335 | + else: |
4336 | + self.emit ("change_buffer", None) |
4337 | + del_links = [] |
4338 | + for l in self.links: |
4339 | + if (l.parent_number == -1 and l.child_number == -1) or \ |
4340 | + (l.parent_number == l.child_number): |
4341 | + del_links.append (l) |
4342 | + continue |
4343 | + parent = child = None |
4344 | + for t in self.thoughts: |
4345 | + if t.identity == l.parent_number: |
4346 | + parent = t |
4347 | + elif t.identity == l.child_number: |
4348 | + child = t |
4349 | + if parent and child: |
4350 | + break |
4351 | + l.set_parent_child (parent, child) |
4352 | + if not l.parent or not l.child: |
4353 | + del_links.append (l) |
4354 | + for l in del_links: |
4355 | + self.delete_link (l) |
4356 | + |
4357 | + def prepare_save(self): |
4358 | + for t in self.thoughts: |
4359 | + t.update_save () |
4360 | + for l in self.links: |
4361 | + l.update_save () |
4362 | + |
4363 | + def save_thyself (self): |
4364 | + self.prepare_save() |
4365 | + if len(self.thoughts) > 0: |
4366 | + self.emit ("doc_save", self.save, self.element) |
4367 | + else: |
4368 | + self.emit ("doc_delete") |
4369 | + |
4370 | + def text_selection_cb (self, thought, start, end, text): |
4371 | + self.emit ("text_selection_changed", start, end, text) |
4372 | + |
4373 | + def copy_clipboard (self, clip): |
4374 | + if len (self.selected) != 1: |
4375 | + return |
4376 | + self.selected[0].copy_text (clip) |
4377 | + |
4378 | + def cut_clipboard (self, clip): |
4379 | + if len (self.selected) != 1: |
4380 | + return |
4381 | + self.selected[0].cut_text (clip) |
4382 | + |
4383 | + def paste_clipboard (self, clip): |
4384 | + if len (self.selected) != 1: |
4385 | + return |
4386 | + self.selected[0].paste_text (clip) |
4387 | + |
4388 | + def export (self, context, width, height, native): |
4389 | + context.rectangle (0, 0, width, height) |
4390 | + context.clip () |
4391 | + context.set_source_rgb (1.0,1.0,1.0) |
4392 | + context.move_to (0,0) |
4393 | + context.paint () |
4394 | + context.set_source_rgb (0.0,0.0,0.0) |
4395 | + if not native: |
4396 | + move_x = self.move_x |
4397 | + move_y = self.move_y |
4398 | + else: |
4399 | + move_x = 0 |
4400 | + move_y = 0 |
4401 | + for l in self.links: |
4402 | + l.export (context, move_x, move_y) |
4403 | + for t in self.thoughts: |
4404 | + t.export (context, move_x, move_y) |
4405 | + |
4406 | + def get_max_area (self): |
4407 | + minx = 999 |
4408 | + maxx = -999 |
4409 | + miny = 999 |
4410 | + maxy = -999 |
4411 | + |
4412 | + for t in self.thoughts: |
4413 | + mx,my,mmx,mmy = t.get_max_area () |
4414 | + if mx < minx: |
4415 | + minx = mx |
4416 | + if my < miny: |
4417 | + miny = my |
4418 | + if mmx > maxx: |
4419 | + maxx = mmx |
4420 | + if mmy > maxy: |
4421 | + maxy = mmy |
4422 | + # Add a 10px border around all |
4423 | + self.move_x = 10-minx |
4424 | + self.move_y = 10-miny |
4425 | + maxx = maxx-minx+20 |
4426 | + maxy = maxy-miny+20 |
4427 | + return (maxx,maxy) |
4428 | + |
4429 | + def get_selection_bounds (self): |
4430 | + if len (self.selected) == 1: |
4431 | + try: |
4432 | + return self.selected[0].index, self.selected[0].end_index |
4433 | + except AttributeError: |
4434 | + return None, None |
4435 | + else: |
4436 | + return None, None |
4437 | + |
4438 | + def thoughts_are_linked (self): |
4439 | + if len (self.selected) != 2: |
4440 | + return False |
4441 | + for l in self.links: |
4442 | + if l.connects (self.selected[0], self.selected[1]): |
4443 | + return True |
4444 | + return False |
4445 | + |
4446 | + def link_menu_cb (self): |
4447 | + if len (self.selected) != 2: |
4448 | + return |
4449 | + lnk = None |
4450 | + for l in self.links: |
4451 | + if l.connects (self.selected[0], self.selected[1]): |
4452 | + lnk = l |
4453 | + break |
4454 | + if lnk: |
4455 | + self.undo.add_undo (UndoManager.UndoAction (self, UNDO_DELETE_LINK, self.undo_link_action, lnk)) |
4456 | + self.delete_link (lnk) |
4457 | + else: |
4458 | + lnk = self.create_link (self.selected[0], None, self.selected[1]) |
4459 | + self.undo.add_undo (UndoManager.UndoAction (self, UNDO_CREATE_LINK, self.undo_link_action, lnk)) |
4460 | + self.invalidate () |
4461 | + |
4462 | + def set_bold (self, active): |
4463 | + if len(self.selected) != 1: |
4464 | + return |
4465 | + self.selected[0].set_bold (active) |
4466 | + self.invalidate() |
4467 | + |
4468 | + def set_italics (self, active): |
4469 | + if len(self.selected) != 1: |
4470 | + return |
4471 | + self.selected[0].set_italics (active) |
4472 | + self.invalidate() |
4473 | + |
4474 | + def set_underline (self, active): |
4475 | + if len(self.selected) != 1: |
4476 | + return |
4477 | + self.selected[0].set_underline (active) |
4478 | + self.invalidate() |
4479 | + |
4480 | + def set_background_color(self, color): |
4481 | + for s in self.selected: |
4482 | + s.background_color = color |
4483 | + self.background_color = color |
4484 | + if len(self.selected) > 1: |
4485 | + self.invalidate() |
4486 | + |
4487 | + def set_foreground_color(self, color): |
4488 | + for s in self.selected: |
4489 | + s.foreground_color = color |
4490 | + self.foreground_color = color |
4491 | + if len(self.selected) > 1: |
4492 | + self.invalidate() |
4493 | + |
4494 | + def set_font(self, font_name): |
4495 | + if len (self.selected) == 1 and hasattr(self.selected[0], "set_font"): |
4496 | + self.selected[0].set_font(font_name) |
4497 | + self.font_name = font_name |
4498 | + self.invalidate() |
4499 | + |
4500 | +class CursorFactory: |
4501 | + # Shared state |
4502 | + cursors = {} |
4503 | + |
4504 | + def get_cursor(self, cur_type): |
4505 | + if cur_type not in self.cursors: |
4506 | + self.cursors[cur_type] = gtk.gdk.Cursor(cur_type) |
4507 | + return self.cursors[cur_type] |
4508 | |
4509 | === added file 'labyrinth_lib/MainWindow.py' |
4510 | --- labyrinth_lib/MainWindow.py 1970-01-01 00:00:00 +0000 |
4511 | +++ labyrinth_lib/MainWindow.py 2012-11-11 04:19:21 +0000 |
4512 | @@ -0,0 +1,770 @@ |
4513 | +# MainWindow.py |
4514 | +# This file is part of Labyrinth |
4515 | +# |
4516 | +# Copyright (C) 2006 - Don Scorgie <Don@Scorgie.org> |
4517 | +# |
4518 | +# Labyrinth is free software; you can redistribute it and/or modify |
4519 | +# it under the terms of the GNU General Public License as published by |
4520 | +# the Free Software Foundation; either version 2 of the License, or |
4521 | +# (at your option) any later version. |
4522 | +# |
4523 | +# Labyrinth is distributed in the hope that it will be useful, |
4524 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
4525 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4526 | +# GNU General Public License for more details. |
4527 | +# |
4528 | +# You should have received a copy of the GNU General Public License |
4529 | +# along with Labyrinth; if not, write to the Free Software |
4530 | +# Foundation, Inc., 51 Franklin St, Fifth Floor, |
4531 | +# Boston, MA 02110-1301 USA |
4532 | +# |
4533 | + |
4534 | +# Standard library |
4535 | +import hashlib |
4536 | +import os |
4537 | +import tarfile |
4538 | +import gettext |
4539 | +_ = gettext.gettext |
4540 | +import xml.dom.minidom as dom |
4541 | + |
4542 | +# Gtk stuff |
4543 | +import gtk |
4544 | +import cairo, pangocairo |
4545 | +import gobject |
4546 | +if os.name != 'nt': |
4547 | + import gconf |
4548 | + |
4549 | +# Local imports |
4550 | +import MMapArea |
4551 | +import UndoManager |
4552 | +import PeriodicSaveThread |
4553 | +import ImageThought |
4554 | +import utils |
4555 | + |
4556 | +# UNDO varieties for us |
4557 | +UNDO_MODE = 0 |
4558 | +UNDO_SHOW_EXTENDED = 1 |
4559 | + |
4560 | +class LabyrinthWindow (gobject.GObject): |
4561 | + __gsignals__ = dict (title_changed = (gobject.SIGNAL_RUN_FIRST, |
4562 | + gobject.TYPE_NONE, |
4563 | + (gobject.TYPE_STRING, gobject.TYPE_OBJECT)), |
4564 | + doc_save = (gobject.SIGNAL_RUN_FIRST, |
4565 | + gobject.TYPE_NONE, |
4566 | + (gobject.TYPE_STRING, gobject.TYPE_OBJECT)), |
4567 | + file_saved = (gobject.SIGNAL_RUN_FIRST, |
4568 | + gobject.TYPE_NONE, |
4569 | + (gobject.TYPE_STRING, gobject.TYPE_OBJECT)), |
4570 | + window_closed = (gobject.SIGNAL_RUN_FIRST, |
4571 | + gobject.TYPE_NONE, |
4572 | + (gobject.TYPE_OBJECT, ))) |
4573 | + |
4574 | + def __init__ (self, filename, imported=False): |
4575 | + super(LabyrinthWindow, self).__init__() |
4576 | + |
4577 | + # FIXME: This can go when we move entirely to gtk 2.10 |
4578 | + # pygtk 2.8 doesn't have the correct function :( |
4579 | + self.set_val = gtk.gtk_version[1] > 8 |
4580 | + |
4581 | + # First, construct the MainArea and connect it all up |
4582 | + self.undo = UndoManager.UndoManager (self) |
4583 | + self.undo.block () |
4584 | + self.MainArea = MMapArea.MMapArea (self.undo) |
4585 | + self.MainArea.connect ("title_changed", self.title_changed_cb) |
4586 | + self.MainArea.connect ("doc_save", self.doc_save_cb) |
4587 | + self.MainArea.connect ("doc_delete", self.doc_del_cb) |
4588 | + self.MainArea.connect ("change_mode", self.mode_request_cb) |
4589 | + self.MainArea.connect ("button-press-event", self.main_area_focus_cb) |
4590 | + self.MainArea.connect ("change_buffer", self.switch_buffer_cb) |
4591 | + self.MainArea.connect ("thought_selection_changed", self.thought_selected_cb) |
4592 | + self.MainArea.connect ("set_focus", self.main_area_focus_cb) |
4593 | + self.MainArea.connect ("set_attrs", self.attrs_cb) |
4594 | + if os.name != 'nt': |
4595 | + self.MainArea.connect ("text_selection_changed", self.selection_changed_cb) |
4596 | + self.config_client = gconf.client_get_default() |
4597 | + |
4598 | + glade = gtk.glade.XML(utils.get_data_file_name('labyrinth.glade')) |
4599 | + self.main_window = glade.get_widget ('MapWindow') |
4600 | + self.main_window.set_focus_child (self.MainArea) |
4601 | + if os.name != 'nt': |
4602 | + try: |
4603 | + self.main_window.set_icon_name ('labyrinth') |
4604 | + except: |
4605 | + self.main_window.set_icon_from_file(utils.get_data_file_name('labyrinth.svg')) |
4606 | + else: |
4607 | + self.main_window.set_icon_from_file('images\\labyrinth-24.png') |
4608 | + |
4609 | + # insert menu, toolbar and map |
4610 | + self.create_menu() |
4611 | + glade.get_widget ('main_area_insertion').pack_start(self.MainArea) |
4612 | + vbox = glade.get_widget ('map_window_vbox') |
4613 | + menubar = self.ui.get_widget('/MenuBar') |
4614 | + menubar.show_all() |
4615 | + vbox.pack_start(menubar) |
4616 | + vbox.reorder_child(menubar, 0) |
4617 | + vbox.set_child_packing(menubar, 0, 0, 0, gtk.PACK_START) |
4618 | + |
4619 | + toolbar = self.ui.get_widget('/ToolBar') |
4620 | + toolbar.show_all() |
4621 | + vbox.pack_start(toolbar) |
4622 | + vbox.reorder_child(toolbar, 1) |
4623 | + vbox.set_child_packing(toolbar, 0, 0, 0, gtk.PACK_START) |
4624 | + |
4625 | + # TODO: Bold, Italics etc. |
4626 | + self.bold_widget = glade.get_widget('tool_bold') |
4627 | + self.bold_widget.connect('toggled', self.bold_toggled) |
4628 | + self.bold_block = False |
4629 | + self.bold_state = False |
4630 | + self.italic_widget = glade.get_widget('tool_italic') |
4631 | + self.italic_widget.connect('toggled', self.italic_toggled) |
4632 | + self.italic_block = False |
4633 | + self.italic_state = False |
4634 | + self.underline_widget = glade.get_widget('tool_underline') |
4635 | + self.underline_widget.connect('toggled', self.underline_toggled) |
4636 | + self.underline_block = False |
4637 | + self.underline_state = False |
4638 | + |
4639 | + self.font_widget = glade.get_widget('font_button') |
4640 | + self.font_widget.connect ("font-set", self.font_change_cb) |
4641 | + self.background_widget = glade.get_widget('background_color_button') |
4642 | + self.background_widget.connect ("color-set", self.background_change_cb) |
4643 | + self.foreground_widget = glade.get_widget('foreground_color_button') |
4644 | + self.foreground_widget.connect ("color-set", self.foreground_change_cb) |
4645 | + |
4646 | + self.cut = self.ui.get_widget ('/MenuBar/EditMenu/Cut') |
4647 | + self.copy = self.ui.get_widget ('/MenuBar/EditMenu/Copy') |
4648 | + self.paste = self.ui.get_widget ('/MenuBar/EditMenu/Paste') |
4649 | + self.link = self.ui.get_widget ('/MenuBar/EditMenu/LinkThoughts') |
4650 | + self.delete = self.ui.get_widget ('/MenuBar/EditMenu/DeleteNodes') |
4651 | + |
4652 | + self.ui.get_widget('/MenuBar/EditMenu').connect ('activate', self.edit_activated_cb) |
4653 | + self.cut.set_sensitive (False) |
4654 | + self.copy.set_sensitive (False) |
4655 | + |
4656 | + # get toolbars and activate corresponding menu entries |
4657 | + self.main_toolbar = self.ui.get_widget ('/ToolBar') |
4658 | + self.format_toolbar = glade.get_widget ('format_toolbar') |
4659 | + self.ui.get_widget('/MenuBar/ViewMenu/ShowToolbars/ShowMainToolbar').set_active(True) |
4660 | + self.ui.get_widget('/MenuBar/ViewMenu/ShowToolbars/ShowFormatToolbar').set_active(True) |
4661 | + self.ui.get_widget('/MenuBar/ViewMenu/UseBezier').set_active(utils.use_bezier_curves) |
4662 | + |
4663 | + # Add in the extended info view |
4664 | + self.extended_window = glade.get_widget('extended_window') |
4665 | + self.extended = glade.get_widget('extended') |
4666 | + self.invisible_buffer = gtk.TextBuffer () |
4667 | + |
4668 | + # Connect all our signals |
4669 | + self.main_window.connect ("configure_event", self.configure_cb) |
4670 | + self.main_window.connect ("window-state-event", self.window_state_cb) |
4671 | + self.main_window.connect ("destroy", self.close_window_cb) |
4672 | + |
4673 | + # Deal with loading the map |
4674 | + if not filename: |
4675 | + self.MainArea.set_size_request (400, 400) |
4676 | + # TODO: This shouldn't be set to a hard-coded number. Fix. |
4677 | + self.pane_pos = 500 |
4678 | + self.title_cp = _("Untitled Map") |
4679 | + self.mode = MMapArea.MODE_EDITING |
4680 | + self.extended_visible = False |
4681 | + else: |
4682 | + self.parse_file (filename) |
4683 | + |
4684 | + up_box = glade.get_widget('up_box') |
4685 | + up_box.connect("button-press-event", self.translate, "Up") |
4686 | + up_box.connect("button-release-event", self.finish_translate) |
4687 | + down_box = glade.get_widget('down_box') |
4688 | + down_box.connect("button-press-event", self.translate, "Down") |
4689 | + down_box.connect("button-release-event", self.finish_translate) |
4690 | + right_box = glade.get_widget('right_box') |
4691 | + right_box.connect("button-press-event", self.translate, "Right") |
4692 | + right_box.connect("button-release-event", self.finish_translate) |
4693 | + left_box = glade.get_widget('left_box') |
4694 | + left_box.connect("button-press-event", self.translate, "Left") |
4695 | + left_box.connect("button-release-event", self.finish_translate) |
4696 | + |
4697 | + panes = glade.get_widget('vpaned1') |
4698 | + panes.connect ("button-release-event", self.pos_changed) |
4699 | + panes.set_position (self.pane_pos) |
4700 | + |
4701 | + # Other stuff |
4702 | + self.width, self.height = self.main_window.get_size () |
4703 | + |
4704 | + # if we import, we dump the old filename to create a new hashed one |
4705 | + self.save_file = None |
4706 | + if not imported: |
4707 | + self.save_file = filename |
4708 | + |
4709 | + self.maximised = False |
4710 | + self.view_type = 0 |
4711 | + if self.set_val: |
4712 | + self.act.set_current_value (self.mode) |
4713 | + |
4714 | + self.undo.unblock () |
4715 | + self.start_timer () |
4716 | + |
4717 | + def show(self): |
4718 | + self.main_window.show_all() |
4719 | + self.ext_act.set_active(self.extended_visible) |
4720 | + if not self.extended_visible: |
4721 | + self.extended_window.hide() |
4722 | + |
4723 | + def create_menu (self): |
4724 | + actions = [ |
4725 | + ('FileMenu', None, _('File')), |
4726 | + ('Export', None, _('Export as Image'), None, |
4727 | + _("Export your map as an image"), self.export_cb), |
4728 | + ('ExportMap', gtk.STOCK_SAVE_AS, _('Export Map...'), '<control>S', |
4729 | + _("Export your map as XML"), self.export_map_cb), |
4730 | + ('Close', gtk.STOCK_CLOSE, None, '<control>W', |
4731 | + _('Close the current window'), self.close_window_cb), |
4732 | + ('EditMenu', None, _('_Edit')), |
4733 | + ('ViewMenu', None, _('_View')), |
4734 | + ('ShowToolbars', None, _('_Toolbar')), |
4735 | + ('Undo', gtk.STOCK_UNDO, None, '<control>Z', None), |
4736 | + ('Redo', gtk.STOCK_REDO, None, '<control><shift>Z', None), |
4737 | + ('Cut', gtk.STOCK_CUT, None, '<control>X', |
4738 | + None, self.cut_text_cb), |
4739 | + ('Copy', gtk.STOCK_COPY, None, '<control>C', |
4740 | + None, self.copy_text_cb), |
4741 | + ('Paste', gtk.STOCK_PASTE, None, '<control>V', |
4742 | + None, self.paste_text_cb), |
4743 | + ('LinkThoughts', None, _("Link Thoughts"), '<control>L', |
4744 | + _("Link the selected thoughts"), self.link_thoughts_cb), |
4745 | + ('ModeMenu', None, _('_Mode')), |
4746 | + ('ZoomIn', gtk.STOCK_ZOOM_IN, None, '<control>plus', |
4747 | + None, self.zoomin_cb), |
4748 | + ('ZoomOut', gtk.STOCK_ZOOM_OUT, None, '<control>minus', |
4749 | + None, self.zoomout_cb), |
4750 | + ('Zoom100', gtk.STOCK_ZOOM_100, None, '<control>0', |
4751 | + None, self.zoom100_cb)] |
4752 | + self.radio_actions = [ |
4753 | + ('Edit', gtk.STOCK_EDIT, _('_Edit Mode'), '<control>E', |
4754 | + _('Turn on edit mode'), MMapArea.MODE_EDITING), |
4755 | + ('AddImage', gtk.STOCK_ADD, _('_Add Image'), None, |
4756 | + _('Add an image to selected thought'), MMapArea.MODE_IMAGE), |
4757 | + ('Drawing', gtk.STOCK_COLOR_PICKER, _('_Drawing Mode'), None, |
4758 | + _('Make a pretty drawing'), MMapArea.MODE_DRAW)] |
4759 | + self.view_radio_actions = [ |
4760 | + ('UseBezier', None, _('Use _Curves'), None, |
4761 | + _('Use curves as links'), MMapArea.VIEW_BEZIER), |
4762 | + ('UseLines', None, _('Use _Lines'), None, |
4763 | + _('Use straight lines as links'), MMapArea.VIEW_LINES)] |
4764 | + self.toggle_actions = [ |
4765 | + ('ViewExtend', None, _('_Extended Information'), None, |
4766 | + _('Show extended information for thoughts'), self.view_extend_cb), |
4767 | + ('ShowMainToolbar', None, _('_Main'), None, |
4768 | + _('Show main toolbar'), self.show_main_toolbar_cb), |
4769 | + ('ShowFormatToolbar', None, _('_Format'), None, |
4770 | + _('Show format toolbar'), self.show_format_toolbar_cb)] |
4771 | + |
4772 | + ag = gtk.ActionGroup ('WindowActions') |
4773 | + ag.set_translation_domain(gettext.textdomain()) |
4774 | + ag.add_actions (actions) |
4775 | + ag.add_radio_actions (self.radio_actions) |
4776 | + ag.add_radio_actions (self.view_radio_actions) |
4777 | + ag.add_toggle_actions (self.toggle_actions) |
4778 | + self.act = ag.get_action ('Edit') |
4779 | + self.ext_act = ag.get_action ('ViewExtend') |
4780 | + self.act.connect ("changed", self.mode_change_cb) |
4781 | + self.undo.set_widgets (ag.get_action ('Undo'), ag.get_action ('Redo')) |
4782 | + |
4783 | + self.view_action = ag.get_action('UseBezier') |
4784 | + self.view_action.connect ("changed", self.view_change_cb) |
4785 | + |
4786 | + self.ui = gtk.UIManager () |
4787 | + self.ui.insert_action_group (ag, 0) |
4788 | + self.ui.add_ui_from_file (utils.get_data_file_name('labyrinth-ui.xml')) |
4789 | + self.main_window.add_accel_group (self.ui.get_accel_group ()) |
4790 | + |
4791 | + def align_cb(self, widget, direction): |
4792 | + if direction == "vl" or direction == "ht": |
4793 | + self.MainArea.align_top_left(direction == "vl") |
4794 | + elif direction == "vr" or direction == "hb": |
4795 | + self.MainArea.align_bottom_right(direction == "vr") |
4796 | + else: |
4797 | + self.MainArea.align_centered(direction == "vc") |
4798 | + |
4799 | + if widget != self.align_button: |
4800 | + self.align_button.disconnect(self.align_handler_id) |
4801 | + self.align_handler_id = self.align_button.connect('clicked', self.align_cb, direction) |
4802 | + self.align_button.set_icon_name(widget.get_image().get_icon_name()[0]) |
4803 | + |
4804 | + def link_thoughts_cb (self, arg): |
4805 | + self.MainArea.link_menu_cb () |
4806 | + |
4807 | + def undo_show_extended (self, action, mode): |
4808 | + self.undo.block () |
4809 | + self.ext_act.set_active (not self.ext_act.get_active ()) |
4810 | + self.undo.unblock () |
4811 | + |
4812 | + def view_extend_cb (self, arg): |
4813 | + self.undo.add_undo (UndoManager.UndoAction (self, UNDO_SHOW_EXTENDED, self.undo_show_extended)) |
4814 | + self.extended_visible = arg.get_active () |
4815 | + if self.extended_visible: |
4816 | + self.extended_window.show () |
4817 | + self.view_type = 1 |
4818 | + else: |
4819 | + self.extended_window.hide () |
4820 | + self.view_type = 0 |
4821 | + |
4822 | + def show_main_toolbar_cb(self, arg): |
4823 | + if arg.get_active(): |
4824 | + self.main_toolbar.show() |
4825 | + else: |
4826 | + self.main_toolbar.hide() |
4827 | + |
4828 | + def show_format_toolbar_cb(self, arg): |
4829 | + if arg.get_active(): |
4830 | + self.format_toolbar.show() |
4831 | + else: |
4832 | + self.format_toolbar.hide() |
4833 | + |
4834 | + def view_change_cb(self, base, activated): |
4835 | + utils.use_bezier_curves = activated.get_current_value() == MMapArea.VIEW_BEZIER |
4836 | + if os.name != 'nt': |
4837 | + self.config_client.set_bool('/apps/labyrinth/curves', utils.use_bezier_curves) |
4838 | + self.MainArea.update_all_links() |
4839 | + self.MainArea.invalidate() |
4840 | + |
4841 | + def attrs_cb (self, widget, bold, italics, underline, pango_font): |
4842 | + # Yes, there is a block method for signals |
4843 | + # but I don't currently know how to |
4844 | + # implement it for action-based signals |
4845 | + # without messyness |
4846 | + if bold != self.bold_state: |
4847 | + self.bold_block = True |
4848 | + self.bold_widget.set_active(bold) |
4849 | + if italics != self.italic_state: |
4850 | + self.italic_block = True |
4851 | + self.italic_widget.set_active(italics) |
4852 | + if underline != self.underline_state: |
4853 | + self.underline_block = True |
4854 | + self.underline_widget.set_active(underline) |
4855 | + if pango_font: |
4856 | + font_name = pango_font.to_string() |
4857 | + self.font_widget.set_font_name (font_name) |
4858 | + self.MainArea.set_font(font_name) |
4859 | + else: |
4860 | + self.font_widget.set_font_name (utils.default_font) |
4861 | + |
4862 | + def translate (self, box, arg1, direction): |
4863 | + self.orig_translate = [self.MainArea.translation[0], self.MainArea.translation[1]] |
4864 | + if direction == "Up": |
4865 | + translation_x = 0 |
4866 | + translation_y = 5 |
4867 | + elif direction == "Down": |
4868 | + translation_x = 0 |
4869 | + translation_y = -5 |
4870 | + elif direction == "Right": |
4871 | + translation_x = -5 |
4872 | + translation_y = 0 |
4873 | + elif direction == "Left": |
4874 | + translation_x = 5 |
4875 | + translation_y = 0 |
4876 | + else: |
4877 | + print "Error" |
4878 | + return |
4879 | + gobject.timeout_add (20, self.translate_timeout, translation_x, translation_y) |
4880 | + self.tr_to = True |
4881 | + |
4882 | + def translate_timeout (self, addition_x, addition_y): |
4883 | + if not self.tr_to: |
4884 | + return False |
4885 | + self.MainArea.translation[0] += addition_x / self.MainArea.scale_fac |
4886 | + self.MainArea.translation[1] += addition_y / self.MainArea.scale_fac |
4887 | + self.MainArea.invalidate() |
4888 | + return self.tr_to |
4889 | + |
4890 | + def finish_translate (self, box, arg1): |
4891 | + self.undo.add_undo (UndoManager.UndoAction (self.MainArea, UndoManager.TRANSFORM_CANVAS, \ |
4892 | + self.MainArea.undo_transform_cb, |
4893 | + self.MainArea.scale_fac, |
4894 | + self.MainArea.scale_fac, |
4895 | + self.orig_translate, |
4896 | + self.MainArea.translation)) |
4897 | + self.tr_to = False |
4898 | + |
4899 | + def pos_changed (self, panes, arg2): |
4900 | + self.pane_pos = panes.get_position () |
4901 | + |
4902 | + def bold_toggled (self, action): |
4903 | + self.bold_state = (not self.bold_state) |
4904 | + if self.bold_block: |
4905 | + self.bold_block = False |
4906 | + return |
4907 | + if self.extended.is_focus (): |
4908 | + self.extended.get_buffer().set_bold(action.get_active()) |
4909 | + else: |
4910 | + self.MainArea.set_bold (action.get_active()) |
4911 | + |
4912 | + def italic_toggled (self, action): |
4913 | + self.italic_state = (not self.italic_state) |
4914 | + if self.italic_block: |
4915 | + self.italic_block = False |
4916 | + return |
4917 | + if self.extended.is_focus (): |
4918 | + self.extended.get_buffer().set_italics(action.get_active()) |
4919 | + else: |
4920 | + self.MainArea.set_italics (action.get_active()) |
4921 | + |
4922 | + def underline_toggled (self, action): |
4923 | + self.underline_state = (not self.underline_state) |
4924 | + if self.underline_block: |
4925 | + self.underline_block = False |
4926 | + return |
4927 | + if self.extended.is_focus (): |
4928 | + self.extended.get_buffer().set_underline(action.get_active()) |
4929 | + else: |
4930 | + self.MainArea.set_underline (action.get_active()) |
4931 | + |
4932 | + def foreground_change_cb (self, button): |
4933 | + if not self.extended.is_focus (): |
4934 | + self.MainArea.set_foreground_color (button.get_color()) |
4935 | + |
4936 | + def background_change_cb (self, button): |
4937 | + if not self.extended.is_focus (): |
4938 | + self.MainArea.set_background_color (button.get_color()) |
4939 | + |
4940 | + def font_change_cb (self, button): |
4941 | + if not self.extended.is_focus (): |
4942 | + self.MainArea.set_font (button.get_font_name ()) |
4943 | + |
4944 | + def zoomin_cb(self, arg): |
4945 | + if self.MainArea.scale_fac < 10: |
4946 | + self.MainArea.scale_fac *= 1.2 |
4947 | + self.MainArea.invalidate() |
4948 | + |
4949 | + def zoomout_cb(self, arg): |
4950 | + if self.MainArea.scale_fac > 0.1: |
4951 | + self.MainArea.scale_fac /= 1.2 |
4952 | + self.MainArea.invalidate() |
4953 | + |
4954 | + def zoom100_cb(self, arg): |
4955 | + self.MainArea.scale_fac = 1.0 |
4956 | + self.MainArea.translation = [0.0, 0.0] |
4957 | + self.MainArea.invalidate() |
4958 | + |
4959 | + def switch_buffer_cb (self, arg, new_buffer): |
4960 | + if new_buffer: |
4961 | + self.extended.set_editable (True) |
4962 | + self.extended.set_buffer (new_buffer) |
4963 | + else: |
4964 | + self.extended.set_buffer (self.invisible_buffer) |
4965 | + self.extended.set_editable (False) |
4966 | + |
4967 | + def thought_selected_cb (self, arg, background_color, foreground_color): |
4968 | + if background_color: |
4969 | + self.background_widget.set_color(background_color) |
4970 | + if foreground_color: |
4971 | + self.foreground_widget.set_color(foreground_color) |
4972 | + |
4973 | + def main_area_focus_cb (self, arg, event, extended = False): |
4974 | + if not extended: |
4975 | + self.MainArea.grab_focus () |
4976 | + else: |
4977 | + self.extended.grab_focus () |
4978 | + |
4979 | + def revert_mode (self, action, mode): |
4980 | + self.undo.block () |
4981 | + if mode == UndoManager.UNDO: |
4982 | + self.mode_request_cb (None, action.args[0]) |
4983 | + else: |
4984 | + self.mode_request_cb (None, action.args[1]) |
4985 | + self.undo.unblock () |
4986 | + |
4987 | + def mode_change_cb (self, base, activated): |
4988 | + self.MainArea.set_mode (activated.get_current_value ()) |
4989 | + self.mode = activated.get_current_value () |
4990 | + |
4991 | + def mode_request_cb (self, widget, mode): |
4992 | + if self.set_val: |
4993 | + self.act.set_current_value (mode) |
4994 | + |
4995 | + def title_changed_cb (self, widget, new_title): |
4996 | + self.title_cp = self.title_cp = _('Untitled Map') |
4997 | + if new_title != '': |
4998 | + split = new_title.splitlines () |
4999 | + self.title_cp = reduce(lambda x,y : x + ' ' + y, split) |
5000 | + |