Merge lp:~valavanisalex/ubuntu/utopic/inkscape/fix-1358863 into lp:ubuntu/utopic/inkscape

Proposed by Alex Valavanis on 2014-08-19
Status: Merged
Merged at revision: 87
Proposed branch: lp:~valavanisalex/ubuntu/utopic/inkscape/fix-1358863
Merge into: lp:ubuntu/utopic/inkscape
Diff against target: 83978 lines (+9530/-59871)
289 files modified
.pc/0001-Fix_Datamatrix_UI_issue.patch/share/extensions/render_barcode_datamatrix.inx (+0/-20)
.pc/0001-Fix_Datamatrix_UI_issue.patch/share/extensions/render_barcode_datamatrix.py (+0/-654)
.pc/0002-Drop_PS_and_PDF_support_in_MimeType.patch/inkscape.desktop.in (+0/-14)
.pc/0003-Fix_LP_-911146.patch/src/extension/implementation/script.cpp (+0/-1034)
.pc/0004-Fix_FTBFS_on_gcc-4.8.patch/src/connector-context.cpp (+0/-2041)
.pc/0004-Fix_FTBFS_on_gcc-4.8.patch/src/extension/effect.cpp (+0/-409)
.pc/0004-Fix_FTBFS_on_gcc-4.8.patch/src/gradient-drag.cpp (+0/-2216)
.pc/0004-Fix_FTBFS_on_gcc-4.8.patch/src/libavoid/vpsc.cpp (+0/-1301)
.pc/0004-Fix_FTBFS_on_gcc-4.8.patch/src/libcola/straightener.cpp (+0/-361)
.pc/0004-Fix_FTBFS_on_gcc-4.8.patch/src/libcroco/cr-parser.c (+0/-4409)
.pc/0004-Fix_FTBFS_on_gcc-4.8.patch/src/libvpsc/block.cpp (+0/-422)
.pc/0004-Fix_FTBFS_on_gcc-4.8.patch/src/widgets/desktop-widget.h (+0/-268)
.pc/0004-Fix_FTBFS_on_gcc-4.8.patch/src/widgets/toolbox.cpp (+0/-8503)
.pc/0005-Fix_FreeType_include.patch/src/libnrtype/FontFactory.h (+0/-163)
.pc/0006-Fix_FTBFS_on_poppler-0.26.patch/configure.ac (+0/-1065)
.pc/0006-Fix_FTBFS_on_poppler-0.26.patch/src/extension/internal/pdfinput/pdf-parser.cpp (+0/-3399)
.pc/0006_add_unity_quicklist_support.patch/inkscape.desktop.in (+0/-14)
.pc/applied-patches (+0/-8)
.pc/librevenge.patch/configure.ac (+0/-1070)
.pc/librevenge.patch/src/extension/internal/wpg-input.cpp (+0/-145)
ChangeLog (+775/-10714)
Info.plist (+3/-3)
Info.plist.in (+1/-1)
Makefile.in (+183/-167)
NEWS (+9/-17)
aclocal.m4 (+406/-348)
build.xml (+1/-1)
compile (+68/-31)
config.guess (+106/-78)
config.h.in (+3/-3)
config.sub (+66/-48)
configure (+520/-358)
configure.ac (+28/-38)
debian/changelog (+55/-0)
debian/control (+3/-3)
debian/patches/0004-Fix_FTBFS_on_gcc-4.8.patch (+10/-139)
debian/patches/0005-Fix_FTBFS_due_to_tests.patch (+482/-0)
debian/patches/0005-Fix_FreeType_include.patch (+0/-23)
debian/patches/0006-Fix_FTBFS_on_poppler-0.26.patch (+0/-150)
debian/patches/0006-Update_to_new_libwpg.patch (+111/-0)
debian/patches/librevenge.patch (+0/-80)
debian/patches/series (+2/-3)
debian/rules (+2/-2)
depcomp (+296/-193)
doc/Makefile.in (+75/-37)
inkscape.1 (+51/-42)
inkscape.desktop.in (+1/-7)
inkscape.el.1 (+74/-65)
inkscape.fr.1 (+63/-54)
inkscape.ja.1 (+33/-24)
inkscape.sk.1 (+58/-49)
inkscape.spec (+1/-1)
inkscape.zh_TW.1 (+31/-22)
install-sh (+7/-7)
missing (+159/-275)
mkinstalldirs (+2/-2)
packaging/macosx/Resources/MenuBar.nib/classes.nib (+0/-4)
packaging/macosx/Resources/MenuBar.nib/info.nib (+0/-23)
packaging/macosx/Resources/MenuBar.nib/objects.xib (+0/-73)
packaging/macosx/Resources/ProgressWindow.nib/classes.nib (+0/-4)
packaging/macosx/Resources/ProgressWindow.nib/info.nib (+0/-18)
packaging/macosx/Resources/ProgressWindow.nib/objects.xib (+0/-53)
packaging/macosx/Resources/bin/getdisplay.sh (+0/-9)
packaging/macosx/Resources/bin/inkscape (+0/-158)
packaging/macosx/Resources/etc/fonts/fonts.conf (+0/-155)
packaging/macosx/Resources/openDoc (+0/-17)
packaging/macosx/Resources/script (+0/-54)
packaging/macosx/Resources/themes/Clearlooks-Quicksilver-OSX/gtk-2.0/Scrollbars_1/copy-slider.sh (+0/-7)
packaging/macosx/Resources/themes/Clearlooks-Quicksilver-OSX/gtk-2.0/Scrollbars_6/copy-slider.sh (+0/-7)
packaging/macosx/Resources/themes/Clearlooks-Quicksilver-OSX/gtk-2.0/pre_gtkrc (+0/-547)
packaging/macosx/ScriptExec/English.lproj/main.nib/classes.nib (+0/-4)
packaging/macosx/ScriptExec/English.lproj/main.nib/info.nib (+0/-19)
packaging/macosx/ScriptExec/English.lproj/main.nib/objects.xib (+0/-271)
packaging/macosx/ScriptExec/Info.plist (+0/-39)
packaging/macosx/ScriptExec/MenuBar.nib/classes.nib (+0/-4)
packaging/macosx/ScriptExec/MenuBar.nib/info.nib (+0/-23)
packaging/macosx/ScriptExec/MenuBar.nib/objects.xib (+0/-73)
packaging/macosx/ScriptExec/ScriptExec.xcode/project.pbxproj (+0/-451)
packaging/macosx/ScriptExec/ScriptExec.xcodeproj/project.pbxproj (+0/-351)
packaging/macosx/ScriptExec/ScriptExec_Prefix.pch (+0/-5)
packaging/macosx/ScriptExec/main.c (+0/-789)
packaging/macosx/ScriptExec/openDoc (+0/-4)
packaging/macosx/ScriptExec/script (+0/-4)
packaging/macosx/ScriptExec/version.plist (+0/-16)
packaging/macosx/osx-app.sh (+0/-499)
packaging/macosx/osx-build.sh (+0/-410)
packaging/macosx/osx-dmg.sh (+0/-178)
packaging/win32/AdvUninstLog.nsh (+0/-437)
packaging/win32/AdvUninstLog.txt (+0/-121)
packaging/win32/MessageBox.txt (+0/-186)
packaging/win32/RequireLatestNSIS.nsh (+0/-9)
packaging/win32/VersionCompleteXXXX.nsh (+0/-47)
packaging/win32/header.svg (+0/-516)
packaging/win32/ifexist.nsh (+0/-21)
packaging/win32/inkscape.nsi (+0/-1032)
packaging/win32/inkscape.nsi.uninstall (+0/-20)
packaging/win32/languages/Breton.nsh (+0/-113)
packaging/win32/languages/Catalan.nsh (+0/-113)
packaging/win32/languages/Czech.nsh (+0/-113)
packaging/win32/languages/Dutch.nsh (+0/-113)
packaging/win32/languages/English.nsh (+0/-113)
packaging/win32/languages/Finnish.nsh (+0/-113)
packaging/win32/languages/French.nsh (+0/-113)
packaging/win32/languages/Galician.nsh (+0/-113)
packaging/win32/languages/German.nsh (+0/-113)
packaging/win32/languages/Indonesian.nsh (+0/-113)
packaging/win32/languages/Italian.nsh (+0/-113)
packaging/win32/languages/Japanese.nsh (+0/-113)
packaging/win32/languages/Polish.nsh (+0/-113)
packaging/win32/languages/Romanian.nsh (+0/-113)
packaging/win32/languages/Russian.nsh (+0/-113)
packaging/win32/languages/SimpChinese.nsh (+0/-113)
packaging/win32/languages/Slovak.nsh (+0/-113)
packaging/win32/languages/Slovenian.nsh (+0/-113)
packaging/win32/languages/Spanish.nsh (+0/-113)
packaging/win32/languages/TradChinese.nsh (+0/-113)
packaging/win32/languages/Ukrainian.nsh (+0/-113)
packaging/win32/md5dll.txt (+0/-68)
packaging/win32/portable/App/AppInfo/appinfo.ini (+0/-26)
packaging/win32/portable/App/AppInfo/installer.ini (+0/-15)
packaging/win32/portable/App/readme.txt (+0/-1)
packaging/win32/portable/Other/Source/InkscapePortable.ini (+0/-6)
packaging/win32/portable/Other/Source/InkscapePortable.nsi (+0/-130)
packaging/win32/portable/Other/Source/License.txt (+0/-344)
packaging/win32/portable/Other/Source/PortableApps.comInstallerCustom.nsh (+0/-12)
packaging/win32/portable/Other/Source/PortableApps.comLauncherLANG_ENGLISH.nsh (+0/-6)
packaging/win32/portable/Other/Source/Readme.txt (+0/-67)
packaging/win32/portable/Other/Source/ReplaceInFileWithTextReplace.nsh (+0/-64)
packaging/win32/portable/help.html (+0/-178)
packaging/win32/portable/readme.packaging.txt (+0/-29)
packaging/win32/welcomefinish.svg (+0/-3510)
po/fi.po (+1866/-2720)
po/nl.po (+2/-2)
share/Makefile.in (+145/-126)
share/clipart/Makefile.am (+0/-1)
share/clipart/Makefile.in (+75/-38)
share/clipart/inkscape.logo.svg (+0/-9)
share/examples/Makefile.in (+75/-37)
share/examples/blend_modes.svg (+12/-12)
share/examples/lighting_filters.svg (+12/-12)
share/extensions/Barcode/Makefile.in (+75/-37)
share/extensions/Makefile.in (+147/-128)
share/extensions/Poly3DObjects/Makefile.in (+76/-39)
share/extensions/alphabet_soup/Makefile.in (+76/-39)
share/extensions/dxf_outlines.py (+6/-1)
share/extensions/inkex.py (+8/-2)
share/extensions/inkscape_help_reportabug.inx (+1/-1)
share/extensions/printing-marks.inx (+5/-5)
share/extensions/render_barcode_datamatrix.inx (+4/-34)
share/extensions/render_barcode_datamatrix.py (+3/-46)
share/extensions/scour.py (+6/-3)
share/extensions/simplestyle.py (+1/-1)
share/extensions/test/Makefile.in (+75/-37)
share/extensions/text_braille.py (+1/-1)
share/extensions/xaml2svg/Makefile.in (+76/-39)
share/extensions/yocto_css.py (+6/-3)
share/filters/Makefile.in (+76/-38)
share/fonts/Makefile.in (+76/-38)
share/gradients/Makefile.in (+76/-38)
share/icons/Makefile.in (+146/-127)
share/icons/application/16x16/Makefile.in (+76/-38)
share/icons/application/22x22/Makefile.in (+76/-38)
share/icons/application/24x24/Makefile.in (+76/-38)
share/icons/application/256x256/Makefile.in (+76/-38)
share/icons/application/32x32/Makefile.in (+76/-38)
share/icons/application/48x48/Makefile.in (+76/-38)
share/icons/application/Makefile.in (+147/-128)
share/icons/inkscape.file.svg (+14/-16)
share/icons/inkscape.svg (+14/-16)
share/keys/Makefile.in (+76/-38)
share/keys/right-handed-illustration.xml (+0/-1)
share/keys/xara.xml (+0/-1)
share/markers/Makefile.in (+76/-38)
share/palettes/Makefile.in (+76/-38)
share/palettes/Tango-Palette.gpl (+2/-0)
share/patterns/Makefile.in (+76/-38)
share/screens/Makefile.in (+76/-38)
share/templates/Makefile.in (+75/-37)
share/tutorials/Makefile.in (+75/-37)
share/ui/Makefile.in (+76/-38)
src/2geom/basic-intersection.cpp (+11/-1)
src/2geom/interval.h (+1/-1)
src/2geom/rect.h (+1/-1)
src/2geom/sbasis.h (+24/-6)
src/2geom/solve-bezier-parametric.cpp (+22/-5)
src/Makefile.am (+1/-5)
src/Makefile.in (+574/-817)
src/box3d-context.cpp (+1/-1)
src/color-profile.cpp (+8/-10)
src/cxxtests.cpp (+62/-62)
src/dialogs/export.cpp (+1/-1)
src/dialogs/spellcheck.cpp (+1/-1)
src/display/canvas-grid.cpp (+5/-0)
src/display/nr-filter-gaussian.cpp (+2/-2)
src/display/sodipodi-ctrl.cpp (+7/-2)
src/display/sp-canvas.cpp (+9/-7)
src/dom/io/uristream.cpp (+24/-25)
src/dom/io/uristream.h (+38/-38)
src/dom/svgimpl.cpp (+3/-3)
src/dropper-context.cpp (+1/-1)
src/extension/effect.cpp (+1/-1)
src/extension/implementation/script.cpp (+1/-8)
src/extension/init.cpp (+1/-1)
src/extension/internal/cairo-renderer.cpp (+1/-1)
src/extension/internal/filter/filter-file.cpp (+1/-1)
src/extension/internal/filter/filter.cpp (+1/-1)
src/extension/internal/pdfinput/svg-builder.cpp (+13/-13)
src/extension/internal/wpg-input.cpp (+17/-10)
src/extension/system.cpp (+2/-1)
src/file.cpp (+1/-1)
src/gc-core.h (+1/-1)
src/gc.cpp (+13/-5)
src/gradient-drag.cpp (+1/-1)
src/helper/pixbuf-ops.cpp (+1/-1)
src/helper/sp-marshal.cpp (+7/-7)
src/helper/sp-marshal.h (+6/-6)
src/inkscape-version.cpp (+1/-1)
src/inkscape.rc (+5/-5)
src/inkview.rc (+5/-5)
src/interface.cpp (+11/-9)
src/io/inkjar.cpp (+17/-9)
src/io/inkjar.h (+4/-2)
src/io/inkscapestream.h (+1/-0)
src/io/uristream.cpp (+29/-31)
src/io/uristream.h (+40/-40)
src/io/xsltstream.cpp (+8/-11)
src/io/xsltstream.h (+19/-21)
src/libavoid/vpsc.cpp (+1/-1)
src/libcola/straightener.cpp (+3/-3)
src/libcroco/cr-parser.c (+1/-1)
src/libcroco/cr-rgb.c (+2/-2)
src/libgdl/gdl-dock-object.c (+1/-1)
src/libgdl/gdl-dock-object.h (+2/-2)
src/libgdl/gdl-tools.h (+1/-1)
src/libnrtype/FontFactory.cpp (+1/-1)
src/libnrtype/Layout-TNG-OutIter.cpp (+26/-5)
src/libvpsc/Makefile_insert (+0/-2)
src/libvpsc/block.cpp (+1/-1)
src/libvpsc/csolve_VPSC.cpp (+0/-126)
src/libvpsc/csolve_VPSC.h (+0/-60)
src/live_effects/lpe-bendpath.cpp (+6/-3)
src/live_effects/lpe-perp_bisector.h (+2/-2)
src/main.cpp (+3/-0)
src/pencil-context.cpp (+16/-9)
src/preferences.cpp (+3/-0)
src/prefix.cpp (+0/-3)
src/registrytool.cpp (+1/-1)
src/remove-last.h (+3/-2)
src/select-context.cpp (+2/-2)
src/selection-chemistry.cpp (+1/-1)
src/selection.cpp (+2/-0)
src/seltrans.cpp (+1/-1)
src/sp-conn-end.cpp (+1/-1)
src/sp-flowregion.cpp (+2/-2)
src/sp-guide.cpp (+4/-4)
src/sp-image.cpp (+4/-6)
src/sp-item-group.cpp (+2/-0)
src/sp-item-rm-unsatisfied-cns.cpp (+2/-2)
src/sp-namedview.cpp (+4/-4)
src/sp-polygon.cpp (+7/-4)
src/splivarot.cpp (+15/-5)
src/spray-context.cpp (+22/-32)
src/trace/imagemap-gdk.cpp (+8/-6)
src/trace/imagemap-gdk.h (+2/-2)
src/trace/siox.cpp (+3/-12)
src/trace/siox.h (+1/-11)
src/trace/trace.cpp (+2/-2)
src/tweak-context.cpp (+1/-1)
src/ui/dialog/filedialogimpl-gtkmm.cpp (+10/-5)
src/ui/dialog/filedialogimpl-win32.cpp (+10/-12)
src/ui/dialog/filedialogimpl-win32.h (+12/-0)
src/ui/dialog/inkscape-preferences.cpp (+5/-2)
src/ui/dialog/layers.cpp (+4/-3)
src/ui/dialog/print.cpp (+4/-3)
src/ui/tool/multi-path-manipulator.cpp (+13/-3)
src/ui/tool/node-tool.cpp (+4/-0)
src/ui/tool/node.h (+8/-0)
src/ui/tool/path-manipulator.cpp (+5/-1)
src/ui/tool/transform-handle-set.cpp (+1/-1)
src/ui/widget/registered-widget.h (+0/-3)
src/verbs.cpp (+5/-0)
src/widgets/desktop-widget.cpp (+12/-14)
src/widgets/eek-preview.cpp (+1/-1)
src/widgets/ege-paint-def.cpp (+1/-1)
src/widgets/sp-color-icc-selector.cpp (+3/-3)
src/widgets/toolbox.cpp (+1/-1)
src/xml/repr-io.cpp (+75/-15)
src/xml/repr-util.cpp (+3/-3)
test-driver (+139/-0)
To merge this branch: bzr merge lp:~valavanisalex/ubuntu/utopic/inkscape/fix-1358863
Reviewer Review Type Date Requested Status
Daniel Holbach 2014-08-19 Approve on 2014-08-22
Review via email: mp+231439@code.launchpad.net

Description of the change

Merge upstream bugfix version 0.48.5 from Debian Unstable/Testing

To post a comment you must log in.
Daniel Holbach (dholbach) wrote :

Thanks. Uploaded.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== removed directory '.pc/0001-Fix_Datamatrix_UI_issue.patch'
=== removed directory '.pc/0001-Fix_Datamatrix_UI_issue.patch/share'
=== removed directory '.pc/0001-Fix_Datamatrix_UI_issue.patch/share/extensions'
=== removed file '.pc/0001-Fix_Datamatrix_UI_issue.patch/share/extensions/render_barcode_datamatrix.inx'
--- .pc/0001-Fix_Datamatrix_UI_issue.patch/share/extensions/render_barcode_datamatrix.inx 2013-07-02 17:25:52 +0000
+++ .pc/0001-Fix_Datamatrix_UI_issue.patch/share/extensions/render_barcode_datamatrix.inx 1970-01-01 00:00:00 +0000
@@ -1,20 +0,0 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
3 <_name>Barcode - Datamatrix</_name>
4 <id>il.datamatrix</id>
5 <dependency type="executable" location="extensions">render_barcode_datamatrix.py</dependency>
6 <dependency type="executable" location="extensions">inkex.py</dependency>
7 <param name="text" type="string" _gui-text="Text">Inkscape</param>
8 <param name="rows" type="int" min="8" max="144" _gui-text="Rows">10</param>
9 <param name="cols" type="int" min="10" max="144" _gui-text="Cols">10</param>
10 <param name="size" type="int" min="1" max="1000" _gui-text="Square Size / px">4</param>
11 <effect>
12 <object-type>all</object-type>
13 <effects-menu>
14 <submenu _name="Render"/>
15 </effects-menu>
16 </effect>
17 <script>
18 <command reldir="extensions" interpreter="python">render_barcode_datamatrix.py</command>
19 </script>
20</inkscape-extension>
210
=== removed file '.pc/0001-Fix_Datamatrix_UI_issue.patch/share/extensions/render_barcode_datamatrix.py'
--- .pc/0001-Fix_Datamatrix_UI_issue.patch/share/extensions/render_barcode_datamatrix.py 2013-07-02 17:25:52 +0000
+++ .pc/0001-Fix_Datamatrix_UI_issue.patch/share/extensions/render_barcode_datamatrix.py 1970-01-01 00:00:00 +0000
@@ -1,654 +0,0 @@
1#!/usr/bin/env python
2# -*- coding: UTF-8 -*-
3'''
4Copyright (C) 2009 John Beard john.j.beard@gmail.com
5
6######DESCRIPTION######
7
8This extension renders a DataMatrix 2D barcode, as specified in
9BS ISO/IEC 16022:2006. Only ECC200 codes are considered, as these are the only
10ones recommended for an "open" system.
11
12The size of the DataMatrix is variable between 10x10 to 144x144
13
14The absolute size of the DataMatrix modules (the little squares) is also
15variable.
16
17If more data is given than can be contained in one DataMatrix,
18more than one DataMatrices will be produced.
19
20Text is encoded as ASCII (the standard provides for other options, but these are
21not implemented). Consecutive digits are encoded in a compressed form, halving
22the space required to store them.
23
24The basis processing flow is;
25 * Convert input string to codewords (modified ASCII and compressed digits)
26 * Split codewords into blocks of the right size for Reed-Solomon coding
27 * Interleave the blocks if required
28 * Apply Reed-Solomon coding
29 * De-interleave the blocks if required
30 * Place the codewords into the matrix bit by bit
31 * Render the modules in the matrix as squares
32
33######LICENCE#######
34This program is free software; you can redistribute it and/or modify
35it under the terms of the GNU General Public License as published by
36the Free Software Foundation; either version 2 of the License, or
37(at your option) any later version.
38
39This program is distributed in the hope that it will be useful,
40but WITHOUT ANY WARRANTY; without even the implied warranty of
41MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
42GNU General Public License for more details.
43
44You should have received a copy of the GNU General Public License
45along with this program; if not, write to the Free Software
46Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
47
48######VERSION HISTORY#####
49 Ver. Date Notes
50
51 0.50 2009-10-25 Full functionality, up to 144x144.
52 ASCII and compressed digit encoding only.
53'''
54
55import inkex, simplestyle
56
57import gettext
58_ = gettext.gettext
59
60#ENCODING ROUTINES ===================================================
61# Take an input string and convert it to a sequence (or sequences)
62# of codewords as specified in ISO/IEC 16022:2006 (section 5.2.3)
63#=====================================================================
64
65#create a 2d list corresponding to the 1's and 0s of the DataMatrix
66def encode(text, (nrow, ncol) ):
67 #retreive the parameters of this size of DataMatrix
68 data_nrow, data_ncol, reg_row, reg_col, nd, nc, inter = get_parameters( nrow, ncol )
69
70 if not ((nrow == 144) and (ncol == 144)): #we have a regular datamatrix
71 size144 = False
72 else: #special handling will be required by get_codewords()
73 size144 = True
74
75 #generate the codewords including padding and ECC
76 codewords = get_codewords( text, nd, nc, inter, size144 )
77
78 # break up into separate arrays if more than one DataMatrix is needed
79 module_arrays = []
80 for codeword_stream in codewords: #for each datamatrix
81 bit_array = place_bits(codeword_stream, (data_nrow*reg_row, data_ncol*reg_col)) #place the codewords' bits across the array as modules
82 module_arrays.append(add_finder_pattern( bit_array, data_nrow, data_ncol, reg_row, reg_col )) #add finder patterns around the modules
83
84 return module_arrays
85
86#return parameters for the selected datamatrix size
87# data_nrow number of rows in each data region
88# data_ncol number of cols in each data region
89# reg_row number of rows of data regions
90# reg_col number of cols of data regions
91# nd number of data codewords per reed-solomon block
92# nc number of ECC codewords per reed-solomon block
93# inter number of interleaved Reed-Solomon blocks
94def get_parameters(nrow, ncol):
95
96 #SQUARE SYMBOLS
97 if ( nrow == 10 and ncol == 10 ):
98 return 8, 8, 1, 1, 3, 5, 1
99 elif ( nrow == 12 and ncol == 12 ):
100 return 10, 10, 1, 1, 5, 7, 1
101 elif ( nrow == 14 and ncol == 14 ):
102 return 12, 12, 1, 1, 8, 10, 1
103 elif ( nrow == 16 and ncol == 16 ):
104 return 14, 14, 1, 1, 12, 12, 1
105 elif ( nrow == 18 and ncol == 18 ):
106 return 16, 16, 1, 1, 18, 14, 1
107 elif ( nrow == 20 and ncol == 20 ):
108 return 18, 18, 1, 1, 22, 18, 1
109 elif ( nrow == 22 and ncol == 22 ):
110 return 18, 18, 1, 1, 30, 20, 1
111 elif ( nrow == 24 and ncol == 24 ):
112 return 22, 22, 1, 1, 36, 24, 1
113 elif ( nrow == 26 and ncol == 26 ):
114 return 24, 24, 1, 1, 44, 28, 1
115 elif ( nrow == 32 and ncol == 32 ):
116 return 14, 14, 2, 2, 62, 36, 1
117 elif ( nrow == 36 and ncol == 36 ):
118 return 16, 16, 2, 2, 86, 42, 1
119 elif ( nrow == 40 and ncol == 40):
120 return 18, 18, 2, 2, 114, 48, 1
121 elif ( nrow == 44 and ncol == 44):
122 return 20, 20, 2, 2, 144, 56, 1
123 elif ( nrow == 48 and ncol == 48 ):
124 return 22, 22, 2, 2, 174, 68, 1
125
126 elif ( nrow == 52 and ncol == 52 ):
127 return 24, 24, 2, 2, 102, 42, 2
128 elif ( nrow == 64 and ncol == 64 ):
129 return 16, 16, 4, 4, 140, 56, 2
130
131 elif ( nrow == 72 and ncol == 72 ):
132 return 16, 16, 4, 4, 92, 36, 4
133 elif ( nrow == 80 and ncol == 80 ):
134 return 18, 18, 4, 4, 114, 48, 4
135 elif ( nrow == 88 and ncol == 88 ):
136 return 20, 20, 4, 4, 144, 56, 4
137 elif ( nrow == 96 and ncol == 96 ):
138 return 22, 22, 4, 4, 174, 68, 4
139
140 elif ( nrow == 104 and ncol == 104 ):
141 return 24, 24, 4, 4, 136, 56, 6
142 elif ( nrow == 120 and ncol == 120):
143 return 18, 18, 6, 6, 175, 68, 6
144
145 elif ( nrow == 132 and ncol == 132):
146 return 20, 20, 6, 6, 163, 62, 8
147
148 elif (nrow == 144 and ncol == 144):
149 return 22, 22, 6, 6, 0, 0, 0 #there are two separate sections of the data matrix with
150 #different interleaving and reed-solomon parameters.
151 #this will be handled separately
152
153 #RECTANGULAR SYMBOLS
154 elif ( nrow == 8 and ncol == 18 ):
155 return 6, 16, 1, 1, 5, 7, 1
156 elif ( nrow == 8 and ncol == 32 ):
157 return 6, 14, 1, 2, 10, 11, 1
158 elif ( nrow == 12 and ncol == 26 ):
159 return 10, 24, 1, 1, 16, 14, 1
160 elif ( nrow == 12 and ncol == 36 ):
161 return 10, 16, 1, 2, 22, 18, 1
162 elif ( nrow == 16 and ncol == 36 ):
163 return 14, 16, 1, 2, 32, 24, 1
164 elif ( nrow == 16 and ncol == 48 ):
165 return 14, 22, 1, 2, 49, 28, 1
166
167 #RETURN ERROR
168 else:
169 inkex.errormsg(_('Unrecognised DataMatrix size'))
170
171 return None
172
173# CODEWORD STREAM GENERATION =========================================
174#take the text input and return the codewords,
175#including the Reed-Solomon error-correcting codes.
176#=====================================================================
177
178def get_codewords( text, nd, nc, inter, size144 ):
179 #convert the data to the codewords
180 data = encode_to_ascii( text )
181
182 if not size144: #render a "normal" datamatrix
183 data_blocks = partition_data(data, nd*inter) #partition into data blocks of length nd*inter -> inter Reed-Solomon block
184
185 data_blocks = interleave( data_blocks, inter) # interleave consecutive inter blocks if required
186
187 data_blocks = reed_solomon(data_blocks, nd, nc) #generate and append the Reed-Solomon codewords
188
189 data_blocks = combine_interleaved(data_blocks, inter, nd, nc, False) #concatenate Reed-Solomon blocks bound for the same datamatrix
190
191 else: #we have a 144x144 datamatrix
192 data_blocks = partition_data(data, 1558) #partition the data into datamtrix-sized chunks (1558 =156*8 + 155*2 )
193
194 for i in range(len(data_blocks)): #for each datamtrix
195
196
197 inter = 8
198 nd = 156
199 nc = 62
200 block1 = data_blocks[i][0:156*8]
201 block1 = interleave( [block1], inter) # interleave into 8 blocks
202 block1 = reed_solomon(block1, nd, nc) #generate and append the Reed-Solomon codewords
203
204 inter = 2
205 nd = 155
206 nc = 62
207 block2 = data_blocks[i][156*8:]
208 block2 = interleave( [block2], inter) # interleave into 2 blocks
209 block2 = reed_solomon(block2, nd, nc) #generate and append the Reed-Solomon codewords
210
211 blocks = block1
212 blocks.extend(block2)
213
214 blocks = combine_interleaved(blocks, 10, nd, nc, True)
215
216 data_blocks[i] = blocks[0]
217
218
219 return data_blocks
220
221
222#Takes a codeword stream and splits up into "inter" blocks.
223#eg interleave( [1,2,3,4,5,6], 2 ) -> [1,3,5], [2,4,6]
224def interleave( blocks, inter):
225
226 if inter == 1: # if we don't have to interleave, just return the blocks
227 return blocks
228 else:
229 result = []
230 for block in blocks: #for each codeword block in the stream
231 block_length = len(block)/inter #length of each interleaved block
232 inter_blocks = [[0] * block_length for i in xrange(inter)] #the interleaved blocks
233
234 for i in range(block_length): #for each element in the interleaved blocks
235 for j in range(inter): #for each interleaved block
236 inter_blocks[j][i] = block[ i*inter + j ]
237
238 result.extend(inter_blocks) #add the interleaved blocks to the output
239
240 return result
241
242#Combine interleaved blocks into the groups for the same datamatrix
243#
244#e.g combine_interleaved( [[d1, d3, d5, e1, e3, e5], [d2, d4, d6, e2, e4, e6]], 2, 3, 3 )
245# --> [[d1, d2, d3, d4, d5, d6, e1, e2, e3, e4, e5, e6]]
246def combine_interleaved( blocks, inter, nd, nc, size144):
247 if inter == 1: #the blocks aren't interleaved
248 return blocks
249 else:
250 result = []
251 for i in range( len(blocks) / inter ): #for each group of "inter" blocks -> one full datamatrix
252 data_codewords = [] #interleaved data blocks
253
254 if size144:
255 nd_range = 1558 #1558 = 156*8 + 155*2
256 nc_range = 620 #620 = 62*8 + 62*2
257 else:
258 nd_range = nd*inter
259 nc_range = nc*inter
260
261 for j in range(nd_range): #for each codeword in the final list
262 data_codewords.append( blocks[i*inter + j%inter][j/inter] )
263
264 for j in range(nc_range): #for each block, add the ecc codewords
265 data_codewords.append( blocks[i*inter + j%inter][nd + j/inter] )
266
267 result.append(data_codewords)
268 return result
269
270#checks if an ASCII character is a digit from 0 - 9
271def is_digit( char ):
272
273 if ord(char) >= 48 and ord(char) <= 57:
274 return True
275 else:
276 return False
277
278def encode_to_ascii( text):
279
280 ascii = []
281 i = 0
282 while i < len(text):
283 #check for double digits
284 if is_digit( text[i] ) and ( i < len(text)-1) and is_digit( text[i+1] ): #if the next char is also a digit
285
286 codeword = int( text[i] + text[i+1] ) + 130
287 ascii.append( codeword )
288 i = i + 2 #move on 2 characters
289 else: #encode as a normal ascii,
290 ascii.append( ord(text[i] ) + 1 ) #codeword is ASCII value + 1 (ISO 16022:2006 5.2.3)
291 i = i + 1 #next character
292
293 return ascii
294
295
296#partition data into blocks of the appropriate size to suit the
297#Reed-Solomon block being used.
298#e.g. partition_data([1,2,3,4,5], 3) -> [[1,2,3],[4,5,PAD]]
299def partition_data( data , rs_data):
300
301 PAD_VAL = 129 # PAD codeword (ISO 16022:2006 5.2.3)
302 data_blocks = []
303 i = 0
304 while i < len(data):
305 if len(data) >= i+rs_data: #we have a whole block in our data
306 data_blocks.append( data[i:i+rs_data] )
307 i = i + rs_data
308 else: #pad out with the pad codeword
309 data_block = data[i:len(data)] #add any remaining data
310 pad_pos = len(data)
311 padded = False
312 while len(data_block) < rs_data:#and then pad with randomised pad codewords
313 if not padded:
314 data_block.append( PAD_VAL ) #add a normal pad codeword
315 padded = True
316 else:
317 data_block.append( randomise_pad_253( PAD_VAL, pad_pos) )
318 pad_pos = pad_pos + 1
319 data_blocks.append( data_block)
320 break
321
322 return data_blocks
323
324#Pad character randomisation, to prevent regular patterns appearing
325#in the data matrix
326def randomise_pad_253(pad_value, pad_position ):
327 pseudo_random_number = ( ( 149 * pad_position ) % 253 )+ 1
328 randomised = pad_value + pseudo_random_number
329 if ( randomised <= 254 ):
330 return randomised
331 else:
332 return randomised - 254
333
334# REED-SOLOMON ENCODING ROUTINES =====================================
335
336# "prod(x,y,log,alog,gf)" returns the product "x" times "y"
337def prod(x, y, log, alog, gf):
338
339 if ( x==0 or y==0):
340 return 0
341 else:
342 result = alog[ ( log[x] + log[y] ) % (gf - 1) ]
343 return result
344
345# generate the log & antilog lists:
346def gen_log_alog(gf, pp):
347 log = [0]*gf
348 alog = [0]*gf
349
350 log[0] = 1-gf
351 alog[0] = 1
352
353 for i in range(1,gf):
354 alog[i] = alog[i-1] * 2
355
356 if (alog[i] >= gf):
357 alog[i] = alog[i] ^ pp
358
359 log[alog[i]] = i
360
361 return log, alog
362
363# generate the generator polynomial coefficients:
364def gen_poly_coeffs(nc, log, alog, gf):
365 c = [0] * (nc+1)
366 c[0] = 1
367
368 for i in range(1,nc+1):
369 c[i] = c[i-1]
370
371 j = i-1
372 while j >= 1:
373 c[j] = c[j-1] ^ prod(c[j],alog[i],log,alog,gf)
374 j = j - 1
375
376 c[0] = prod(c[0],alog[i],log,alog,gf)
377
378 return c
379
380# "ReedSolomon(wd,nd,nc)" takes "nd" data codeword values in wd[]
381# and adds on "nc" check codewords, all within GF(gf) where "gf" is a
382# power of 2 and "pp" is the value of its prime modulus polynomial */
383def reed_solomon(data, nd, nc):
384 #parameters of the polynomial arithmetic
385 gf = 256 #operating on 8-bit codewords -> Galois field = 2^8 = 256
386 pp = 301 #prime modulus polynomial for ECC-200 is 0b100101101 = 301 (ISO 16022:2006 5.7.1)
387
388 log, alog = gen_log_alog(gf,pp)
389 c = gen_poly_coeffs(nc, log, alog, gf)
390
391 for block in data: #for each block of data codewords
392
393 block.extend( [0]*(nc+1) ) #extend to make space for the error codewords
394
395 #generate "nc" checkwords in the list block
396 for i in range(0, nd):
397 k = block[nd] ^ block[i]
398
399 for j in range(0,nc):
400 block[nd+j] = block[nd+j+1] ^ prod(k,c[nc-j-1],log, alog,gf)
401
402 block.pop()
403
404 return data
405
406#MODULE PLACEMENT ROUTINES===========================================
407# These routines take a steam of codewords, and place them into the
408# DataMatrix in accordance with Annex F of BS ISO/IEC 16022:2006
409
410# bit() returns the bit'th bit of the byte
411def bit(byte, bit):
412 #the MSB is bit 1, LSB is bit 8
413 return ( byte >> (8-bit) ) %2
414
415# "module" places a given bit with appropriate wrapping within array
416def module(array, nrow, ncol, row, col, bit) :
417 if (row < 0) :
418 row = row + nrow
419 col = col + 4 - ((nrow+4)%8)
420
421 if (col < 0):
422 col = col + ncol
423 row = row + 4 - ((ncol+4)%8)
424
425 array[row][col] = bit
426
427def corner1(array, nrow, ncol, char):
428 module(array, nrow, ncol, nrow-1, 0, bit(char,1));
429 module(array, nrow, ncol, nrow-1, 1, bit(char,2));
430 module(array, nrow, ncol, nrow-1, 2, bit(char,3));
431 module(array, nrow, ncol, 0, ncol-2, bit(char,4));
432 module(array, nrow, ncol, 0, ncol-1, bit(char,5));
433 module(array, nrow, ncol, 1, ncol-1, bit(char,6));
434 module(array, nrow, ncol, 2, ncol-1, bit(char,7));
435 module(array, nrow, ncol, 3, ncol-1, bit(char,8));
436
437def corner2(array, nrow, ncol, char):
438 module(array, nrow, ncol, nrow-3, 0, bit(char,1));
439 module(array, nrow, ncol, nrow-2, 0, bit(char,2));
440 module(array, nrow, ncol, nrow-1, 0, bit(char,3));
441 module(array, nrow, ncol, 0, ncol-4, bit(char,4));
442 module(array, nrow, ncol, 0, ncol-3, bit(char,5));
443 module(array, nrow, ncol, 0, ncol-2, bit(char,6));
444 module(array, nrow, ncol, 0, ncol-1, bit(char,7));
445 module(array, nrow, ncol, 1, ncol-1, bit(char,8));
446
447def corner3(array, nrow, ncol, char):
448 module(array, nrow, ncol, nrow-3, 0, bit(char,1));
449 module(array, nrow, ncol, nrow-2, 0, bit(char,2));
450 module(array, nrow, ncol, nrow-1, 0, bit(char,3));
451 module(array, nrow, ncol, 0, ncol-2, bit(char,4));
452 module(array, nrow, ncol, 0, ncol-1, bit(char,5));
453 module(array, nrow, ncol, 1, ncol-1, bit(char,6));
454 module(array, nrow, ncol, 2, ncol-1, bit(char,7));
455 module(array, nrow, ncol, 3, ncol-1, bit(char,8));
456
457def corner4(array, nrow, ncol, char):
458 module(array, nrow, ncol, nrow-1, 0, bit(char,1));
459 module(array, nrow, ncol, nrow-1, ncol-1, bit(char,2));
460 module(array, nrow, ncol, 0, ncol-3, bit(char,3));
461 module(array, nrow, ncol, 0, ncol-2, bit(char,4));
462 module(array, nrow, ncol, 0, ncol-1, bit(char,5));
463 module(array, nrow, ncol, 1, ncol-3, bit(char,6));
464 module(array, nrow, ncol, 1, ncol-2, bit(char,7));
465 module(array, nrow, ncol, 1, ncol-1, bit(char,8));
466
467#"utah" places the 8 bits of a utah-shaped symbol character in ECC200
468def utah(array, nrow, ncol, row, col, char):
469 module(array, nrow, ncol,row-2, col-2, bit(char,1))
470 module(array, nrow, ncol,row-2, col-1, bit(char,2))
471 module(array, nrow, ncol,row-1, col-2, bit(char,3))
472 module(array, nrow, ncol,row-1, col-1, bit(char,4))
473 module(array, nrow, ncol,row-1, col, bit(char,5))
474 module(array, nrow, ncol,row, col-2, bit(char,6))
475 module(array, nrow, ncol,row, col-1, bit(char,7))
476 module(array, nrow, ncol,row, col, bit(char,8))
477
478#"place_bits" fills an nrow x ncol array with the bits from the
479# codewords in data.
480def place_bits(data, (nrow, ncol)):
481# First, fill the array[] with invalid entries */
482 INVALID = 2
483 array = [[INVALID] * ncol for i in xrange(nrow)] #initialise and fill with -1's (invalid value)
484# Starting in the correct location for character #1, bit 8,...
485 char = 0
486 row = 4
487 col = 0
488 while True:
489
490 #first check for one of the special corner cases, then...
491 if ((row == nrow) and (col == 0)):
492 corner1(array, nrow, ncol, data[char])
493 char = char + 1
494 if ((row == nrow-2) and (col == 0) and (ncol%4)) :
495 corner2(array, nrow, ncol, data[char])
496 char = char + 1
497 if ((row == nrow-2) and (col == 0) and (ncol%8 == 4)):
498 corner3(array, nrow, ncol, data[char])
499 char = char + 1
500 if ((row == nrow+4) and (col == 2) and ((ncol%8) == 0)):
501 corner4(array, nrow, ncol, data[char])
502 char = char + 1
503
504 #sweep upward diagonally, inserting successive characters,...
505 while True:
506 if ((row < nrow) and (col >= 0) and (array[row][col] == INVALID)) :
507 utah(array, nrow, ncol,row,col,data[char])
508 char = char+1
509 row = row - 2
510 col = col + 2
511
512 if not((row >= 0) and (col < ncol)):
513 break
514
515 row = row + 1
516 col = col + 3
517
518 # & then sweep downward diagonally, inserting successive characters,...
519 while True:
520 if ((row >= 0) and (col < ncol) and (array[row][col] == INVALID)) :
521 utah(array, nrow, ncol,row,col,data[char])
522 char = char + 1
523 row = row + 2
524 col = col - 2
525
526 if not((row < nrow) and (col >= 0)):
527 break
528
529 row = row + 3
530 col = col + 1
531
532 #... until the entire array is scanned
533 if not((row < nrow) or (col < ncol)):
534 break
535
536 # Lastly, if the lower righthand corner is untouched, fill in fixed pattern */
537 if (array[nrow-1][ncol-1] == INVALID):
538 array[nrow-1][ncol-2] = 0
539 array[nrow-1][ncol-1] = 1
540 array[nrow-2][ncol-1] = 0
541 array[nrow-2][ncol-2] = 1
542
543 return array #return the array of 1's and 0's
544
545
546def add_finder_pattern( array, data_nrow, data_ncol, reg_row, reg_col ):
547
548 #get the total size of the datamatrix
549 nrow = (data_nrow+2) * reg_row
550 ncol = (data_ncol+2) * reg_col
551
552 datamatrix = [[0] * ncol for i in xrange(nrow)] #initialise and fill with 0's
553
554 for i in range( reg_col ): #for each column of data regions
555 for j in range(nrow):
556 datamatrix[j][i*(data_ncol+2)] = 1 #vertical black bar on left
557 datamatrix[j][i*(data_ncol+2)+data_ncol+1] = (j)%2 # alternating blocks
558
559 for i in range( reg_row): # for each row of data regions
560 for j in range(ncol):
561 datamatrix[i*(data_nrow+2)+data_nrow+1][j] = 1 #horizontal black bar at bottom
562 datamatrix[i*(data_nrow+2)][j] = (j+1)%2 # alternating blocks
563
564 for i in range( data_nrow*reg_row ):
565 for j in range( data_ncol* reg_col ):
566 dest_col = j + 1 + 2*(j/(data_ncol)) #offset by 1, plus two for every addition block
567 dest_row = i + 1 + 2*(i/(data_nrow))
568
569 datamatrix[dest_row][dest_col] = array[i][j] #transfer from the plain bit array
570
571 return datamatrix
572
573#RENDERING ROUTINES ==================================================
574# Take the array of 1's and 0's and render as a series of black
575# squares. A binary 1 is a filled square
576#=====================================================================
577
578#SVG element generation routine
579def draw_SVG_square((w,h), (x,y), parent):
580
581 style = { 'stroke' : 'none',
582 'stroke-width' : '1',
583 'fill' : '#000000'
584 }
585
586 attribs = {
587 'style' :simplestyle.formatStyle(style),
588 'height' : str(h),
589 'width' : str(w),
590 'x' : str(x),
591 'y' : str(y)
592 }
593 circ = inkex.etree.SubElement(parent, inkex.addNS('rect','svg'), attribs )
594
595#turn a 2D array of 1's and 0's into a set of black squares
596def render_data_matrix( module_arrays, size, spacing, parent):
597
598 for i in range(len(module_arrays)): #for each data matrix
599
600 height = len(module_arrays[i])
601 width = len(module_arrays[i][0] )
602
603 for y in range(height): #loop over all the modules in the datamatrix
604 for x in range(width):
605
606 if module_arrays[i][y][x] == 1: #A binary 1 is a filled square
607 draw_SVG_square((size,size), (x*size + i*spacing,y*size), parent)
608 elif module_arrays[i][y][x] != 0: #we have an invalid bit value
609 inkex.errormsg(_('Invalid bit value, this is a bug!'))
610
611class DataMatrix(inkex.Effect):
612 def __init__(self):
613 inkex.Effect.__init__(self)
614
615 #PARSE OPTIONS
616 self.OptionParser.add_option("--text",
617 action="store", type="string",
618 dest="TEXT", default='Inkscape')
619 self.OptionParser.add_option("--rows",
620 action="store", type="int",
621 dest="ROWS", default=10)
622 self.OptionParser.add_option("--cols",
623 action="store", type="int",
624 dest="COLS", default=10)
625 self.OptionParser.add_option("--size",
626 action="store", type="int",
627 dest="SIZE", default=4)
628
629 def effect(self):
630
631 so = self.options
632
633 if so.TEXT == '': #abort if converting blank text
634 inkex.errormsg(_('Please enter an input string'))
635 else:
636
637 #INKSCAPE GROUP TO CONTAIN EVERYTHING
638
639 centre = self.view_center #Put in in the centre of the current view
640 grp_transform = 'translate' + str( centre )
641 grp_name = 'DataMatrix'
642 grp_attribs = {inkex.addNS('label','inkscape'):grp_name,
643 'transform':grp_transform }
644 grp = inkex.etree.SubElement(self.current_layer, 'g', grp_attribs)#the group to put everything in
645
646 #GENERATE THE DATAMATRIX
647 encoded = encode( so.TEXT, (so.ROWS, so.COLS) ) #get the pattern of squares
648 render_data_matrix( encoded, so.SIZE, so.COLS*so.SIZE*1.5, grp ) # generate the SVG elements
649
650if __name__ == '__main__':
651 e = DataMatrix()
652 e.affect()
653
654# vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 encoding=utf-8 textwidth=99
6550
=== removed directory '.pc/0002-Drop_PS_and_PDF_support_in_MimeType.patch'
=== removed file '.pc/0002-Drop_PS_and_PDF_support_in_MimeType.patch/inkscape.desktop.in'
--- .pc/0002-Drop_PS_and_PDF_support_in_MimeType.patch/inkscape.desktop.in 2013-07-02 17:25:52 +0000
+++ .pc/0002-Drop_PS_and_PDF_support_in_MimeType.patch/inkscape.desktop.in 1970-01-01 00:00:00 +0000
@@ -1,14 +0,0 @@
1[Desktop Entry]
2Version=1.0
3_Name=Inkscape
4_GenericName=Vector Graphics Editor
5_X-GNOME-FullName=Inkscape Vector Graphics Editor
6_Comment=Create and edit Scalable Vector Graphics images
7Type=Application
8Categories=Graphics;VectorGraphics;GTK;
9MimeType=image/svg+xml;image/svg+xml-compressed;application/vnd.corel-draw;application/pdf;application/postscript;image/x-eps;application/illustrator;
10Exec=inkscape %F
11TryExec=inkscape
12Terminal=false
13StartupNotify=true
14Icon=inkscape
150
=== removed directory '.pc/0003-Fix_LP_-911146.patch'
=== removed directory '.pc/0003-Fix_LP_-911146.patch/src'
=== removed directory '.pc/0003-Fix_LP_-911146.patch/src/extension'
=== removed directory '.pc/0003-Fix_LP_-911146.patch/src/extension/implementation'
=== removed file '.pc/0003-Fix_LP_-911146.patch/src/extension/implementation/script.cpp'
--- .pc/0003-Fix_LP_-911146.patch/src/extension/implementation/script.cpp 2013-07-02 17:25:52 +0000
+++ .pc/0003-Fix_LP_-911146.patch/src/extension/implementation/script.cpp 1970-01-01 00:00:00 +0000
@@ -1,1034 +0,0 @@
1/** \file
2 * Code for handling extensions (i.e.\ scripts).
3 */
4/*
5 * Authors:
6 * Bryce Harrington <bryce@osdl.org>
7 * Ted Gould <ted@gould.cx>
8 * Jon A. Cruz <jon@joncruz.org>
9 *
10 * Copyright (C) 2002-2005,2007 Authors
11 *
12 * Released under GNU GPL, read the file 'COPYING' for more information
13 */
14
15#define __INKSCAPE_EXTENSION_IMPLEMENTATION_SCRIPT_C__
16
17#ifdef HAVE_CONFIG_H
18# include <config.h>
19#endif
20
21#include <unistd.h>
22
23#include <errno.h>
24#include <glib.h>
25#include <glib/gstdio.h>
26#include <gtkmm.h>
27
28#include "ui/view/view.h"
29#include "desktop-handles.h"
30#include "desktop.h"
31#include "selection.h"
32#include "sp-namedview.h"
33#include "io/sys.h"
34#include "preferences.h"
35#include "../system.h"
36#include "extension/effect.h"
37#include "extension/output.h"
38#include "extension/input.h"
39#include "extension/db.h"
40#include "script.h"
41#include "dialogs/dialog-events.h"
42#include "application/application.h"
43#include "xml/node.h"
44#include "xml/attribute-record.h"
45
46#include "util/glib-list-iterators.h"
47#include "path-prefix.h"
48
49
50#ifdef WIN32
51#include <windows.h>
52#include <sys/stat.h>
53#include "registrytool.h"
54#endif
55
56
57
58/** This is the command buffer that gets allocated from the stack */
59#define BUFSIZE (255)
60
61
62
63/* Namespaces */
64namespace Inkscape {
65namespace Extension {
66namespace Implementation {
67
68/** \brief Make GTK+ events continue to come through a little bit
69
70 This just keeps coming the events through so that we'll make the GUI
71 update and look pretty.
72*/
73void Script::pump_events (void) {
74 while ( Gtk::Main::events_pending() ) {
75 Gtk::Main::iteration();
76 }
77 return;
78}
79
80
81/** \brief A table of what interpreters to call for a given language
82
83 This table is used to keep track of all the programs to execute a
84 given script. It also tracks the preference to use to overwrite
85 the given interpreter to a custom one per user.
86*/
87Script::interpreter_t const Script::interpreterTab[] = {
88 {"perl", "perl-interpreter", "perl" },
89#ifdef WIN32
90 {"python", "python-interpreter", "pythonw" },
91#else
92 {"python", "python-interpreter", "python" },
93#endif
94 {"ruby", "ruby-interpreter", "ruby" },
95 {"shell", "shell-interpreter", "sh" },
96 { NULL, NULL, NULL }
97};
98
99
100
101/** \brief Look up an interpreter name, and translate to something that
102 is executable
103 \param interpNameArg The name of the interpreter that we're looking
104 for, should be an entry in interpreterTab
105*/
106std::string Script::resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
107{
108 interpreter_t const *interp = 0;
109 bool foundInterp = false;
110 for (interp = interpreterTab ; interp->identity ; interp++ ){
111 if (interpNameArg == interp->identity) {
112 foundInterp = true;
113 break;
114 }
115 }
116
117 // Do we have a supported interpreter type?
118 if (!foundInterp) {
119 return "";
120 }
121 std::string interpreter_path = Glib::filename_from_utf8(interp->defaultval);
122
123 // 1. Check preferences for an override.
124 // Note: this must be an absolute path.
125 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
126 Glib::ustring prefInterp = prefs->getString("/extensions/" + Glib::ustring(interp->prefstring));
127
128 if (!prefInterp.empty()) {
129 interpreter_path = Glib::filename_from_utf8(prefInterp);
130 }
131
132 // 2. Search the path.
133 // Do this on all systems, for consistency.
134 // PATH is set up to contain the Python and Perl binary directories
135 // on Windows, so no extra code is necessary.
136 if (!Glib::path_is_absolute(interpreter_path)) {
137 interpreter_path = Glib::find_program_in_path(interpreter_path);
138 }
139 return interpreter_path;
140}
141
142/** \brief This function creates a script object and sets up the
143 variables.
144 \return A script object
145
146 This function just sets the command to NULL. It should get built
147 officially in the load function. This allows for less allocation
148 of memory in the unloaded state.
149*/
150Script::Script() :
151 Implementation()
152{
153}
154
155/**
156 * brief Destructor
157 */
158Script::~Script()
159{
160}
161
162
163
164/**
165 \return A string with the complete string with the relative directory expanded
166 \brief This function takes in a Repr that contains a reldir entry
167 and returns that data with the relative directory expanded.
168 Mostly it is here so that relative directories all get used
169 the same way.
170 \param reprin The Inkscape::XML::Node with the reldir in it.
171
172 Basically this function looks at an attribute of the Repr, and makes
173 a decision based on that. Currently, it is only working with the
174 'extensions' relative directory, but there will be more of them.
175 One thing to notice is that this function always returns an allocated
176 string. This means that the caller of this function can always
177 free what they are given (and should do it too!).
178*/
179std::string
180Script::solve_reldir(Inkscape::XML::Node *reprin) {
181
182 gchar const *s = reprin->attribute("reldir");
183
184 // right now the only recognized relative directory is "extensions"
185 if (!s || Glib::ustring(s) != "extensions") {
186 Glib::ustring str = sp_repr_children(reprin)->content();
187 return str;
188 }
189
190 Glib::ustring reldir = s;
191 for (unsigned int i=0;
192 i < Inkscape::Extension::Extension::search_path.size();
193 i++) {
194
195 gchar * fname = g_build_filename(
196 Inkscape::Extension::Extension::search_path[i],
197 sp_repr_children(reprin)->content(),
198 NULL);
199 Glib::ustring filename = fname;
200 g_free(fname);
201 //printf("Filename: %s\n", filename.c_str());
202 if ( Inkscape::IO::file_test(filename.c_str(), G_FILE_TEST_EXISTS) ) {
203 return Glib::filename_from_utf8(filename);
204 }
205 }
206
207 return "";
208}
209
210
211
212/**
213 \return Whether the command given exists, including in the path
214 \brief This function is used to find out if something exists for
215 the check command. It can look in the path if required.
216 \param command The command or file that should be looked for
217
218 The first thing that this function does is check to see if the
219 incoming file name has a directory delimiter in it. This would
220 mean that it wants to control the directories, and should be
221 used directly.
222
223 If not, the path is used. Each entry in the path is stepped through,
224 attached to the string, and then tested. If the file is found
225 then a TRUE is returned. If we get all the way through the path
226 then a FALSE is returned, the command could not be found.
227*/
228bool Script::check_existence(const std::string &command)
229{
230 // Check the simple case first
231 if (command.empty()) {
232 return false;
233 }
234
235 //Don't search when it is an absolute path. */
236 if (Glib::path_is_absolute(command)) {
237 if (Glib::file_test(command, Glib::FILE_TEST_EXISTS)) {
238 return true;
239 } else {
240 return false;
241 }
242 }
243
244 // First search in the current directory
245 std::string path = G_SEARCHPATH_SEPARATOR_S;
246 path.append(";");
247 // And then in the PATH environment variable.
248 path.append(Glib::getenv("PATH"));
249
250 std::string::size_type pos = 0;
251 std::string::size_type pos2 = 0;
252 while ( pos < path.size() ) {
253
254 std::string localPath;
255
256 pos2 = path.find(G_SEARCHPATH_SEPARATOR, pos);
257 if (pos2 == path.npos) {
258 localPath = path.substr(pos);
259 pos = path.size();
260 } else {
261 localPath = path.substr(pos, pos2-pos);
262 pos = pos2+1;
263 }
264
265 //printf("### %s\n", localPath.c_str());
266 std::string candidatePath =
267 Glib::build_filename(localPath, command);
268
269 if (Glib::file_test(candidatePath,
270 Glib::FILE_TEST_EXISTS)) {
271 return true;
272 }
273
274 }
275
276 return false;
277}
278
279
280
281
282
283/**
284 \return none
285 \brief This function 'loads' an extention, basically it determines
286 the full command for the extention and stores that.
287 \param module The extention to be loaded.
288
289 The most difficult part about this function is finding the actual
290 command through all of the Reprs. Basically it is hidden down a
291 couple of layers, and so the code has to move down too. When
292 the command is actually found, it has its relative directory
293 solved.
294
295 At that point all of the loops are exited, and there is an
296 if statement to make sure they didn't exit because of not finding
297 the command. If that's the case, the extention doesn't get loaded
298 and should error out at a higher level.
299*/
300
301bool Script::load(Inkscape::Extension::Extension *module)
302{
303 if (module->loaded()) {
304 return true;
305 }
306
307 helper_extension = "";
308
309 /* This should probably check to find the executable... */
310 Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
311 while (child_repr != NULL) {
312 if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "script")) {
313 child_repr = sp_repr_children(child_repr);
314 while (child_repr != NULL) {
315 if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "command")) {
316 const gchar *interpretstr = child_repr->attribute("interpreter");
317 if (interpretstr != NULL) {
318 std::string interpString = resolveInterpreterExecutable(interpretstr);
319 command.insert(command.end(), interpString);
320 }
321 command.insert(command.end(), solve_reldir(child_repr));
322 }
323 if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "helper_extension")) {
324 helper_extension = sp_repr_children(child_repr)->content();
325 }
326 child_repr = sp_repr_next(child_repr);
327 }
328
329 break;
330 }
331 child_repr = sp_repr_next(child_repr);
332 }
333
334 //g_return_val_if_fail(command.length() > 0, false);
335
336 return true;
337}
338
339
340/**
341 \return None.
342 \brief Unload this puppy!
343 \param module Extension to be unloaded.
344
345 This function just sets the module to unloaded. It free's the
346 command if it has been allocated.
347*/
348void Script::unload(Inkscape::Extension::Extension */*module*/)
349{
350 command.clear();
351 helper_extension = "";
352}
353
354
355
356
357/**
358 \return Whether the check passed or not
359 \brief Check every dependency that was given to make sure we should keep this extension
360 \param module The Extension in question
361
362*/
363bool
364Script::check(Inkscape::Extension::Extension *module)
365{
366 int script_count = 0;
367 Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
368 while (child_repr != NULL) {
369 if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "script")) {
370 script_count++;
371 child_repr = sp_repr_children(child_repr);
372 while (child_repr != NULL) {
373 if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "check")) {
374 std::string command_text = solve_reldir(child_repr);
375 if (!command_text.empty()) {
376 /* I've got the command */
377 bool existance = check_existence(command_text);
378 if (!existance) {
379 return false;
380 }
381 } else {
382 return false;
383 }
384 }
385
386 if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "helper_extension")) {
387 gchar const *helper = sp_repr_children(child_repr)->content();
388 if (Inkscape::Extension::db.get(helper) == NULL) {
389 return false;
390 }
391 }
392
393 child_repr = sp_repr_next(child_repr);
394 }
395
396 break;
397 }
398 child_repr = sp_repr_next(child_repr);
399 }
400
401 if (script_count == 0) {
402 return false;
403 }
404
405 return true;
406}
407
408class ScriptDocCache : public ImplementationDocumentCache {
409 friend class Script;
410protected:
411 std::string _filename;
412 int _tempfd;
413public:
414 ScriptDocCache (Inkscape::UI::View::View * view);
415 ~ScriptDocCache ( );
416};
417
418ScriptDocCache::ScriptDocCache (Inkscape::UI::View::View * view) :
419 ImplementationDocumentCache(view),
420 _filename(""),
421 _tempfd(0)
422{
423 try {
424 _tempfd = Inkscape::IO::file_open_tmp(_filename, "ink_ext_XXXXXX.svg");
425 } catch (...) {
426 /// \todo Popup dialog here
427 return;
428 }
429
430 SPDesktop *desktop = (SPDesktop *) view;
431 sp_namedview_document_from_window(desktop);
432
433 Inkscape::Extension::save(
434 Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
435 view->doc(), _filename.c_str(), false, false, false, Inkscape::Extension::FILE_SAVE_METHOD_TEMPORARY);
436
437 return;
438}
439
440ScriptDocCache::~ScriptDocCache ( )
441{
442 close(_tempfd);
443 unlink(_filename.c_str());
444}
445
446ImplementationDocumentCache *Script::newDocCache( Inkscape::Extension::Extension * /*ext*/, Inkscape::UI::View::View * view ) {
447 return new ScriptDocCache(view);
448}
449
450
451/**
452 \return A dialog for preferences
453 \brief A stub funtion right now
454 \param module Module who's preferences need getting
455 \param filename Hey, the file you're getting might be important
456
457 This function should really do something, right now it doesn't.
458*/
459Gtk::Widget *Script::prefs_input(Inkscape::Extension::Input *module,
460 const gchar */*filename*/)
461{
462 return module->autogui(NULL, NULL);
463}
464
465
466
467/**
468 \return A dialog for preferences
469 \brief A stub funtion right now
470 \param module Module whose preferences need getting
471
472 This function should really do something, right now it doesn't.
473*/
474Gtk::Widget *Script::prefs_output(Inkscape::Extension::Output *module)
475{
476 return module->autogui(NULL, NULL);
477}
478
479/**
480 \return A new document that has been opened
481 \brief This function uses a filename that is put in, and calls
482 the extension's command to create an SVG file which is
483 returned.
484 \param module Extension to use.
485 \param filename File to open.
486
487 First things first, this function needs a temporary file name. To
488 create on of those the function g_file_open_tmp is used with
489 the header of ink_ext_.
490
491 The extension is then executed using the 'execute' function
492 with the filname coming in, and the temporary filename. After
493 That executing, the SVG should be in the temporary file.
494
495 Finally, the temporary file is opened using the SVG input module and
496 a document is returned. That document has its filename set to
497 the incoming filename (so that it's not the temporary filename).
498 That document is then returned from this function.
499*/
500SPDocument *Script::open(Inkscape::Extension::Input *module,
501 const gchar *filenameArg)
502{
503 std::list<std::string> params;
504 module->paramListString(params);
505
506 std::string tempfilename_out;
507 int tempfd_out = 0;
508 try {
509 tempfd_out = Inkscape::IO::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
510 } catch (...) {
511 /// \todo Popup dialog here
512 return NULL;
513 }
514
515 std::string lfilename = Glib::filename_from_utf8(filenameArg);
516
517 file_listener fileout;
518 int data_read = execute(command, params, lfilename, fileout);
519 fileout.toFile(tempfilename_out);
520
521 SPDocument * mydoc = NULL;
522 if (data_read > 10) {
523 if (helper_extension.size()==0) {
524 mydoc = Inkscape::Extension::open(
525 Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
526 tempfilename_out.c_str());
527 } else {
528 mydoc = Inkscape::Extension::open(
529 Inkscape::Extension::db.get(helper_extension.c_str()),
530 tempfilename_out.c_str());
531 }
532 } // data_read
533
534 if (mydoc != NULL) {
535 g_free(mydoc->base);
536 mydoc->base = NULL;
537 sp_document_change_uri_and_hrefs(mydoc, filenameArg);
538 }
539
540 // make sure we don't leak file descriptors from g_file_open_tmp
541 close(tempfd_out);
542
543 unlink(tempfilename_out.c_str());
544
545 return mydoc;
546} // open
547
548
549
550/**
551 \return none
552 \brief This function uses an extention to save a document. It first
553 creates an SVG file of the document, and then runs it through
554 the script.
555 \param module Extention to be used
556 \param doc Document to be saved
557 \param filename The name to save the final file as
558 \return false in case of any failure writing the file, otherwise true
559
560 Well, at some point people need to save - it is really what makes
561 the entire application useful. And, it is possible that someone
562 would want to use an extetion for this, so we need a function to
563 do that eh?
564
565 First things first, the document is saved to a temporary file that
566 is an SVG file. To get the temporary filename g_file_open_tmp is used with
567 ink_ext_ as a prefix. Don't worry, this file gets deleted at the
568 end of the function.
569
570 After we have the SVG file, then extention_execute is called with
571 the temporary file name and the final output filename. This should
572 put the output of the script into the final output file. We then
573 delete the temporary file.
574*/
575void Script::save(Inkscape::Extension::Output *module,
576 SPDocument *doc,
577 const gchar *filenameArg)
578{
579 std::list<std::string> params;
580 module->paramListString(params);
581
582 std::string tempfilename_in;
583 int tempfd_in = 0;
584 try {
585 tempfd_in = Inkscape::IO::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX.svg");
586 } catch (...) {
587 /// \todo Popup dialog here
588 throw Inkscape::Extension::Output::save_failed();
589 }
590
591 if (helper_extension.size() == 0) {
592 Inkscape::Extension::save(
593 Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
594 doc, tempfilename_in.c_str(), false, false, false,
595 Inkscape::Extension::FILE_SAVE_METHOD_TEMPORARY);
596 } else {
597 Inkscape::Extension::save(
598 Inkscape::Extension::db.get(helper_extension.c_str()),
599 doc, tempfilename_in.c_str(), false, false, false,
600 Inkscape::Extension::FILE_SAVE_METHOD_TEMPORARY);
601 }
602
603
604 file_listener fileout;
605 execute(command, params, tempfilename_in, fileout);
606
607 std::string lfilename = Glib::filename_from_utf8(filenameArg);
608 bool success = fileout.toFile(lfilename);
609
610 // make sure we don't leak file descriptors from g_file_open_tmp
611 close(tempfd_in);
612 // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
613 unlink(tempfilename_in.c_str());
614
615 if (success == false) {
616 throw Inkscape::Extension::Output::save_failed();
617 }
618
619 return;
620}
621
622
623
624/**
625 \return none
626 \brief This function uses an extention as a effect on a document.
627 \param module Extention to effect with.
628 \param doc Document to run through the effect.
629
630 This function is a little bit trickier than the previous two. It
631 needs two temporary files to get it's work done. Both of these
632 files have random names created for them using the g_file_open_temp function
633 with the ink_ext_ prefix in the temporary directory. Like the other
634 functions, the temporary files are deleted at the end.
635
636 To save/load the two temporary documents (both are SVG) the internal
637 modules for SVG load and save are used. They are both used through
638 the module system function by passing their keys into the functions.
639
640 The command itself is built a little bit differently than in other
641 functions because the effect support selections. So on the command
642 line a list of all the ids that are selected is included. Currently,
643 this only works for a single selected object, but there will be more.
644 The command string is filled with the data, and then after the execution
645 it is freed.
646
647 The execute function is used at the core of this function
648 to execute the Script on the two SVG documents (actually only one
649 exists at the time, the other is created by that script). At that
650 point both should be full, and the second one is loaded.
651*/
652void Script::effect(Inkscape::Extension::Effect *module,
653 Inkscape::UI::View::View *doc,
654 ImplementationDocumentCache * docCache)
655{
656 if (docCache == NULL) {
657 docCache = newDocCache(module, doc);
658 }
659 ScriptDocCache * dc = dynamic_cast<ScriptDocCache *>(docCache);
660 if (dc == NULL) {
661 printf("TOO BAD TO LIVE!!!");
662 exit(1);
663 }
664
665 SPDesktop *desktop = (SPDesktop *)doc;
666 sp_namedview_document_from_window(desktop);
667
668 std::list<std::string> params;
669 module->paramListString(params);
670
671 if (module->no_doc) {
672 // this is a no-doc extension, e.g. a Help menu command;
673 // just run the command without any files, ignoring errors
674
675 Glib::ustring empty;
676 file_listener outfile;
677 execute(command, params, empty, outfile);
678
679 return;
680 }
681
682 std::string tempfilename_out;
683 int tempfd_out = 0;
684 try {
685 tempfd_out = Inkscape::IO::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
686 } catch (...) {
687 /// \todo Popup dialog here
688 return;
689 }
690
691 if (desktop != NULL) {
692 Inkscape::Util::GSListConstIterator<SPItem *> selected =
693 sp_desktop_selection(desktop)->itemList();
694 while ( selected != NULL ) {
695 Glib::ustring selected_id;
696 selected_id += "--id=";
697 selected_id += (*selected)->getId();
698 params.insert(params.begin(), selected_id);
699 ++selected;
700 }
701 }
702
703 file_listener fileout;
704 int data_read = execute(command, params, dc->_filename, fileout);
705 fileout.toFile(tempfilename_out);
706
707 pump_events();
708
709 SPDocument * mydoc = NULL;
710 if (data_read > 10) {
711 mydoc = Inkscape::Extension::open(
712 Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
713 tempfilename_out.c_str());
714 } // data_read
715
716 pump_events();
717
718 // make sure we don't leak file descriptors from g_file_open_tmp
719 close(tempfd_out);
720
721 g_unlink(tempfilename_out.c_str());
722
723 /* Do something with mydoc.... */
724 if (mydoc) {
725 doc->doc()->emitReconstructionStart();
726 copy_doc(doc->doc()->rroot, mydoc->rroot);
727 doc->doc()->emitReconstructionFinish();
728 SPObject *layer = NULL;
729
730 // Getting the named view from the document generated by the extension
731 SPNamedView *nv = sp_document_namedview(mydoc, NULL);
732
733 //Check if it has a default layer set up
734 if ( nv != NULL){
735 if( nv->default_layer_id != 0 ) {
736 SPDocument *document = desktop->doc();
737 //If so, get that layer
738 layer = document->getObjectById(g_quark_to_string(nv->default_layer_id));
739 }
740 }
741
742 sp_namedview_update_layers_from_document(desktop);
743 //If that layer exists,
744 if (layer) {
745 //set the current layer
746 desktop->setCurrentLayer(layer);
747 }
748 mydoc->release();
749 }
750
751 return;
752}
753
754
755
756/**
757 \brief A function to take all the svg elements from one document
758 and put them in another.
759 \param oldroot The root node of the document to be replaced
760 \param newroot The root node of the document to replace it with
761
762 This function first deletes all of the data in the old document. It
763 does this by creating a list of what needs to be deleted, and then
764 goes through the list. This two pass approach removes issues with
765 the list being change while parsing through it. Lots of nasty bugs.
766
767 Then, it goes through the new document, duplicating all of the
768 elements and putting them into the old document. The copy
769 is then complete.
770*/
771void Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
772{
773 std::vector<Inkscape::XML::Node *> delete_list;
774 Inkscape::XML::Node * oldroot_namedview = NULL;
775
776 for (Inkscape::XML::Node * child = oldroot->firstChild();
777 child != NULL;
778 child = child->next()) {
779 if (!strcmp("sodipodi:namedview", child->name())) {
780 oldroot_namedview = child;
781 for (Inkscape::XML::Node * oldroot_namedview_child = child->firstChild();
782 oldroot_namedview_child != NULL;
783 oldroot_namedview_child = oldroot_namedview_child->next()) {
784 delete_list.push_back(oldroot_namedview_child);
785 }
786 } else {
787 delete_list.push_back(child);
788 }
789 }
790 for (unsigned int i = 0; i < delete_list.size(); i++) {
791 sp_repr_unparent(delete_list[i]);
792 }
793
794 for (Inkscape::XML::Node * child = newroot->firstChild();
795 child != NULL;
796 child = child->next()) {
797 if (!strcmp("sodipodi:namedview", child->name())) {
798 if (oldroot_namedview != NULL) {
799 for (Inkscape::XML::Node * newroot_namedview_child = child->firstChild();
800 newroot_namedview_child != NULL;
801 newroot_namedview_child = newroot_namedview_child->next()) {
802 oldroot_namedview->appendChild(newroot_namedview_child->duplicate(oldroot->document()));
803 }
804 }
805 } else {
806 oldroot->appendChild(child->duplicate(oldroot->document()));
807 }
808 }
809
810 {
811 using Inkscape::Util::List;
812 using Inkscape::XML::AttributeRecord;
813 std::vector<gchar const *> attribs;
814
815 // Make a list of all attributes of the old root node.
816 for (List<AttributeRecord const> iter = oldroot->attributeList(); iter; ++iter) {
817 attribs.push_back(g_quark_to_string(iter->key));
818 }
819
820 // Delete the attributes of the old root nodes.
821 for (std::vector<gchar const *>::const_iterator it = attribs.begin(); it != attribs.end(); it++) {
822 oldroot->setAttribute(*it, NULL);
823 }
824
825 // Set the new attributes.
826 for (List<AttributeRecord const> iter = newroot->attributeList(); iter; ++iter) {
827 gchar const *name = g_quark_to_string(iter->key);
828 oldroot->setAttribute(name, newroot->attribute(name));
829 }
830 }
831
832 /** \todo Restore correct layer */
833 /** \todo Restore correct selection */
834}
835
836/** \brief This function checks the stderr file, and if it has data,
837 shows it in a warning dialog to the user
838 \param filename Filename of the stderr file
839*/
840void Script::checkStderr (const Glib::ustring &data,
841 Gtk::MessageType type,
842 const Glib::ustring &message)
843{
844 Gtk::MessageDialog warning(message, false, type, Gtk::BUTTONS_OK, true);
845 warning.set_resizable(true);
846 GtkWidget *dlg = GTK_WIDGET(warning.gobj());
847 sp_transientize(dlg);
848
849 Gtk::VBox * vbox = warning.get_vbox();
850
851 /* Gtk::TextView * textview = new Gtk::TextView(Gtk::TextBuffer::create()); */
852 Gtk::TextView * textview = new Gtk::TextView();
853 textview->set_editable(false);
854 textview->set_wrap_mode(Gtk::WRAP_WORD);
855 textview->show();
856
857 // Remove the last character
858 char *errormsg = (char*) data.c_str();
859 while (*errormsg != '\0') errormsg++;
860 errormsg -= 1;
861 *errormsg = '\0';
862
863 textview->get_buffer()->set_text(_(data.c_str()));
864
865 Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow();
866 scrollwindow->add(*textview);
867 scrollwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
868 scrollwindow->set_shadow_type(Gtk::SHADOW_IN);
869 scrollwindow->show();
870 scrollwindow->set_size_request(0, 60);
871
872 vbox->pack_start(*scrollwindow, true, true, 5 /* fix these */);
873
874 warning.run();
875
876 return;
877}
878
879bool Script::cancelProcessing (void) {
880 _canceled = true;
881 _main_loop->quit();
882 Glib::spawn_close_pid(_pid);
883
884 return true;
885}
886
887
888/** \brief This is the core of the extension file as it actually does
889 the execution of the extension.
890 \param in_command The command to be executed
891 \param filein Filename coming in
892 \param fileout Filename of the out file
893 \return Number of bytes that were read into the output file.
894
895 The first thing that this function does is build the command to be
896 executed. This consists of the first string (in_command) and then
897 the filename for input (filein). This file is put on the command
898 line.
899
900 The next thing is that this function does is open a pipe to the
901 command and get the file handle in the ppipe variable. It then
902 opens the output file with the output file handle. Both of these
903 operations are checked extensively for errors.
904
905 After both are opened, then the data is copied from the output
906 of the pipe into the file out using fread and fwrite. These two
907 functions are used because of their primitive nature they make
908 no assumptions about the data. A buffer is used in the transfer,
909 but the output of fread is stored so the exact number of bytes
910 is handled gracefully.
911
912 At the very end (after the data has been copied) both of the files
913 are closed, and we return to what we were doing.
914*/
915int Script::execute (const std::list<std::string> &in_command,
916 const std::list<std::string> &in_params,
917 const Glib::ustring &filein,
918 file_listener &fileout)
919{
920 g_return_val_if_fail(!in_command.empty(), 0);
921 // printf("Executing\n");
922
923 std::vector<std::string> argv;
924
925 bool interpreted = (in_command.size() == 2);
926 std::string program = in_command.front();
927 std::string script = interpreted ? in_command.back() : "";
928 std::string working_directory = "";
929
930 // Use Glib::find_program_in_path instead of the equivalent
931 // Glib::spawn_* functionality, because _wspawnp is broken on Windows:
932 // it doesn't work when PATH contains Unicode directories
933 if (!Glib::path_is_absolute(program)) {
934 program = Glib::find_program_in_path(program);
935 }
936 argv.push_back(program);
937
938 if (interpreted) {
939 // On Windows, Python garbles Unicode command line parameters
940 // in an useless way. This means extensions fail when Inkscape
941 // is run from an Unicode directory.
942 // As a workaround, we set the working directory to the one
943 // containing the script.
944 working_directory = Glib::path_get_dirname(script);
945 script = Glib::path_get_basename(script);
946 #ifdef G_OS_WIN32
947 // ANNOYING: glibmm does not wrap g_win32_locale_filename_from_utf8
948 gchar *workdir_s = g_win32_locale_filename_from_utf8(working_directory.data());
949 working_directory = workdir_s;
950 g_free(workdir_s);
951 #endif
952
953 argv.push_back(script);
954 }
955
956 // assemble the rest of argv
957 std::copy(in_params.begin(), in_params.end(), std::back_inserter(argv));
958 if (!filein.empty()) {
959 argv.push_back(filein);
960 }
961
962 int stdout_pipe, stderr_pipe;
963
964 try {
965 Glib::spawn_async_with_pipes(working_directory, // working directory
966 argv, // arg v
967 static_cast<Glib::SpawnFlags>(0), // no flags
968 sigc::slot<void>(),
969 &_pid, // Pid
970 NULL, // STDIN
971 &stdout_pipe, // STDOUT
972 &stderr_pipe); // STDERR
973 } catch (Glib::Error e) {
974 printf("Can't Spawn!!! spawn returns: %s\n", e.what().data());
975 return 0;
976 }
977
978 _main_loop = Glib::MainLoop::create(false);
979
980 file_listener fileerr;
981 fileout.init(stdout_pipe, _main_loop);
982 fileerr.init(stderr_pipe, _main_loop);
983
984 _canceled = false;
985 _main_loop->run();
986
987 // Ensure all the data is out of the pipe
988 while (!fileout.isDead()) {
989 fileout.read(Glib::IO_IN);
990 }
991 while (!fileerr.isDead()) {
992 fileerr.read(Glib::IO_IN);
993 }
994
995 if (_canceled) {
996 // std::cout << "Script Canceled" << std::endl;
997 return 0;
998 }
999
1000 Glib::ustring stderr_data = fileerr.string();
1001 if (stderr_data.length() != 0 &&
1002 Inkscape::NSApplication::Application::getUseGui()
1003 ) {
1004 checkStderr(stderr_data, Gtk::MESSAGE_INFO,
1005 _("Inkscape has received additional data from the script executed. "
1006 "The script did not return an error, but this may indicate the results will not be as expected."));
1007 }
1008
1009 Glib::ustring stdout_data = fileout.string();
1010 if (stdout_data.length() == 0) {
1011 return 0;
1012 }
1013
1014 // std::cout << "Finishing Execution." << std::endl;
1015 return stdout_data.length();
1016}
1017
1018
1019
1020
1021} // namespace Implementation
1022} // namespace Extension
1023} // namespace Inkscape
1024
1025/*
1026 Local Variables:
1027 mode:c++
1028 c-file-style:"stroustrup"
1029 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1030 indent-tabs-mode:nil
1031 fill-column:99
1032 End:
1033*/
1034// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
10350
=== removed directory '.pc/0004-Fix_FTBFS_on_gcc-4.8.patch'
=== removed directory '.pc/0004-Fix_FTBFS_on_gcc-4.8.patch/src'
=== removed file '.pc/0004-Fix_FTBFS_on_gcc-4.8.patch/src/connector-context.cpp'
--- .pc/0004-Fix_FTBFS_on_gcc-4.8.patch/src/connector-context.cpp 2013-07-02 17:25:52 +0000
+++ .pc/0004-Fix_FTBFS_on_gcc-4.8.patch/src/connector-context.cpp 1970-01-01 00:00:00 +0000
@@ -1,2041 +0,0 @@
1/*
2 * Connector creation tool
3 *
4 * Authors:
5 * Michael Wybrow <mjwybrow@users.sourceforge.net>
6 *
7 * Copyright (C) 2005-2008 Michael Wybrow
8 * Copyright (C) 2009 Monash University
9 *
10 * Released under GNU GPL, read the file 'COPYING' for more information
11 *
12 * TODO:
13 * o Show a visual indicator for objects with the 'avoid' property set.
14 * o Allow user to change a object between a path and connector through
15 * the interface.
16 * o Create an interface for setting markers (arrow heads).
17 * o Better distinguish between paths and connectors to prevent problems
18 * in the node tool and paths accidentally being turned into connectors
19 * in the connector tool. Perhaps have a way to convert between.
20 * o Only call libavoid's updateEndPoint as required. Currently we do it
21 * for both endpoints, even if only one is moving.
22 * o Allow user-placeable connection points.
23 * o Deal sanely with connectors with both endpoints attached to the
24 * same connection point, and drawing of connectors attaching
25 * overlapping shapes (currently tries to adjust connector to be
26 * outside both bounding boxes).
27 * o Fix many special cases related to connectors updating,
28 * e.g., copying a couple of shapes and a connector that are
29 * attached to each other.
30 * e.g., detach connector when it is moved or transformed in
31 * one of the other contexts.
32 * o Cope with shapes whose ids change when they have attached
33 * connectors.
34 * o During dragging motion, gobble up to and use the final motion event.
35 * Gobbling away all duplicates after the current can occasionally result
36 * in the path lagging behind the mouse cursor if it is no longer being
37 * dragged.
38 * o Fix up libavoid's representation after undo actions. It doesn't see
39 * any transform signals and hence doesn't know shapes have moved back to
40 * there earlier positions.
41 * o Decide whether drawing/editing mode should be an Inkscape preference
42 * or the connector tool should always start in drawing mode.
43 * o Correct the problem with switching to the select tool when pressing
44 * space bar (there are moments when it refuses to do so).
45 *
46 * ----------------------------------------------------------------------------
47 *
48 * mjwybrow's observations on acracan's Summer of Code connector work:
49 *
50 * - GUI comments:
51 *
52 * - Buttons for adding and removing user-specified connection
53 * points should probably have "+" and "-" symbols on them so they
54 * are consistent with the similar buttons for the node tool.
55 * - Controls on the connector tool be should be reordered logically,
56 * possibly as follows:
57 *
58 * *Connector*: [Polyline-radio-button] [Orthgonal-radio-button]
59 * [Curvature-control] | *Shape*: [Avoid-button] [Dont-avoid-button]
60 * [Spacing-control] | *Connection pts*: [Edit-mode] [Add-pt] [Rm-pt]
61 *
62 * I think that the network layout controls be moved to the
63 * Align and Distribute dialog (there is already the layout button
64 * there, but no options are exposed).
65 *
66 * I think that the style change between polyline and orthogonal
67 * would be much clearer with two buttons (radio behaviour -- just
68 * one is true).
69 *
70 * The other tools show a label change from "New:" to "Change:"
71 * depending on whether an object is selected. We could consider
72 * this but there may not be space.
73 *
74 * The Add-pt and Rm-pt buttons should be greyed out (inactive) if
75 * we are not in connection point editing mode. And probably also
76 * if there is no shape selected, i.e. at the times they have no
77 * effect when clicked.
78 *
79 * Likewise for the avoid/ignore shapes buttons. These should be
80 * inactive when a shape is not selected in the connector context.
81 *
82 * - When creating/editing connection points:
83 *
84 * - Strange things can happen if you have connectors selected, or
85 * try rerouting connectors by dragging their endpoints when in
86 * connection point editing mode.
87 *
88 * - Possibly the selected shape's connection points should always
89 * be shown (i.e., have knots) when in editing mode.
90 *
91 * - It is a little strange to be able to place connection points
92 * competely outside shapes. Especially when you later can't draw
93 * connectors to them since the knots are only visible when you
94 * are over the shape. I think that you should only be able to
95 * place connection points inside or on the boundary of the shape
96 * itself.
97 *
98 * - The intended ability to place a new point at the current cursor
99 * position by pressing RETURN does not seem to work.
100 *
101 * - The Status bar tooltip should change to reflect editing mode
102 * and tell the user about RETURN and how to use the tool.
103 *
104 * - Connection points general:
105 *
106 * - Connection points that were inside the shape can end up outside
107 * after a rotation is applied to the shape in the select tool.
108 * It doesn't seem like the correct transform is being applied to
109 * these, or it is being applied at the wrong time. I'd expect
110 * connection points to rotate with the shape, and stay at the
111 * same position "on the shape"
112 *
113 * - I was able to make the connectors attached to a shape fall off
114 * the shape after scaling it. Not sure the exact cause, but may
115 * require more investigation/debugging.
116 *
117 * - The user-defined connection points should be either absolute
118 * (as the current ones are) or defined as a percentage of the
119 * shape. These would be based on a toggle setting on the
120 * toolbar, and they would be placed in exactly the same way by
121 * the user. The only difference would be that they would be
122 * store as percentage positions in the SVG connection-points
123 * property and that they would update/move automatically if the
124 * object was resized or scaled.
125 *
126 * - Thinking more, I think you always want to store and think about
127 * the positions of connection points to be pre-transform, but
128 * obviously the shape transform is applied to them. That way,
129 * they will rotate and scale automatically with the shape, when
130 * the shape transform is altered. The Percentage version would
131 * compute their position from the pre-transform dimensions and
132 * then have the transform applied to them, for example.
133 *
134 * - The connection points in the test_connection_points.svg file
135 * seem to follow the shape when it is moved, but connection
136 * points I add to new shapes, do not follow the shape, either
137 * when the shape is just moved or transformed. There is
138 * something wrong here. What exactly should the behaviour be
139 * currently?
140 *
141 * - I see that connection points are specified at absolute canvas
142 * positions. I really think that they should be specified in
143 * shape coordinated relative to the shapes. There may be
144 * transforms applied to layers and the canvas which would make
145 * specifying them quite difficult. I'd expect a position of 0, 0
146 * to be on the shape in question or very close to it, for example.
147 *
148 */
149
150
151
152#include <gdk/gdkkeysyms.h>
153#include <string>
154#include <cstring>
155
156#include "connector-context.h"
157#include "pixmaps/cursor-connector.xpm"
158#include "pixmaps/cursor-node.xpm"
159//#include "pixmaps/cursor-node-m.xpm"
160//#include "pixmaps/cursor-node-d.xpm"
161#include "xml/node-event-vector.h"
162#include "xml/repr.h"
163#include "svg/svg.h"
164#include "desktop.h"
165#include "desktop-style.h"
166#include "desktop-handles.h"
167#include "document.h"
168#include "message-context.h"
169#include "message-stack.h"
170#include "selection.h"
171#include "inkscape.h"
172#include "preferences.h"
173#include "sp-path.h"
174#include "display/canvas-bpath.h"
175#include "display/sodipodi-ctrl.h"
176#include <glibmm/i18n.h>
177#include <glibmm/stringutils.h>
178#include "snap.h"
179#include "knot.h"
180#include "sp-conn-end.h"
181#include "sp-conn-end-pair.h"
182#include "conn-avoid-ref.h"
183#include "libavoid/vertices.h"
184#include "libavoid/router.h"
185#include "context-fns.h"
186#include "sp-namedview.h"
187#include "sp-text.h"
188#include "sp-flowtext.h"
189#include "display/curve.h"
190
191static void sp_connector_context_class_init(SPConnectorContextClass *klass);
192static void sp_connector_context_init(SPConnectorContext *conn_context);
193static void sp_connector_context_dispose(GObject *object);
194
195static void sp_connector_context_setup(SPEventContext *ec);
196static void sp_connector_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val);
197static void sp_connector_context_finish(SPEventContext *ec);
198static gint sp_connector_context_root_handler(SPEventContext *ec, GdkEvent *event);
199static gint sp_connector_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event);
200
201// Stuff borrowed from DrawContext
202static void spcc_connector_set_initial_point(SPConnectorContext *cc, Geom::Point const p);
203static void spcc_connector_set_subsequent_point(SPConnectorContext *cc, Geom::Point const p);
204static void spcc_connector_finish_segment(SPConnectorContext *cc, Geom::Point p);
205static void spcc_reset_colors(SPConnectorContext *cc);
206static void spcc_connector_finish(SPConnectorContext *cc);
207static void spcc_concat_colors_and_flush(SPConnectorContext *cc);
208static void spcc_flush_white(SPConnectorContext *cc, SPCurve *gc);
209
210// Context event handlers
211static gint connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const &bevent);
212static gint connector_handle_motion_notify(SPConnectorContext *const cc, GdkEventMotion const &mevent);
213static gint connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton const &revent);
214static gint connector_handle_key_press(SPConnectorContext *const cc, guint const keyval);
215
216static void cc_active_shape_add_knot(SPDesktop* desktop, SPItem* item, ConnectionPointMap &cphandles, ConnectionPoint& cp);
217static void cc_set_active_shape(SPConnectorContext *cc, SPItem *item);
218static void cc_clear_active_shape(SPConnectorContext *cc);
219static void cc_set_active_conn(SPConnectorContext *cc, SPItem *item);
220static void cc_clear_active_conn(SPConnectorContext *cc);
221static bool conn_pt_handle_test(SPConnectorContext *cc, Geom::Point& p, gchar **href, gchar **cpid);
222static void cc_select_handle(SPKnot* knot);
223static void cc_deselect_handle(SPKnot* knot);
224static bool cc_item_is_shape(SPItem *item);
225static void cc_selection_changed(Inkscape::Selection *selection, gpointer data);
226static void cc_connector_rerouting_finish(SPConnectorContext *const cc,
227 Geom::Point *const p);
228
229static void shape_event_attr_deleted(Inkscape::XML::Node *repr,
230 Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data);
231static void shape_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
232 gchar const *old_value, gchar const *new_value, bool is_interactive,
233 gpointer data);
234
235
236static char* cc_knot_tips[] = { _("<b>Connection point</b>: click or drag to create a new connector"),
237 _("<b>Connection point</b>: click to select, drag to move") };
238
239/*static Geom::Point connector_drag_origin_w(0, 0);
240static bool connector_within_tolerance = false;*/
241static SPEventContextClass *parent_class;
242
243
244static Inkscape::XML::NodeEventVector shape_repr_events = {
245 NULL, /* child_added */
246 NULL, /* child_added */
247 shape_event_attr_changed,
248 NULL, /* content_changed */
249 NULL /* order_changed */
250};
251
252static Inkscape::XML::NodeEventVector layer_repr_events = {
253 NULL, /* child_added */
254 shape_event_attr_deleted,
255 NULL, /* child_added */
256 NULL, /* content_changed */
257 NULL /* order_changed */
258};
259
260
261GType
262sp_connector_context_get_type(void)
263{
264 static GType type = 0;
265 if (!type) {
266 GTypeInfo info = {
267 sizeof(SPConnectorContextClass),
268 NULL, NULL,
269 (GClassInitFunc) sp_connector_context_class_init,
270 NULL, NULL,
271 sizeof(SPConnectorContext),
272 4,
273 (GInstanceInitFunc) sp_connector_context_init,
274 NULL, /* value_table */
275 };
276 type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SPConnectorContext", &info, (GTypeFlags)0);
277 }
278 return type;
279}
280
281static void
282sp_connector_context_class_init(SPConnectorContextClass *klass)
283{
284 GObjectClass *object_class;
285 SPEventContextClass *event_context_class;
286
287 object_class = (GObjectClass *) klass;
288 event_context_class = (SPEventContextClass *) klass;
289
290 parent_class = (SPEventContextClass*)g_type_class_peek_parent(klass);
291
292 object_class->dispose = sp_connector_context_dispose;
293
294 event_context_class->setup = sp_connector_context_setup;
295 event_context_class->set = sp_connector_context_set;
296 event_context_class->finish = sp_connector_context_finish;
297 event_context_class->root_handler = sp_connector_context_root_handler;
298 event_context_class->item_handler = sp_connector_context_item_handler;
299}
300
301
302static void
303sp_connector_context_init(SPConnectorContext *cc)
304{
305 SPEventContext *ec = SP_EVENT_CONTEXT(cc);
306
307 ec->cursor_shape = cursor_connector_xpm;
308 ec->hot_x = 1;
309 ec->hot_y = 1;
310 ec->xp = 0;
311 ec->yp = 0;
312
313 cc->mode = SP_CONNECTOR_CONTEXT_DRAWING_MODE;
314 cc->knot_tip = 0;
315
316 cc->red_color = 0xff00007f;
317
318 cc->newconn = NULL;
319 cc->newConnRef = NULL;
320 cc->curvature = 0.0;
321
322 cc->sel_changed_connection = sigc::connection();
323
324 cc->active_shape = NULL;
325 cc->active_shape_repr = NULL;
326 cc->active_shape_layer_repr = NULL;
327
328 cc->active_conn = NULL;
329 cc->active_conn_repr = NULL;
330
331 cc->active_handle = NULL;
332
333 cc->selected_handle = NULL;
334
335 cc->clickeditem = NULL;
336 cc->clickedhandle = NULL;
337
338 new (&cc->connpthandles) ConnectionPointMap();
339
340 for (int i = 0; i < 2; ++i) {
341 cc->endpt_handle[i] = NULL;
342 cc->endpt_handler_id[i] = 0;
343 }
344 cc->shref = NULL;
345 cc->scpid = NULL;
346 cc->ehref = NULL;
347 cc->ecpid = NULL;
348 cc->npoints = 0;
349 cc->state = SP_CONNECTOR_CONTEXT_IDLE;
350}
351
352
353static void
354sp_connector_context_dispose(GObject *object)
355{
356 SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(object);
357
358 cc->sel_changed_connection.disconnect();
359
360 if (!cc->connpthandles.empty()) {
361 for (ConnectionPointMap::iterator it = cc->connpthandles.begin();
362 it != cc->connpthandles.end(); ++it) {
363 g_object_unref(it->first);
364 }
365 cc->connpthandles.clear();
366 }
367 cc->connpthandles.~ConnectionPointMap();
368 for (int i = 0; i < 2; ++i) {
369 if (cc->endpt_handle[1]) {
370 g_object_unref(cc->endpt_handle[i]);
371 cc->endpt_handle[i] = NULL;
372 }
373 }
374 if (cc->shref) {
375 g_free(cc->shref);
376 cc->shref = NULL;
377 }
378 if (cc->scpid) {
379 g_free(cc->scpid);
380 cc->scpid = NULL;
381 }
382 if (cc->ehref) {
383 g_free(cc->shref);
384 cc->shref = NULL;
385 }
386 if (cc->ecpid) {
387 g_free(cc->scpid);
388 cc->scpid = NULL;
389 }
390 g_assert( cc->newConnRef == NULL );
391
392 G_OBJECT_CLASS(parent_class)->dispose(object);
393}
394
395
396static void
397sp_connector_context_setup(SPEventContext *ec)
398{
399 SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(ec);
400 SPDesktop *dt = ec->desktop;
401
402 if (((SPEventContextClass *) parent_class)->setup) {
403 ((SPEventContextClass *) parent_class)->setup(ec);
404 }
405
406 cc->selection = sp_desktop_selection(dt);
407
408 cc->sel_changed_connection.disconnect();
409 cc->sel_changed_connection = cc->selection->connectChanged(
410 sigc::bind(sigc::ptr_fun(&cc_selection_changed),
411 (gpointer) cc));
412
413 /* Create red bpath */
414 cc->red_bpath = sp_canvas_bpath_new(sp_desktop_sketch(ec->desktop), NULL);
415 sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(cc->red_bpath), cc->red_color,
416 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
417 sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(cc->red_bpath), 0x00000000,
418 SP_WIND_RULE_NONZERO);
419 /* Create red curve */
420 cc->red_curve = new SPCurve();
421
422 /* Create green curve */
423 cc->green_curve = new SPCurve();
424
425 // Notice the initial selection.
426 cc_selection_changed(cc->selection, (gpointer) cc);
427
428 cc->within_tolerance = false;
429
430 sp_event_context_read(ec, "curvature");
431 sp_event_context_read(ec, "orthogonal");
432 sp_event_context_read(ec, "mode");
433 cc->knot_tip = cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE ? cc_knot_tips[0] : cc_knot_tips[1];
434 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
435 if (prefs->getBool("/tools/connector/selcue", 0)) {
436 ec->enableSelectionCue();
437 }
438
439 // Make sure we see all enter events for canvas items,
440 // even if a mouse button is depressed.
441 dt->canvas->gen_all_enter_events = true;
442}
443
444
445static void
446sp_connector_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val)
447{
448 SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(ec);
449
450 /* fixme: Proper error handling for non-numeric data. Use a locale-independent function like
451 * g_ascii_strtod (or a thin wrapper that does the right thing for invalid values inf/nan). */
452 Glib::ustring name = val->getEntryName();
453 if ( name == "curvature" ) {
454 cc->curvature = val->getDoubleLimited(); // prevents NaN and +/-Inf from messing up
455 }
456 else if ( name == "orthogonal" ) {
457 cc->isOrthogonal = val->getBool();
458 }
459 else if ( name == "mode")
460 {
461 sp_connector_context_switch_mode(ec, val->getBool() ? SP_CONNECTOR_CONTEXT_EDITING_MODE : SP_CONNECTOR_CONTEXT_DRAWING_MODE);
462 }
463}
464
465void sp_connector_context_switch_mode(SPEventContext* ec, unsigned int newMode)
466{
467 SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(ec);
468
469 cc->mode = newMode;
470 if ( cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE )
471 {
472 ec->cursor_shape = cursor_connector_xpm;
473 cc->knot_tip = cc_knot_tips[0];
474 if (cc->selected_handle)
475 cc_deselect_handle( cc->selected_handle );
476 cc->selected_handle = NULL;
477 // Show all default connection points
478
479 }
480 else if ( cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE )
481 {
482 ec->cursor_shape = cursor_node_xpm;
483 cc->knot_tip = cc_knot_tips[1];
484/* if (cc->active_shape)
485 {
486 cc->selection->set( SP_OBJECT( cc->active_shape ) );
487 }
488 else
489 {
490 SPItem* item = cc->selection->singleItem();
491 if ( item )
492 {
493 cc_set_active_shape(cc, item);
494 cc->selection->set( SP_OBJECT( item ) );
495 }
496 }*/
497 }
498 sp_event_context_update_cursor(ec);
499
500}
501
502
503static void
504sp_connector_context_finish(SPEventContext *ec)
505{
506 SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(ec);
507
508 spcc_connector_finish(cc);
509 cc->state = SP_CONNECTOR_CONTEXT_IDLE;
510
511 if (((SPEventContextClass *) parent_class)->finish) {
512 ((SPEventContextClass *) parent_class)->finish(ec);
513 }
514
515 if (cc->selection) {
516 cc->selection = NULL;
517 }
518 cc_clear_active_shape(cc);
519 cc_clear_active_conn(cc);
520
521 // Restore the default event generating behaviour.
522 SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(ec);
523 desktop->canvas->gen_all_enter_events = false;
524}
525
526
527//-----------------------------------------------------------------------------
528
529
530static void
531cc_clear_active_shape(SPConnectorContext *cc)
532{
533 if (cc->active_shape == NULL) {
534 return;
535 }
536 g_assert( cc->active_shape_repr );
537 g_assert( cc->active_shape_layer_repr );
538
539 cc->active_shape = NULL;
540
541 if (cc->active_shape_repr) {
542 sp_repr_remove_listener_by_data(cc->active_shape_repr, cc);
543 Inkscape::GC::release(cc->active_shape_repr);
544 cc->active_shape_repr = NULL;
545
546 sp_repr_remove_listener_by_data(cc->active_shape_layer_repr, cc);
547 Inkscape::GC::release(cc->active_shape_layer_repr);
548 cc->active_shape_layer_repr = NULL;
549 }
550
551 // Hide the connection points if they exist.
552 if (cc->connpthandles.size()) {
553 for (ConnectionPointMap::iterator it = cc->connpthandles.begin();
554 it != cc->connpthandles.end(); ++it) {
555 sp_knot_hide(it->first);
556 }
557 }
558}
559
560
561static void
562cc_clear_active_conn(SPConnectorContext *cc)
563{
564 if (cc->active_conn == NULL) {
565 return;
566 }
567 g_assert( cc->active_conn_repr );
568
569 cc->active_conn = NULL;
570
571 if (cc->active_conn_repr) {
572 sp_repr_remove_listener_by_data(cc->active_conn_repr, cc);
573 Inkscape::GC::release(cc->active_conn_repr);
574 cc->active_conn_repr = NULL;
575 }
576
577 // Hide the endpoint handles.
578 for (int i = 0; i < 2; ++i) {
579 if (cc->endpt_handle[i]) {
580 sp_knot_hide(cc->endpt_handle[i]);
581 }
582 }
583}
584
585
586static bool
587conn_pt_handle_test(SPConnectorContext *cc, Geom::Point& p, gchar **href, gchar **cpid)
588{
589 // TODO: this will need to change when there are more connection
590 // points available for each shape.
591
592 if (cc->active_handle && (cc->connpthandles.find(cc->active_handle) != cc->connpthandles.end()))
593 {
594 p = cc->active_handle->pos;
595 const ConnectionPoint& cp = cc->connpthandles[cc->active_handle];
596 *href = g_strdup_printf("#%s", cc->active_shape->getId());
597 *cpid = g_strdup_printf("%c%d", cp.type == ConnPointDefault ? 'd' : 'u' , cp.id);
598 return true;
599 }
600 *href = NULL;
601 *cpid = NULL;
602 return false;
603}
604
605static void
606cc_select_handle(SPKnot* knot)
607{
608 knot->setShape(SP_KNOT_SHAPE_SQUARE);
609 knot->setSize(10);
610 knot->setAnchor(GTK_ANCHOR_CENTER);
611 knot->setFill(0x0000ffff, 0x0000ffff, 0x0000ffff);
612 sp_knot_update_ctrl(knot);
613}
614
615static void
616cc_deselect_handle(SPKnot* knot)
617{
618 knot->setShape(SP_KNOT_SHAPE_SQUARE);
619 knot->setSize(8);
620 knot->setAnchor(GTK_ANCHOR_CENTER);
621 knot->setFill(0xffffff00, 0xff0000ff, 0xff0000ff);
622 sp_knot_update_ctrl(knot);
623}
624
625static gint
626sp_connector_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event)
627{
628 gint ret = FALSE;
629
630 SPDesktop *desktop = event_context->desktop;
631
632 SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(event_context);
633
634 Geom::Point p(event->button.x, event->button.y);
635
636 switch (event->type) {
637 case GDK_BUTTON_RELEASE:
638 if (event->button.button == 1 && !event_context->space_panning) {
639 if ((cc->state == SP_CONNECTOR_CONTEXT_DRAGGING) &&
640 (event_context->within_tolerance))
641 {
642 spcc_reset_colors(cc);
643 cc->state = SP_CONNECTOR_CONTEXT_IDLE;
644 }
645 if (cc->state != SP_CONNECTOR_CONTEXT_IDLE) {
646 // Doing something else like rerouting.
647 break;
648 }
649 // find out clicked item, honoring Alt
650 SPItem *item = sp_event_context_find_item(desktop,
651 p, event->button.state & GDK_MOD1_MASK, FALSE);
652
653 if (event->button.state & GDK_SHIFT_MASK) {
654 cc->selection->toggle(item);
655 } else {
656 cc->selection->set(item);
657 if ( cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE && cc->selected_handle )
658 {
659 cc_deselect_handle( cc->selected_handle );
660 cc->selected_handle = NULL;
661 }
662 /* When selecting a new item,
663 do not allow showing connection points
664 on connectors. (yet?)
665 */
666 if ( item != cc->active_shape && !cc_item_is_connector( item ) )
667 cc_set_active_shape( cc, item );
668 }
669 ret = TRUE;
670
671 }
672 break;
673 case GDK_ENTER_NOTIFY:
674 {
675 if (cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE || (cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE && !cc->selected_handle))
676 {
677 if (cc_item_is_shape(item)) {
678
679 // I don't really understand what the above does,
680 // so I commented it.
681 // This is a shape, so show connection point(s).
682 /* if (!(cc->active_shape)
683 // Don't show handle for another handle.
684 // || (cc->connpthandles.find((SPKnot*) item) != cc->connpthandles.end())
685 )
686 {
687 cc_set_active_shape(cc, item);
688 }*/
689 cc_set_active_shape(cc, item);
690 }
691 ret = TRUE;
692 }
693 break;
694 }
695 default:
696 break;
697 }
698
699 return ret;
700}
701
702
703gint
704sp_connector_context_root_handler(SPEventContext *ec, GdkEvent *event)
705{
706 SPConnectorContext *const cc = SP_CONNECTOR_CONTEXT(ec);
707
708 gint ret = FALSE;
709
710 switch (event->type) {
711 case GDK_BUTTON_PRESS:
712 ret = connector_handle_button_press(cc, event->button);
713 break;
714
715 case GDK_MOTION_NOTIFY:
716 ret = connector_handle_motion_notify(cc, event->motion);
717 break;
718
719 case GDK_BUTTON_RELEASE:
720 ret = connector_handle_button_release(cc, event->button);
721 break;
722 case GDK_KEY_PRESS:
723 ret = connector_handle_key_press(cc, get_group0_keyval (&event->key));
724 break;
725
726 default:
727 break;
728 }
729
730 if (!ret) {
731 gint (*const parent_root_handler)(SPEventContext *, GdkEvent *)
732 = ((SPEventContextClass *) parent_class)->root_handler;
733 if (parent_root_handler) {
734 ret = parent_root_handler(ec, event);
735 }
736 }
737
738 return ret;
739}
740
741
742static gint
743connector_handle_button_press(SPConnectorContext *const cc, GdkEventButton const &bevent)
744{
745 Geom::Point const event_w(bevent.x, bevent.y);
746 /* Find desktop coordinates */
747 Geom::Point p = cc->desktop->w2d(event_w);
748 SPEventContext *event_context = SP_EVENT_CONTEXT(cc);
749
750 gint ret = FALSE;
751 if ( cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE )
752 {
753 if ( bevent.button == 1 && !event_context->space_panning ) {
754
755 SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
756
757 if (Inkscape::have_viable_layer(desktop, cc->_message_context) == false) {
758 return TRUE;
759 }
760
761 Geom::Point const event_w(bevent.x,
762 bevent.y);
763// connector_drag_origin_w = event_w;
764 cc->xp = bevent.x;
765 cc->yp = bevent.y;
766 cc->within_tolerance = true;
767
768 Geom::Point const event_dt = cc->desktop->w2d(event_w);
769
770 SnapManager &m = cc->desktop->namedview->snap_manager;
771 m.setup(cc->desktop);
772
773 switch (cc->state) {
774 case SP_CONNECTOR_CONTEXT_STOP:
775 /* This is allowed, if we just canceled curve */
776 case SP_CONNECTOR_CONTEXT_IDLE:
777 {
778 if ( cc->npoints == 0 ) {
779 cc_clear_active_conn(cc);
780
781 SP_EVENT_CONTEXT_DESKTOP(cc)->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Creating new connector"));
782
783 /* Set start anchor */
784 /* Create green anchor */
785 Geom::Point p = event_dt;
786
787 // Test whether we clicked on a connection point
788 bool found = conn_pt_handle_test(cc, p, &cc->shref, &cc->scpid);
789
790 if (!found) {
791 // This is the first point, so just snap it to the grid
792 // as there's no other points to go off.
793 m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
794 }
795 spcc_connector_set_initial_point(cc, p);
796
797 }
798 cc->state = SP_CONNECTOR_CONTEXT_DRAGGING;
799 ret = TRUE;
800 break;
801 }
802 case SP_CONNECTOR_CONTEXT_DRAGGING:
803 {
804 // This is the second click of a connector creation.
805 m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
806
807 spcc_connector_set_subsequent_point(cc, p);
808 spcc_connector_finish_segment(cc, p);
809 // Test whether we clicked on a connection point
810 /*bool found = */conn_pt_handle_test(cc, p, &cc->ehref, &cc->ecpid);
811 if (cc->npoints != 0) {
812 spcc_connector_finish(cc);
813 }
814 cc_set_active_conn(cc, cc->newconn);
815 cc->state = SP_CONNECTOR_CONTEXT_IDLE;
816 ret = TRUE;
817 break;
818 }
819 case SP_CONNECTOR_CONTEXT_CLOSE:
820 {
821 g_warning("Button down in CLOSE state");
822 break;
823 }
824 default:
825 break;
826 }
827 } else if (bevent.button == 3) {
828 if (cc->state == SP_CONNECTOR_CONTEXT_REROUTING) {
829 // A context menu is going to be triggered here,
830 // so end the rerouting operation.
831 cc_connector_rerouting_finish(cc, &p);
832
833 cc->state = SP_CONNECTOR_CONTEXT_IDLE;
834
835 // Don't set ret to TRUE, so we drop through to the
836 // parent handler which will open the context menu.
837 }
838 else if (cc->npoints != 0) {
839 spcc_connector_finish(cc);
840 cc->state = SP_CONNECTOR_CONTEXT_IDLE;
841 ret = TRUE;
842 }
843 }
844 }
845 else if ( cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE )
846 {
847 if ( bevent.button == 1 && !event_context->space_panning )
848 {
849 // Initialize variables in case of dragging
850
851 SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
852
853 if (Inkscape::have_viable_layer(desktop, cc->_message_context) == false) {
854 return TRUE;
855 }
856
857 cc->xp = bevent.x;
858 cc->yp = bevent.y;
859 cc->within_tolerance = true;
860
861 ConnectionPointMap::iterator const& active_knot_it = cc->connpthandles.find( cc->active_handle );
862
863 switch (cc->state)
864 {
865 case SP_CONNECTOR_CONTEXT_IDLE:
866 if ( active_knot_it != cc->connpthandles.end() )
867 {
868 // We do not allow selecting and, thereby, moving default knots
869 if ( active_knot_it->second.type != ConnPointDefault)
870 {
871 if (cc->selected_handle != cc->active_handle)
872 {
873 if ( cc->selected_handle )
874 cc_deselect_handle( cc->selected_handle );
875 cc->selected_handle = cc->active_handle;
876 cc_select_handle( cc->selected_handle );
877 }
878 }
879 else
880 // Just ignore the default connection point
881 return FALSE;
882 }
883 else
884 if ( cc->selected_handle )
885 {
886 cc_deselect_handle( cc->selected_handle );
887 cc->selected_handle = NULL;
888 }
889
890 if ( cc->selected_handle )
891 {
892 cc->state = SP_CONNECTOR_CONTEXT_DRAGGING;
893 cc->selection->set( SP_OBJECT( cc->active_shape ) );
894 }
895
896 ret = TRUE;
897 break;
898 // Dragging valid because of the way we create
899 // new connection points.
900 case SP_CONNECTOR_CONTEXT_DRAGGING:
901 // Do nothing.
902 ret = TRUE;
903 break;
904 }
905 }
906 }
907 return ret;
908}
909
910
911static gint
912connector_handle_motion_notify(SPConnectorContext *const cc, GdkEventMotion const &mevent)
913{
914 gint ret = FALSE;
915 SPEventContext *event_context = SP_EVENT_CONTEXT(cc);
916 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
917
918 if (event_context->space_panning || mevent.state & GDK_BUTTON2_MASK || mevent.state & GDK_BUTTON3_MASK) {
919 // allow middle-button scrolling
920 return FALSE;
921 }
922
923 Geom::Point const event_w(mevent.x, mevent.y);
924
925 if (cc->within_tolerance) {
926 cc->tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
927 if ( ( abs( (gint) mevent.x - cc->xp ) < cc->tolerance ) &&
928 ( abs( (gint) mevent.y - cc->yp ) < cc->tolerance ) ) {
929 return FALSE; // Do not drag if we're within tolerance from origin.
930 }
931 }
932 // Once the user has moved farther than tolerance from the original location
933 // (indicating they intend to move the object, not click), then always process
934 // the motion notify coordinates as given (no snapping back to origin)
935 cc->within_tolerance = false;
936
937 SPDesktop *const dt = cc->desktop;
938
939 /* Find desktop coordinates */
940 Geom::Point p = dt->w2d(event_w);
941
942 if ( cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE )
943 {
944 SnapManager &m = dt->namedview->snap_manager;
945 m.setup(dt);
946
947 switch (cc->state) {
948 case SP_CONNECTOR_CONTEXT_DRAGGING:
949 {
950 gobble_motion_events(mevent.state);
951 // This is movement during a connector creation.
952 if ( cc->npoints > 0 ) {
953 m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
954 cc->selection->clear();
955 spcc_connector_set_subsequent_point(cc, p);
956 ret = TRUE;
957 }
958 break;
959 }
960 case SP_CONNECTOR_CONTEXT_REROUTING:
961 {
962 gobble_motion_events(GDK_BUTTON1_MASK);
963 g_assert( SP_IS_PATH(cc->clickeditem));
964
965 m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
966
967 // Update the hidden path
968 Geom::Matrix i2d = sp_item_i2d_affine(cc->clickeditem);
969 Geom::Matrix d2i = i2d.inverse();
970 SPPath *path = SP_PATH(cc->clickeditem);
971 SPCurve *curve = path->original_curve ? path->original_curve : path->curve;
972 if (cc->clickedhandle == cc->endpt_handle[0]) {
973 Geom::Point o = cc->endpt_handle[1]->pos;
974 curve->stretch_endpoints(p * d2i, o * d2i);
975 }
976 else {
977 Geom::Point o = cc->endpt_handle[0]->pos;
978 curve->stretch_endpoints(o * d2i, p * d2i);
979 }
980 sp_conn_reroute_path_immediate(path);
981
982 // Copy this to the temporary visible path
983 cc->red_curve = path->original_curve ?
984 path->original_curve->copy() : path->curve->copy();
985 cc->red_curve->transform(i2d);
986
987 sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), cc->red_curve);
988 ret = TRUE;
989 break;
990 }
991 case SP_CONNECTOR_CONTEXT_STOP:
992 /* This is perfectly valid */
993 break;
994 default:
995 if (!sp_event_context_knot_mouseover(cc)) {
996 m.preSnap(Inkscape::SnapCandidatePoint(p, Inkscape::SNAPSOURCE_OTHER_HANDLE));
997 }
998 break;
999 }
1000 }
1001 else if ( cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE )
1002 {
1003 switch ( cc->state )
1004 {
1005 case SP_CONNECTOR_CONTEXT_DRAGGING:
1006 sp_knot_set_position(cc->selected_handle, p, 0);
1007 ret = TRUE;
1008 break;
1009 case SP_CONNECTOR_CONTEXT_NEWCONNPOINT:
1010 sp_knot_set_position(cc->selected_handle, p, 0);
1011 ret = TRUE;
1012 break;
1013 }
1014 }
1015
1016 return ret;
1017}
1018
1019
1020static gint
1021connector_handle_button_release(SPConnectorContext *const cc, GdkEventButton const &revent)
1022{
1023 gint ret = FALSE;
1024 SPEventContext *event_context = SP_EVENT_CONTEXT(cc);
1025 if ( revent.button == 1 && !event_context->space_panning ) {
1026
1027 SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
1028 SPDocument *doc = sp_desktop_document(desktop);
1029
1030 SnapManager &m = desktop->namedview->snap_manager;
1031 m.setup(desktop);
1032
1033 Geom::Point const event_w(revent.x, revent.y);
1034
1035 /* Find desktop coordinates */
1036 Geom::Point p = cc->desktop->w2d(event_w);
1037 if ( cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE )
1038 {
1039 switch (cc->state) {
1040 //case SP_CONNECTOR_CONTEXT_POINT:
1041 case SP_CONNECTOR_CONTEXT_DRAGGING:
1042 {
1043 m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
1044
1045 if (cc->within_tolerance)
1046 {
1047 spcc_connector_finish_segment(cc, p);
1048 return TRUE;
1049 }
1050 // Connector has been created via a drag, end it now.
1051 spcc_connector_set_subsequent_point(cc, p);
1052 spcc_connector_finish_segment(cc, p);
1053 // Test whether we clicked on a connection point
1054 /*bool found = */conn_pt_handle_test(cc, p, &cc->ehref, &cc->ecpid);
1055 if (cc->npoints != 0) {
1056 spcc_connector_finish(cc);
1057 }
1058 cc_set_active_conn(cc, cc->newconn);
1059 cc->state = SP_CONNECTOR_CONTEXT_IDLE;
1060 break;
1061 }
1062 case SP_CONNECTOR_CONTEXT_REROUTING:
1063 {
1064 m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
1065 cc_connector_rerouting_finish(cc, &p);
1066
1067 sp_document_ensure_up_to_date(doc);
1068 cc->state = SP_CONNECTOR_CONTEXT_IDLE;
1069 return TRUE;
1070 break;
1071 }
1072 case SP_CONNECTOR_CONTEXT_STOP:
1073 /* This is allowed, if we just cancelled curve */
1074 break;
1075 default:
1076 break;
1077 }
1078 ret = TRUE;
1079 }
1080 else if ( cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE )
1081 {
1082 switch ( cc->state )
1083 {
1084 case SP_CONNECTOR_CONTEXT_DRAGGING:
1085
1086 if (!cc->within_tolerance)
1087 {
1088 m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
1089 sp_knot_set_position(cc->selected_handle, p, 0);
1090 ConnectionPoint& cp = cc->connpthandles[cc->selected_handle];
1091 cp.pos = p * sp_item_dt2i_affine(cc->active_shape);
1092 cc->active_shape->avoidRef->updateConnectionPoint(cp);
1093 }
1094
1095 cc->state = SP_CONNECTOR_CONTEXT_IDLE;
1096 ret = TRUE;
1097 break;
1098
1099
1100 case SP_CONNECTOR_CONTEXT_NEWCONNPOINT:
1101 m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
1102
1103 sp_knot_set_position(cc->selected_handle, p, 0);
1104
1105 ConnectionPoint cp;
1106 cp.type = ConnPointUserDefined;
1107 cp.pos = p * sp_item_dt2i_affine(cc->active_shape);
1108 cp.dir = Avoid::ConnDirAll;
1109 g_object_unref(cc->selected_handle);
1110 cc->active_shape->avoidRef->addConnectionPoint(cp);
1111 sp_document_ensure_up_to_date(doc);
1112 for (ConnectionPointMap::iterator it = cc->connpthandles.begin(); it != cc->connpthandles.end(); ++it)
1113 if (it->second.type == ConnPointUserDefined && it->second.id == cp.id)
1114 {
1115 cc->selected_handle = it->first;
1116 break;
1117 }
1118 cc_select_handle( cc->selected_handle );
1119 cc->state = SP_CONNECTOR_CONTEXT_IDLE;
1120 ret = TRUE;
1121 break;
1122 }
1123 }
1124 }
1125
1126
1127 return ret;
1128}
1129
1130
1131static gint
1132connector_handle_key_press(SPConnectorContext *const cc, guint const keyval)
1133{
1134 gint ret = FALSE;
1135 /* fixme: */
1136 if ( cc->mode == SP_CONNECTOR_CONTEXT_DRAWING_MODE )
1137 {
1138 switch (keyval) {
1139 case GDK_Return:
1140 case GDK_KP_Enter:
1141 if (cc->npoints != 0) {
1142 spcc_connector_finish(cc);
1143 cc->state = SP_CONNECTOR_CONTEXT_IDLE;
1144 ret = TRUE;
1145 }
1146 break;
1147 case GDK_Escape:
1148 if (cc->state == SP_CONNECTOR_CONTEXT_REROUTING) {
1149
1150 SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
1151 SPDocument *doc = sp_desktop_document(desktop);
1152
1153 cc_connector_rerouting_finish(cc, NULL);
1154
1155 sp_document_undo(doc);
1156
1157 cc->state = SP_CONNECTOR_CONTEXT_IDLE;
1158 desktop->messageStack()->flash( Inkscape::NORMAL_MESSAGE,
1159 _("Connector endpoint drag cancelled."));
1160 ret = TRUE;
1161 }
1162 else if (cc->npoints != 0) {
1163 // if drawing, cancel, otherwise pass it up for deselecting
1164 cc->state = SP_CONNECTOR_CONTEXT_STOP;
1165 spcc_reset_colors(cc);
1166 ret = TRUE;
1167 }
1168 break;
1169 default:
1170 break;
1171 }
1172 }
1173 else if ( cc->mode == SP_CONNECTOR_CONTEXT_EDITING_MODE )
1174 {
1175 switch ( cc->state )
1176 {
1177 case SP_CONNECTOR_CONTEXT_DRAGGING:
1178 if ( keyval == GDK_Escape )
1179 {
1180 // Cancel connection point dragging
1181
1182 // Obtain original position
1183 ConnectionPoint const& cp = cc->connpthandles[cc->selected_handle];
1184 SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
1185 const Geom::Matrix& i2doc = sp_item_i2doc_affine(cc->active_shape);
1186 sp_knot_set_position(cc->selected_handle, cp.pos * i2doc * desktop->doc2dt(), 0);
1187 cc->state = SP_CONNECTOR_CONTEXT_IDLE;
1188 desktop->messageStack()->flash( Inkscape::NORMAL_MESSAGE,
1189 _("Connection point drag cancelled."));
1190 ret = TRUE;
1191 }
1192 else if ( keyval == GDK_Return || keyval == GDK_KP_Enter )
1193 {
1194 // Put connection point at current position
1195
1196 SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
1197 SnapManager &m = desktop->namedview->snap_manager;
1198 m.setup(desktop);
1199 Geom::Point p = cc->selected_handle->pos;
1200// SPEventContext* event_context = SP_EVENT_CONTEXT( cc );
1201
1202 if (!cc->within_tolerance)
1203 {
1204 m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
1205 sp_knot_set_position(cc->selected_handle, p, 0);
1206 ConnectionPoint& cp = cc->connpthandles[cc->selected_handle];
1207 cp.pos = p * sp_item_dt2i_affine(cc->active_shape);
1208 cc->active_shape->avoidRef->updateConnectionPoint(cp);
1209 }
1210
1211 cc->state = SP_CONNECTOR_CONTEXT_IDLE;
1212 ret = TRUE;
1213 }
1214 break;
1215 case SP_CONNECTOR_CONTEXT_NEWCONNPOINT:
1216 if ( keyval == GDK_Escape )
1217 {
1218 // Just destroy the knot
1219 g_object_unref( cc->selected_handle );
1220 cc->selected_handle = NULL;
1221 cc->state = SP_CONNECTOR_CONTEXT_IDLE;
1222 ret = TRUE;
1223 }
1224 else if ( keyval == GDK_Return || keyval == GDK_KP_Enter )
1225 {
1226 SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
1227 SPDocument *doc = sp_desktop_document(desktop);
1228 SnapManager &m = desktop->namedview->snap_manager;
1229 m.setup(desktop);
1230 Geom::Point p = cc->selected_handle->pos;
1231
1232 m.freeSnapReturnByRef(p, Inkscape::SNAPSOURCE_OTHER_HANDLE);
1233
1234 sp_knot_set_position(cc->selected_handle, p, 0);
1235
1236 ConnectionPoint cp;
1237 cp.type = ConnPointUserDefined;
1238 cp.pos = p * sp_item_dt2i_affine(cc->active_shape);
1239 cp.dir = Avoid::ConnDirAll;
1240 g_object_unref(cc->selected_handle);
1241 cc->active_shape->avoidRef->addConnectionPoint(cp);
1242 sp_document_ensure_up_to_date(doc);
1243 for (ConnectionPointMap::iterator it = cc->connpthandles.begin(); it != cc->connpthandles.end(); ++it)
1244 if (it->second.type == ConnPointUserDefined && it->second.id == cp.id)
1245 {
1246 cc->selected_handle = it->first;
1247 break;
1248 }
1249 cc_select_handle( cc->selected_handle );
1250 cc->state = SP_CONNECTOR_CONTEXT_IDLE;
1251 ret = TRUE;
1252 }
1253
1254 break;
1255 case SP_CONNECTOR_CONTEXT_IDLE:
1256 if ( keyval == GDK_Delete && cc->selected_handle )
1257 {
1258 cc->active_shape->avoidRef->deleteConnectionPoint(cc->connpthandles[cc->selected_handle]);
1259 cc->selected_handle = NULL;
1260 ret = TRUE;
1261 }
1262
1263 break;
1264 }
1265 }
1266
1267 return ret;
1268}
1269
1270
1271static void
1272cc_connector_rerouting_finish(SPConnectorContext *const cc, Geom::Point *const p)
1273{
1274 SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
1275 SPDocument *doc = sp_desktop_document(desktop);
1276
1277 // Clear the temporary path:
1278 cc->red_curve->reset();
1279 sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), NULL);
1280
1281 if (p != NULL)
1282 {
1283 // Test whether we clicked on a connection point
1284 gchar *shape_label, *cpid;
1285 bool found = conn_pt_handle_test(cc, *p, &shape_label, &cpid);
1286
1287 if (found) {
1288 if (cc->clickedhandle == cc->endpt_handle[0]) {
1289 sp_object_setAttribute(cc->clickeditem,
1290 "inkscape:connection-start", shape_label, false);
1291 sp_object_setAttribute(cc->clickeditem,
1292 "inkscape:connection-start-point", cpid, false);
1293 }
1294 else {
1295 sp_object_setAttribute(cc->clickeditem,
1296 "inkscape:connection-end", shape_label, false);
1297 sp_object_setAttribute(cc->clickeditem,
1298 "inkscape:connection-end-point", cpid, false);
1299 }
1300 g_free(shape_label);
1301 }
1302 }
1303 cc->clickeditem->setHidden(false);
1304 sp_conn_reroute_path_immediate(SP_PATH(cc->clickeditem));
1305 cc->clickeditem->updateRepr();
1306 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR,
1307 _("Reroute connector"));
1308 cc_set_active_conn(cc, cc->clickeditem);
1309}
1310
1311
1312static void
1313spcc_reset_colors(SPConnectorContext *cc)
1314{
1315 /* Red */
1316 cc->red_curve->reset();
1317 sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), NULL);
1318
1319 cc->green_curve->reset();
1320 cc->npoints = 0;
1321}
1322
1323
1324static void
1325spcc_connector_set_initial_point(SPConnectorContext *const cc, Geom::Point const p)
1326{
1327 g_assert( cc->npoints == 0 );
1328
1329 cc->p[0] = p;
1330 cc->p[1] = p;
1331 cc->npoints = 2;
1332 sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), NULL);
1333}
1334
1335
1336static void
1337spcc_connector_set_subsequent_point(SPConnectorContext *const cc, Geom::Point const p)
1338{
1339 g_assert( cc->npoints != 0 );
1340
1341 SPDesktop *dt = cc->desktop;
1342 Geom::Point o = dt->dt2doc(cc->p[0]);
1343 Geom::Point d = dt->dt2doc(p);
1344 Avoid::Point src(o[Geom::X], o[Geom::Y]);
1345 Avoid::Point dst(d[Geom::X], d[Geom::Y]);
1346
1347 if (!cc->newConnRef) {
1348 Avoid::Router *router = sp_desktop_document(dt)->router;
1349 cc->newConnRef = new Avoid::ConnRef(router);
1350 cc->newConnRef->setEndpoint(Avoid::VertID::src, src);
1351 if (cc->isOrthogonal)
1352 cc->newConnRef->setRoutingType(Avoid::ConnType_Orthogonal);
1353 else
1354 cc->newConnRef->setRoutingType(Avoid::ConnType_PolyLine);
1355 }
1356 // Set new endpoint.
1357 cc->newConnRef->setEndpoint(Avoid::VertID::tar, dst);
1358 // Immediately generate new routes for connector.
1359 cc->newConnRef->makePathInvalid();
1360 cc->newConnRef->router()->processTransaction();
1361 // Recreate curve from libavoid route.
1362 recreateCurve( cc->red_curve, cc->newConnRef, cc->curvature );
1363 cc->red_curve->transform(dt->doc2dt());
1364 sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), cc->red_curve);
1365}
1366
1367
1368/**
1369 * Concats red, blue and green.
1370 * If any anchors are defined, process these, optionally removing curves from white list
1371 * Invoke _flush_white to write result back to object.
1372 */
1373static void
1374spcc_concat_colors_and_flush(SPConnectorContext *cc)
1375{
1376 SPCurve *c = cc->green_curve;
1377 cc->green_curve = new SPCurve();
1378
1379 cc->red_curve->reset();
1380 sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), NULL);
1381
1382 if (c->is_empty()) {
1383 c->unref();
1384 return;
1385 }
1386
1387 spcc_flush_white(cc, c);
1388
1389 c->unref();
1390}
1391
1392
1393/*
1394 * Flushes white curve(s) and additional curve into object
1395 *
1396 * No cleaning of colored curves - this has to be done by caller
1397 * No rereading of white data, so if you cannot rely on ::modified, do it in caller
1398 *
1399 */
1400
1401static void
1402spcc_flush_white(SPConnectorContext *cc, SPCurve *gc)
1403{
1404 SPCurve *c;
1405
1406 if (gc) {
1407 c = gc;
1408 c->ref();
1409 } else {
1410 return;
1411 }
1412
1413 /* Now we have to go back to item coordinates at last */
1414 c->transform(SP_EVENT_CONTEXT_DESKTOP(cc)->dt2doc());
1415
1416 SPDesktop *desktop = SP_EVENT_CONTEXT_DESKTOP(cc);
1417 SPDocument *doc = sp_desktop_document(desktop);
1418 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
1419
1420 if ( c && !c->is_empty() ) {
1421 /* We actually have something to write */
1422
1423 Inkscape::XML::Node *repr = xml_doc->createElement("svg:path");
1424 /* Set style */
1425 sp_desktop_apply_style_tool(desktop, repr, "/tools/connector", false);
1426
1427 gchar *str = sp_svg_write_path( c->get_pathvector() );
1428 g_assert( str != NULL );
1429 repr->setAttribute("d", str);
1430 g_free(str);
1431
1432 /* Attach repr */
1433 cc->newconn = SP_ITEM(desktop->currentLayer()->appendChildRepr(repr));
1434 cc->newconn->transform = sp_item_i2doc_affine(SP_ITEM(desktop->currentLayer())).inverse();
1435
1436 bool connection = false;
1437 sp_object_setAttribute(cc->newconn, "inkscape:connector-type",
1438 cc->isOrthogonal ? "orthogonal" : "polyline", false);
1439 sp_object_setAttribute(cc->newconn, "inkscape:connector-curvature",
1440 Glib::Ascii::dtostr(cc->curvature).c_str(), false);
1441 if (cc->shref)
1442 {
1443 sp_object_setAttribute(cc->newconn, "inkscape:connection-start",
1444 cc->shref, false);
1445 if (cc->scpid)
1446 sp_object_setAttribute(cc->newconn, "inkscape:connection-start-point",
1447 cc->scpid, false);
1448 connection = true;
1449 }
1450
1451 if (cc->ehref)
1452 {
1453 sp_object_setAttribute(cc->newconn, "inkscape:connection-end",
1454 cc->ehref, false);
1455 if (cc->ecpid)
1456 sp_object_setAttribute(cc->newconn, "inkscape:connection-end-point",
1457 cc->ecpid, false);
1458 connection = true;
1459 }
1460 // Process pending updates.
1461 cc->newconn->updateRepr();
1462 sp_document_ensure_up_to_date(doc);
1463
1464 if (connection) {
1465 // Adjust endpoints to shape edge.
1466 sp_conn_reroute_path_immediate(SP_PATH(cc->newconn));
1467 cc->newconn->updateRepr();
1468 }
1469
1470 // Only set the selection after we are finished with creating the attributes of
1471 // the connector. Otherwise, the selection change may alter the defaults for
1472 // values like curvature in the connector context, preventing subsequent lookup
1473 // of their original values.
1474 cc->selection->set(repr);
1475 Inkscape::GC::release(repr);
1476 }
1477
1478 c->unref();
1479
1480 sp_document_done(doc, SP_VERB_CONTEXT_CONNECTOR, _("Create connector"));
1481}
1482
1483
1484static void
1485spcc_connector_finish_segment(SPConnectorContext *const cc, Geom::Point const /*p*/)
1486{
1487 if (!cc->red_curve->is_empty()) {
1488 cc->green_curve->append_continuous(cc->red_curve, 0.0625);
1489
1490 cc->p[0] = cc->p[3];
1491 cc->p[1] = cc->p[4];
1492 cc->npoints = 2;
1493
1494 cc->red_curve->reset();
1495 }
1496}
1497
1498
1499static void
1500spcc_connector_finish(SPConnectorContext *const cc)
1501{
1502 SPDesktop *const desktop = cc->desktop;
1503 desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Finishing connector"));
1504
1505 cc->red_curve->reset();
1506 spcc_concat_colors_and_flush(cc);
1507
1508 cc->npoints = 0;
1509
1510 if (cc->newConnRef) {
1511 cc->newConnRef->removeFromGraph();
1512 delete cc->newConnRef;
1513 cc->newConnRef = NULL;
1514 }
1515}
1516
1517
1518static gboolean
1519cc_generic_knot_handler(SPCanvasItem *, GdkEvent *event, SPKnot *knot)
1520{
1521 g_assert (knot != NULL);
1522
1523 g_object_ref(knot);
1524
1525 SPConnectorContext *cc = SP_CONNECTOR_CONTEXT(
1526 knot->desktop->event_context);
1527
1528 gboolean consumed = FALSE;
1529
1530 gchar* knot_tip = knot->tip ? knot->tip : cc->knot_tip;
1531 switch (event->type) {
1532 case GDK_ENTER_NOTIFY:
1533 sp_knot_set_flag(knot, SP_KNOT_MOUSEOVER, TRUE);
1534
1535 cc->active_handle = knot;
1536 if (knot_tip)
1537 {
1538 knot->desktop->event_context->defaultMessageContext()->set(
1539 Inkscape::NORMAL_MESSAGE, knot_tip);
1540 }
1541
1542 consumed = TRUE;
1543 break;
1544 case GDK_LEAVE_NOTIFY:
1545 sp_knot_set_flag(knot, SP_KNOT_MOUSEOVER, FALSE);
1546
1547 cc->active_handle = NULL;
1548
1549 if (knot_tip) {
1550 knot->desktop->event_context->defaultMessageContext()->clear();
1551 }
1552
1553 consumed = TRUE;
1554 break;
1555 default:
1556 break;
1557 }
1558
1559 g_object_unref(knot);
1560
1561 return consumed;
1562}
1563
1564
1565static gboolean
1566endpt_handler(SPKnot */*knot*/, GdkEvent *event, SPConnectorContext *cc)
1567{
1568 g_assert( SP_IS_CONNECTOR_CONTEXT(cc) );
1569
1570 gboolean consumed = FALSE;
1571
1572 switch (event->type) {
1573 case GDK_BUTTON_PRESS:
1574 g_assert( (cc->active_handle == cc->endpt_handle[0]) ||
1575 (cc->active_handle == cc->endpt_handle[1]) );
1576 if (cc->state == SP_CONNECTOR_CONTEXT_IDLE) {
1577 cc->clickeditem = cc->active_conn;
1578 cc->clickedhandle = cc->active_handle;
1579 cc_clear_active_conn(cc);
1580 cc->state = SP_CONNECTOR_CONTEXT_REROUTING;
1581
1582 // Disconnect from attached shape
1583 unsigned ind = (cc->active_handle == cc->endpt_handle[0]) ? 0 : 1;
1584 sp_conn_end_detach(cc->clickeditem, ind);
1585
1586 Geom::Point origin;
1587 if (cc->clickedhandle == cc->endpt_handle[0]) {
1588 origin = cc->endpt_handle[1]->pos;
1589 }
1590 else {
1591 origin = cc->endpt_handle[0]->pos;
1592 }
1593
1594 // Show the red path for dragging.
1595 cc->red_curve = SP_PATH(cc->clickeditem)->original_curve ? SP_PATH(cc->clickeditem)->original_curve->copy() : SP_PATH(cc->clickeditem)->curve->copy();
1596 Geom::Matrix i2d = sp_item_i2d_affine(cc->clickeditem);
1597 cc->red_curve->transform(i2d);
1598 sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(cc->red_bpath), cc->red_curve);
1599
1600 cc->clickeditem->setHidden(true);
1601
1602 // The rest of the interaction rerouting the connector is
1603 // handled by the context root handler.
1604 consumed = TRUE;
1605 }
1606 break;
1607 default:
1608 break;
1609 }
1610
1611 return consumed;
1612}
1613
1614static void cc_active_shape_add_knot(SPDesktop* desktop, SPItem* item, ConnectionPointMap &cphandles, ConnectionPoint& cp)
1615{
1616 SPKnot *knot = sp_knot_new(desktop, 0);
1617
1618 knot->setShape(SP_KNOT_SHAPE_SQUARE);
1619 knot->setSize(8);
1620 knot->setAnchor(GTK_ANCHOR_CENTER);
1621 knot->setFill(0xffffff00, 0xff0000ff, 0xff0000ff);
1622 sp_knot_update_ctrl(knot);
1623
1624 // We don't want to use the standard knot handler.
1625 g_signal_handler_disconnect(G_OBJECT(knot->item),
1626 knot->_event_handler_id);
1627 knot->_event_handler_id = 0;
1628
1629 gtk_signal_connect(GTK_OBJECT(knot->item), "event",
1630 GTK_SIGNAL_FUNC(cc_generic_knot_handler), knot);
1631 sp_knot_set_position(knot, item->avoidRef->getConnectionPointPos(cp.type, cp.id) * desktop->doc2dt(), 0);
1632 sp_knot_show(knot);
1633 cphandles[knot] = cp;
1634}
1635
1636static void cc_set_active_shape(SPConnectorContext *cc, SPItem *item)
1637{
1638 g_assert(item != NULL );
1639
1640 std::map<int, ConnectionPoint>* connpts = &item->avoidRef->connection_points;
1641
1642 if (cc->active_shape != item)
1643 {
1644 // The active shape has changed
1645 // Rebuild everything
1646 cc->active_shape = item;
1647 // Remove existing active shape listeners
1648 if (cc->active_shape_repr) {
1649 sp_repr_remove_listener_by_data(cc->active_shape_repr, cc);
1650 Inkscape::GC::release(cc->active_shape_repr);
1651
1652 sp_repr_remove_listener_by_data(cc->active_shape_layer_repr, cc);
1653 Inkscape::GC::release(cc->active_shape_layer_repr);
1654 }
1655
1656 // Listen in case the active shape changes
1657 cc->active_shape_repr = SP_OBJECT_REPR(item);
1658 if (cc->active_shape_repr) {
1659 Inkscape::GC::anchor(cc->active_shape_repr);
1660 sp_repr_add_listener(cc->active_shape_repr, &shape_repr_events, cc);
1661
1662 cc->active_shape_layer_repr = cc->active_shape_repr->parent();
1663 Inkscape::GC::anchor(cc->active_shape_layer_repr);
1664 sp_repr_add_listener(cc->active_shape_layer_repr, &layer_repr_events, cc);
1665 }
1666
1667
1668 // Set the connection points.
1669 if ( cc->connpthandles.size() )
1670 // destroy the old list
1671 while (! cc->connpthandles.empty() )
1672 {
1673 g_object_unref(cc->connpthandles.begin()->first);
1674 cc->connpthandles.erase(cc->connpthandles.begin());
1675 }
1676 // build the new one
1677 if ( connpts->size() )
1678 for (std::map<int, ConnectionPoint>::iterator it = connpts->begin(); it != connpts->end(); ++it)
1679 cc_active_shape_add_knot(cc->desktop, item, cc->connpthandles, it->second);
1680
1681 // Also add default connection points
1682 // For now, only centre default connection point will
1683 // be available
1684 ConnectionPoint centre;
1685 centre.type = ConnPointDefault;
1686 centre.id = ConnPointPosCC;
1687 cc_active_shape_add_knot(cc->desktop, item, cc->connpthandles, centre);
1688 }
1689 else
1690 {
1691 // The active shape didn't change
1692 // Update only the connection point knots
1693
1694 // Ensure the item's connection_points map
1695 // has been updated
1696 sp_document_ensure_up_to_date(SP_OBJECT_DOCUMENT(item));
1697
1698 std::set<int> seen;
1699 for ( ConnectionPointMap::iterator it = cc->connpthandles.begin(); it != cc->connpthandles.end() ;)
1700 {
1701 bool removed = false;
1702 if ( it->second.type == ConnPointUserDefined )
1703 {
1704 std::map<int, ConnectionPoint>::iterator p = connpts->find(it->second.id);
1705 if (p != connpts->end())
1706 {
1707 if ( it->second != p->second )
1708 // Connection point position has changed
1709 // Update knot position
1710 sp_knot_set_position(it->first,
1711 item->avoidRef->getConnectionPointPos(it->second.type, it->second.id) * cc->desktop->doc2dt(), 0);
1712 seen.insert(it->second.id);
1713 sp_knot_show(it->first);
1714 }
1715 else
1716 {
1717 // This connection point does no longer exist,
1718 // remove the knot
1719 ConnectionPointMap::iterator curr = it;
1720 ++it;
1721 g_object_unref( curr->first );
1722 cc->connpthandles.erase(curr);
1723 removed = true;
1724 }
1725 }
1726 else
1727 {
1728 // It's a default connection point
1729 // Just make sure it's position is correct
1730 sp_knot_set_position(it->first,
1731 item->avoidRef->getConnectionPointPos(it->second.type, it->second.id) * cc->desktop->doc2dt(), 0);
1732 sp_knot_show(it->first);
1733
1734 }
1735 if ( !removed )
1736 ++it;
1737 }
1738 // Add knots for new connection points.
1739 if (connpts->size())
1740 for ( std::map<int, ConnectionPoint>::iterator it = connpts->begin(); it != connpts->end(); ++it )
1741 if ( seen.find(it->first) == seen.end() )
1742 // A new connection point has been added
1743 // to the shape. Add a knot for it.
1744 cc_active_shape_add_knot(cc->desktop, item, cc->connpthandles, it->second);
1745 }
1746}
1747
1748
1749static void
1750cc_set_active_conn(SPConnectorContext *cc, SPItem *item)
1751{
1752 g_assert( SP_IS_PATH(item) );
1753
1754 SPCurve *curve = SP_PATH(item)->original_curve ? SP_PATH(item)->original_curve : SP_PATH(item)->curve;
1755 Geom::Matrix i2d = sp_item_i2d_affine(item);
1756
1757 if (cc->active_conn == item)
1758 {
1759 if (curve->is_empty())
1760 {
1761 // Connector is invisible because it is clipped to the boundary of
1762 // two overlpapping shapes.
1763 sp_knot_hide(cc->endpt_handle[0]);
1764 sp_knot_hide(cc->endpt_handle[1]);
1765 }
1766 else
1767 {
1768 // Just adjust handle positions.
1769 Geom::Point startpt = *(curve->first_point()) * i2d;
1770 sp_knot_set_position(cc->endpt_handle[0], startpt, 0);
1771
1772 Geom::Point endpt = *(curve->last_point()) * i2d;
1773 sp_knot_set_position(cc->endpt_handle[1], endpt, 0);
1774 }
1775
1776 return;
1777 }
1778
1779 cc->active_conn = item;
1780
1781 // Remove existing active conn listeners
1782 if (cc->active_conn_repr) {
1783 sp_repr_remove_listener_by_data(cc->active_conn_repr, cc);
1784 Inkscape::GC::release(cc->active_conn_repr);
1785 cc->active_conn_repr = NULL;
1786 }
1787
1788 // Listen in case the active conn changes
1789 cc->active_conn_repr = SP_OBJECT_REPR(item);
1790 if (cc->active_conn_repr) {
1791 Inkscape::GC::anchor(cc->active_conn_repr);
1792 sp_repr_add_listener(cc->active_conn_repr, &shape_repr_events, cc);
1793 }
1794
1795 for (int i = 0; i < 2; ++i) {
1796
1797 // Create the handle if it doesn't exist
1798 if ( cc->endpt_handle[i] == NULL ) {
1799 SPKnot *knot = sp_knot_new(cc->desktop,
1800 _("<b>Connector endpoint</b>: drag to reroute or connect to new shapes"));
1801
1802 knot->setShape(SP_KNOT_SHAPE_SQUARE);
1803 knot->setSize(7);
1804 knot->setAnchor(GTK_ANCHOR_CENTER);
1805 knot->setFill(0xffffff00, 0xff0000ff, 0xff0000ff);
1806 knot->setStroke(0x000000ff, 0x000000ff, 0x000000ff);
1807 sp_knot_update_ctrl(knot);
1808
1809 // We don't want to use the standard knot handler,
1810 // since we don't want this knot to be draggable.
1811 g_signal_handler_disconnect(G_OBJECT(knot->item),
1812 knot->_event_handler_id);
1813 knot->_event_handler_id = 0;
1814
1815 gtk_signal_connect(GTK_OBJECT(knot->item), "event",
1816 GTK_SIGNAL_FUNC(cc_generic_knot_handler), knot);
1817
1818 cc->endpt_handle[i] = knot;
1819 }
1820
1821 // Remove any existing handlers
1822 if (cc->endpt_handler_id[i]) {
1823 g_signal_handlers_disconnect_by_func(
1824 G_OBJECT(cc->endpt_handle[i]->item),
1825 (void*)G_CALLBACK(endpt_handler), (gpointer) cc );
1826 cc->endpt_handler_id[i] = 0;
1827 }
1828
1829 // Setup handlers for connector endpoints, this is
1830 // is as 'after' so that cc_generic_knot_handler is
1831 // triggered first for any endpoint.
1832 cc->endpt_handler_id[i] = g_signal_connect_after(
1833 G_OBJECT(cc->endpt_handle[i]->item), "event",
1834 G_CALLBACK(endpt_handler), cc);
1835 }
1836
1837 if (curve->is_empty())
1838 {
1839 // Connector is invisible because it is clipped to the boundary
1840 // of two overlpapping shapes. So, it doesn't need endpoints.
1841 return;
1842 }
1843
1844 Geom::Point startpt = *(curve->first_point()) * i2d;
1845 sp_knot_set_position(cc->endpt_handle[0], startpt, 0);
1846
1847 Geom::Point endpt = *(curve->last_point()) * i2d;
1848 sp_knot_set_position(cc->endpt_handle[1], endpt, 0);
1849
1850 sp_knot_show(cc->endpt_handle[0]);
1851 sp_knot_show(cc->endpt_handle[1]);
1852}
1853
1854void cc_create_connection_point(SPConnectorContext* cc)
1855{
1856 if (cc->active_shape && cc->state == SP_CONNECTOR_CONTEXT_IDLE)
1857 {
1858 if (cc->selected_handle)
1859 {