Merge lp:~ubuntu-branches/ubuntu/raring/labyrinth/raring-201211110411 into lp:ubuntu/raring/labyrinth

Proposed by Ubuntu Package Importer
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
Reviewer Review Type Date Requested Status
Ubuntu branches Pending
Review via email: mp+133816@code.launchpad.net

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://bugs.launchpad.net/udd linking to this merge proposal.

(this is an automatically generated message)

To post a comment you must log in.

Unmerged revisions

13. By Andrew Starr-Bochicchio

Merge lp:~takluyver/ubuntu/raring/labyrinth/0.6

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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'
173Binary 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'
175Binary 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'
177Binary 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+
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: