Merge branch 'master' into complex_drc_ops

This commit is contained in:
Matthias Koefferlein 2020-11-22 09:31:15 +01:00
commit db6b3d280e
266 changed files with 20889 additions and 8198 deletions

11
.gitignore vendored
View File

@ -37,6 +37,8 @@ build-*
bin-*
mkqtdecl.tmp
testtmp
*build.macos*
*bin.macos*
# private data
private
@ -54,3 +56,12 @@ src/klayout.pro.user
*.egg-info/
build/
dist/
# IDEs
.vscode
# Macos artifacts
*.dmg
*.dmg.md5

View File

@ -7,6 +7,37 @@ branches:
matrix:
include:
# python manylinux packages
- name: "cp39-cp39m-manylinux1_x86_64.whl"
os: linux
sudo: true
language: python
python: '3.9'
services:
- docker
env:
- DOCKER_IMAGE="quay.io/pypa/manylinux1_x86_64"
- PY_VERSION="cp39-cp39"
- DOCKER_BUILD=true
- TEST_IN_HOST=true
- MATRIX_EVAL=""
cache:
directories:
- ccache
- name: "cp39-cp39m-manylinux1_i686.whl"
os: linux
sudo: true
services:
- docker
env:
- DOCKER_IMAGE="quay.io/pypa/manylinux1_i686"
- PY_VERSION="cp39-cp39"
- DOCKER_BUILD=true
- MATRIX_EVAL=""
cache:
directories:
- ccache
- name: "cp38-cp38m-manylinux1_x86_64.whl"
os: linux
sudo: true

View File

@ -3,10 +3,10 @@
GITCOMMIT := $(shell git rev-parse --short HEAD)
KLAYOUT_VERSION := $(shell source version.sh && echo $$KLAYOUT_VERSION)
ifndef PYTHON_VERSION
PYTHON_VERSION := B37
PYTHON_VERSION := HB38
endif
ifndef MACOS_VERSION
MACOS_VERSION := HighSierra
MACOS_VERSION := Catalina
endif
.ONESHELL:
@ -15,11 +15,11 @@ default: help
help:
@echo "For Mac OS only"
@echo "make build PYTHON_VERSION=B37"
@echo "make deploy PYTHON_VERSION=B37"
@echo "make build PYTHON_VERSION=HB38"
@echo "make deploy PYTHON_VERSION=HB38"
@echo "make test MACOS_VERSION=HighSierra"
@echo "Valid Mac OS Versions: [Yosemite, ElCapitan, Sierra, HighSierra]"
@echo "Valid Python Version: [nil, Sys, B37]"
@echo "Valid Mac OS Versions: [Yosemite, ElCapitan, Sierra, HighSierra, Mojave, Catalina]"
@echo "Valid Python Version: [nil, Sys, HB38]"
build:
@echo "Building for Mac $(GITCOMMIT)"
@ -29,11 +29,11 @@ build:
deploy:
@echo "Deploying 4 Mac $(GITCOMMIT)"
./build4mac.py -p $(PYTHON_VERSION) -q Qt5Brew -y
test:
@echo "Testing 4 Mac $(GITCOMMIT)"
qt5.pkg.macos-$(MACOS_VERSION)-release/klayout.app/Contents/MacOS/klayout -b -r test-pylib-script.py; \
cd qt5.build.macos-$(MACOS_VERSION)-release; \
PIP_REQUIRE_VIRTUALENV="false" HW-qt5Brew.pkg.macos-$(MACOS_VERSION)-release-RsysPhb38/klayout.app/Contents/MacOS/klayout -b -r test-pylib-script.py; \
cd qt5Brew.build.macos-$(MACOS_VERSION)-release-RsysPhb38; \
ln -s klayout.app/Contents/MacOS/klayout klayout; \
export TESTTMP=testtmp; \
export TESTSRC=..; \
@ -41,18 +41,24 @@ test:
./ut_runner -h || true; \
cd ..
dmg-template:
mkdir -p testtemplate/klayout.app
./makeDMG4mac.py -p testtemplate -m -z -t klayoutDMGTemplate.dmg
cp -a klayoutDMGTemplate.dmg* macbuild/Resources/
rm -Rf testtemplate
dropbox-deploy:
@echo "Preparing for dropbox deployment $(MACOS_VERSION) $(GITCOMMIT)"
mkdir -p deploy/$(MACOS_VERSION)/$(PYTHON_VERSION)/$(KLAYOUT_VERSION); \
pwd; \
ls -lah; \
touch build.txt; \
cp build.txt deploy/$(MACOS_VERSION)/$(PYTHON_VERSION)/$(KLAYOUT_VERSION)/qt5.pkg.macos-$(MACOS_VERSION)-$(PYTHON_VERSION)-release-$(KLAYOUT_VERSION)-$(GITCOMMIT).log.txt; \
hdiutil convert macbuild/Resources/klayoutDMGTemplate.dmg -format UDRW -o work-KLayout.dmg; \
hdiutil resize -size 500m work-KLayout.dmg; \
hdiutil attach work-KLayout.dmg -readwrite -noverify -quiet -mountpoint tempKLayout -noautoopen; \
cp -a qt5.pkg.macos-$(MACOS_VERSION)-release/ tempKLayout/; \
hdiutil detach tempKLayout; \
hdiutil convert work-KLayout.dmg -format UDZO -imagekey zlib-level=9 -o deploy/$(MACOS_VERSION)/$(PYTHON_VERSION)/$(KLAYOUT_VERSION)/qt5.pkg.macos-$(MACOS_VERSION)-$(PYTHON_VERSION)-release-$(KLAYOUT_VERSION)-$(GITCOMMIT).dmg; \
md5 -q deploy/$(MACOS_VERSION)/$(PYTHON_VERSION)/$(KLAYOUT_VERSION)/qt5.pkg.macos-$(MACOS_VERSION)-$(PYTHON_VERSION)-release-$(KLAYOUT_VERSION)-$(GITCOMMIT).dmg > deploy/$(MACOS_VERSION)/$(PYTHON_VERSION)/$(KLAYOUT_VERSION)/qt5.pkg.macos-$(MACOS_VERSION)-$(PYTHON_VERSION)-release-$(KLAYOUT_VERSION)-$(GITCOMMIT).dmg.md5; \
mkdir -p deploy/$(MACOS_VERSION)/$(PYTHON_VERSION)/$(KLAYOUT_VERSION)
pwd
ls -lah
touch build.txt
cp build.txt deploy/$(MACOS_VERSION)/$(PYTHON_VERSION)/$(KLAYOUT_VERSION)/qt5.pkg.macos-$(MACOS_VERSION)-$(PYTHON_VERSION)-release-$(KLAYOUT_VERSION)-$(GITCOMMIT).log.txt
hdiutil convert macbuild/Resources/klayoutDMGTemplate.dmg -ov -format UDRW -o work-KLayout.dmg
hdiutil resize -size 500m work-KLayout.dmg
hdiutil attach -readwrite -noverify -quiet -mountpoint tempKLayout -noautoopen work-KLayout.dmg
cp -a HW-qt5Brew.pkg.macos-$(MACOS_VERSION)-release-RsysPhb38/ tempKLayout/
hdiutil detach tempKLayout
hdiutil convert work-KLayout.dmg -ov -format UDZO -imagekey zlib-level=9 -o deploy/$(MACOS_VERSION)/$(PYTHON_VERSION)/$(KLAYOUT_VERSION)/qt5.pkg.macos-$(MACOS_VERSION)-$(PYTHON_VERSION)-release-$(KLAYOUT_VERSION)-$(GITCOMMIT).dmg
md5 -q deploy/$(MACOS_VERSION)/$(PYTHON_VERSION)/$(KLAYOUT_VERSION)/qt5.pkg.macos-$(MACOS_VERSION)-$(PYTHON_VERSION)-release-$(KLAYOUT_VERSION)-$(GITCOMMIT).dmg > deploy/$(MACOS_VERSION)/$(PYTHON_VERSION)/$(KLAYOUT_VERSION)/qt5.pkg.macos-$(MACOS_VERSION)-$(PYTHON_VERSION)-release-$(KLAYOUT_VERSION)-$(GITCOMMIT).dmg.md5
rm work-KLayout.dmg

View File

@ -532,6 +532,11 @@ if [ "$BIN" = "" ]; then
BIN=$CURR_DIR/bin-$CONFIG
fi
if [ "$QMAKE_CCACHE" = 1 ]; then
echo " Compilation caching is activated."
else
echo " Compilation caching is deactivated!"
fi
echo " Installation target: $BIN"
echo " Build directory: $BUILD"

1
macbuild/.gitignore vendored
View File

@ -1 +1,2 @@
*.pyc
KLayoutDMG.applescript

View File

@ -1,4 +1,4 @@
Relevant KLayout version: 0.26.5
Relevant KLayout version: 0.26.7
# 1. Introduction
This directory **`macbuild`** contains different files required for building KLayout (http://www.klayout.de/) version 0.26.1 or later for different 64-bit Mac OSXs including:
@ -72,16 +72,16 @@ $ [python] ./build4mac.py
: MP26: use Ruby 2.6 from MacPorts |
: HB27: use Ruby 2.7 from Homebrew |
: Ana3: use Ruby 2.5 from Anaconda3 |
[-p|--python <type>] : case-insensitive type=['nil', 'Sys', 'MP37', 'HB37', 'Ana3'] | sys
[-p|--python <type>] : case-insensitive type=['nil', 'Sys', 'MP38', 'HB38', 'Ana3'] | sys
: nil: don't bind Python |
: Sys: use OS-bundled Python 2.7 [ElCapitan -- Catalina] |
: MP37: use Python 3.7 from MacPorts |
: HB37: use Python 3.7 from Homebrew |
: Ana3: use Python 3.7 from Anaconda3 |
: MP38: use Python 3.8 from MacPorts |
: HB38: use Python 3.8 from Homebrew |
: Ana3: use Python 3.8 from Anaconda3 |
[-n|--noqtbinding] : don't create Qt bindings for ruby scripts | disabled
[-m|--make <option>] : option passed to 'make' | '-j4'
[-d|--debug] : enable debug mode build | disabled
[-c|--checkcom] : check command-line and exit without building | disabled
[-c|--checkcom] : check command line and exit without building | disabled
[-y|--deploy] : deploy executables and dylibs including Qt's Frameworks | disabled
[-Y|--DEPLOY] : deploy executables and dylibs for those who built KLayout | disabled
: from the source code and use the tools in the same machine |
@ -126,64 +126,64 @@ Then the directory name will be **`LW-qt5MP.pkg.macos-Catalina-release-RsysPsys`
#### If you build KLayout from the source code AND use it on the same machine, "-Y" option is highly recommended. ####
### 6B. Fully MacPorts-flavored build with MacPorts Ruby 2.6 and MacPorts Python 3.7
### 6B. Fully MacPorts-flavored build with MacPorts Ruby 2.6 and MacPorts Python 3.8
```
$ cd /where/'build.sh'/exists
$ ./build4mac.py -q qt5macports -r mp26 -p mp37
$ ./build4mac.py -q qt5macports -r mp26 -p mp38
```
2. Confirm successful build (it will take about one hour depending on your machine spec).
3. Run **`build4mac.py`** again with the same options used in 1. PLUS "-Y" to deploy executables and libraries under **`klayout.app`** bundle.<br>
The buddy command-line tools (strm*) will also be deployed in this step.
```
$ ./build4mac.py -q qt5macports -r mp26 -p mp37 -Y
$ ./build4mac.py -q qt5macports -r mp26 -p mp38 -Y
```
The application bundle **`klayout.app`** is located under:<br>
**`LW-qt5MP.pkg.macos-Catalina-release-Rmp26Pmp37`** directory, where
**`LW-qt5MP.pkg.macos-Catalina-release-Rmp26Pmp38`** directory, where
* "LW-" means that this is a lightweight package.
* "qt5MP" means that Qt5 from MacPorts is used.
* "Rmp26Pmp37" means that Ruby is 2.6 from MacPorts; Python is 3.7 from MacPorts.
* "Rmp26Pmp38" means that Ruby is 2.6 from MacPorts; Python is 3.8 from MacPorts.
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
### 6C. Fully Homebrew-flavored build with Homebrew Ruby 2.7 and Homebrew Python 3.7
### 6C. Fully Homebrew-flavored build with Homebrew Ruby 2.7 and Homebrew Python 3.8
```
$ cd /where/'build.sh'/exists
$ ./build4mac.py -q qt5brew -r hb27 -p hb37
$ ./build4mac.py -q qt5brew -r hb27 -p hb38
```
2. Confirm successful build (it will take about one hour depending on your machine spec).
3. Run **`build4mac.py`** again with the same options used in 1. PLUS "-Y" to deploy executables and libraries under **`klayout.app`** bundle.<br>
The buddy command-line tools (strm*) will also be deployed in this step.
```
$ ./build4mac.py -q qt5brew -r hb27 -p hb37 -Y
$ ./build4mac.py -q qt5brew -r hb27 -p hb38 -Y
```
The application bundle **`klayout.app`** is located under:<br>
**`LW-qt5Brew.pkg.macos-Catalina-release-Rhb27Phb37`** directory, where
**`LW-qt5Brew.pkg.macos-Catalina-release-Rhb27Phb38`** directory, where
* "LW-" means that this is a lightweight package.
* "qt5Brew" means that Qt5 from Homebrew is used.
* "Rhb27Phb37" means that Ruby is 2.7 from Homebrew; Python is 3.7 from Homebrew.
* "Rhb27Phb38" means that Ruby is 2.7 from Homebrew; Python is 3.8 from Homebrew.
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
### 6D. Partially Homebrew-flavored build with System Ruby and Homebrew Python 3.7
### 6D. Partially Homebrew-flavored build with System Ruby and Homebrew Python 3.8
```
$ cd /where/'build.sh'/exists
$ ./build4mac.py -q qt5brew -r sys -p hb37
$ ./build4mac.py -q qt5brew -r sys -p hb38
```
2. Confirm successful build (it will take about one hour depending on your machine spec).
3. Run **`build4mac.py`** again with the same options used in 1. PLUS "-y" to deploy executables and libraries (including Qt's frameworks and Python frameworks) under **`klayout.app`** bundle.<br>
The buddy command-line tools (strm*) will also be deployed in this step.
```
$ ./build4mac.py -q qt5brew -r sys -p hb37 -y
$ ./build4mac.py -q qt5brew -r sys -p hb38 -y
```
The application bundle **`klayout.app`** is located under:<br>
**`HW-qt5Brew.pkg.macos-Catalina-release-RsysPhb37`** directory, where
**`HW-qt5Brew.pkg.macos-Catalina-release-RsysPhb38`** directory, where
* "HW-" means that this is a heavyweight package because both Qt5 and Python are deployed.
* "qt5Brew" means that Qt5 from Homebrew is used.
* "RsysPhb37" means that Ruby is OS-bundled; Python is 3.7 from Homebrew.
* "RsysPhb38" means that Ruby is OS-bundled; Python is 3.8 from Homebrew.
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
### Important ###
So far, deployment of Homebrew Ruby is not supported. <br>
Therefore, if you intend to use "-y" option, you need to use "-r sys" for building.
### 6E. Fully Anaconda3-flavored build with Anaconda3 Ruby 2.5 and Anaconda3 Python 3.7
### 6E. Fully Anaconda3-flavored build with Anaconda3 Ruby 2.5 and Anaconda3 Python 3.8
```
$ cd /where/'build.sh'/exists
$ ./build4mac.py -q qt5ana3 -r ana3 -p ana3
@ -198,7 +198,7 @@ $ ./build4mac.py -q qt5ana3 -r ana3 -p ana3 -Y
**`LW-qt5Ana3.pkg.macos-Catalina-release-Rana3Pana3`** directory, where
* "LW-" means that this is a lightweight package.
* "qt5Ana3" means that Qt5 from Anaconda3 is used.
* "Rana3Pana3" means that Ruby (2.5) is from Anaconda3; Python (3.7) is from Anaconda3.
* "Rana3Pana3" means that Ruby (2.5) is from Anaconda3; Python (3.8) is from Anaconda3.
4. Copy/move the generated application bundle **`klayout.app`** to your **`/Applications`** directory for installation.
5. You may have to set `PYTHONHOME` environment variable like:
```

View File

@ -26,6 +26,12 @@
-------------------------------------------------------------------------------------------------
on run (volumeName) -- most likely, the volume name is "KLayout"
tell application "Finder"
repeat 20 times
if (exists (disk (volumeName as string))) then
exit repeat
end if
delay 1
end repeat
tell disk (volumeName as string)
-- [1] Open the volume
open

File diff suppressed because it is too large Load Diff

View File

@ -104,9 +104,10 @@ RubyMojave = { 'exe': '/System/Library/Frameworks/Ruby.framework/Versions/2
# !!! Catalina does not allow to hack the "/System" directory; it's READ ONLY even for the super user!
# Hence, we need to refer to the Ruby header file in "Xcode.app" directly.
# [Key Type Name] = 'Sys'
CatalinaSDK = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk"
CatalinaSDK = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk"
RubyCatalina = { 'exe': '/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/bin/ruby',
'inc': '%s/System/Library/Frameworks/Ruby.framework/Headers' % CatalinaSDK,
'inc2': '%s/System/Library/Frameworks/Ruby.framework/Headers/ruby' % CatalinaSDK,
'lib': '/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/libruby.dylib'
}
@ -154,7 +155,7 @@ RubyDictionary = { 'nil' : None,
#-----------------------------------------------------
PythonNil = [ 'nil' ]
PythonSys = [ 'PythonElCapitan', 'PythonSierra', 'PythonHighSierra', 'PythonMojave', 'PythonCatalina' ]
PythonExt = [ 'Python37MacPorts', 'Python37Brew', 'PythonAnaconda3' ]
PythonExt = [ 'Python38MacPorts', 'Python38Brew', 'PythonAnaconda3' ]
Pythons = PythonNil + PythonSys + PythonExt
#-----------------------------------------------------
@ -204,30 +205,45 @@ PythonCatalina = { 'exe': '/System/Library/Frameworks/Python.framework/Versions
'lib': '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/libpython2.7.dylib'
}
# Python 3.7 from MacPorts (https://www.macports.org/) *+*+*+ EXPERIMENTAL *+*+*+
# install with 'sudo port install python37'
# [Key Type Name] = 'MP37'
Python37MacPorts= { 'exe': '/opt/local/Library/Frameworks/Python.framework/Versions/3.7/bin/python3.7m',
'inc': '/opt/local/Library/Frameworks/Python.framework/Versions/3.7/include/python3.7m',
'lib': '/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/libpython3.7m.dylib'
# Python 3.8 from MacPorts (https://www.macports.org/) *+*+*+ EXPERIMENTAL *+*+*+
# install with 'sudo port install python38'
# [Key Type Name] = 'MP38'
Python38MacPorts= { 'exe': '/opt/local/Library/Frameworks/Python.framework/Versions/3.8/bin/python3.8',
'inc': '/opt/local/Library/Frameworks/Python.framework/Versions/3.8/include/python3.8',
'lib': '/opt/local/Library/Frameworks/Python.framework/Versions/3.8/lib/libpython3.8.dylib'
}
# Python 3.7 from Homebrew *+*+*+ EXPERIMENTAL *+*+*+
# Python 3.8 from Homebrew *+*+*+ EXPERIMENTAL *+*+*+
# install with 'brew install python'
# [Key Type Name] = 'HB37'
HBPython37FrameworkPath = '/usr/local/opt/python3/Frameworks/Python.framework'
Python37Brew = { 'exe': '%s/Versions/3.7/bin/python3.7m' % HBPython37FrameworkPath,
'inc': '%s/Versions/3.7/include/python3.7m' % HBPython37FrameworkPath,
'lib': '%s/Versions/3.7/lib/libpython3.7m.dylib' % HBPython37FrameworkPath
# [Key Type Name] = 'HB38'
HBPython38FrameworkPath = '/usr/local/opt/python3/Frameworks/Python.framework'
Python38Brew = { 'exe': '%s/Versions/3.8/bin/python3.8' % HBPython38FrameworkPath,
'inc': '%s/Versions/3.8/include/python3.8' % HBPython38FrameworkPath,
'lib': '%s/Versions/3.8/lib/libpython3.8.dylib' % HBPython38FrameworkPath
}
# Python 3.7 bundled with anaconda3 installed under /Applications/anaconda3/ *+*+*+ EXPERIMENTAL *+*+*+
# # Latest Python from Homebrew *+*+*+ EXPERIMENTAL *+*+*+
# # install with 'brew install python'
# # [Key Type Name] = 'HBAuto'
# import glob
# # In my system, there are four candidates: (python, python3, python@3, python@3.8)
# # Hard to tell which is going to be available to the user. Picking the last one
# HBAutoFrameworkPath = glob.glob("/usr/local/opt/python*/Frameworks/Python.framework/")[-1]
# # expand 3* into _py_version, there should be only one, but I am taking no chances.
# HBAutoFrameworkVersionPath, _py_version = os.path.split(glob.glob("%s/Versions/3*" % HBAutoFrameworkPath)[0])
# PythonAutoBrew = { 'exe': '%s/bin/python%s' % (HBAutoFrameworkVersionPath, _py_version),
# 'inc': '%s/include/python%s' % (HBAutoFrameworkVersionPath, _py_version),
# 'lib': glob.glob("%s/lib/*.dylib" % HBAutoFrameworkVersionPath)[0]
# }
# Python 3.8 bundled with anaconda3 installed under /Applications/anaconda3/ *+*+*+ EXPERIMENTAL *+*+*+
# The standard installation deploys the tool under $HOME/opt/anaconda3/.
# If so, you need to make a symbolic link: /Applications/anaconda3 ---> $HOME/opt/anaconda3/
# [Key Type Name] = 'Ana3'
PythonAnaconda3 = { 'exe': '/Applications/anaconda3/bin/python3.7m',
'inc': '/Applications/anaconda3/include/python3.7m',
'lib': '/Applications/anaconda3/lib/libpython3.7m.dylib'
PythonAnaconda3 = { 'exe': '/Applications/anaconda3/bin/python3.8',
'inc': '/Applications/anaconda3/include/python3.8',
'lib': '/Applications/anaconda3/lib/libpython3.8.dylib'
}
# Consolidated dictionary kit for Python
@ -237,8 +253,8 @@ PythonDictionary= { 'nil' : None,
'PythonHighSierra': PythonHighSierra,
'PythonMojave' : PythonMojave,
'PythonCatalina' : PythonCatalina,
'Python37MacPorts': Python37MacPorts,
'Python37Brew' : Python37Brew,
'Python38MacPorts': Python38MacPorts,
'Python38Brew' : Python38Brew,
'PythonAnaconda3' : PythonAnaconda3
}

View File

@ -1,7 +1,7 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
#===============================================================================
#========================================================================================
# File: "macbuild/build4mac_util.py"
#
# Here are utility functions and classes ...
@ -9,7 +9,7 @@
# version 0.26.1 or later on different Apple Mac OSX platforms.
#
# This file is imported by 'build4mac.py' script.
#===============================================================================
#========================================================================================
from __future__ import print_function # to use print() of Python 3 in Python >= 2.7
import sys
import os
@ -18,14 +18,14 @@ import string
import subprocess
import shutil
#-------------------------------------------------------------------------------
#----------------------------------------------------------------------------------------
## To import global dictionaries of different modules
#-------------------------------------------------------------------------------
#----------------------------------------------------------------------------------------
mydir = os.path.dirname(os.path.abspath(__file__))
sys.path.append( mydir )
from build4mac_env import *
#------------------------------------------------------------------------------
#----------------------------------------------------------------------------------------
## To decompose strings obtained by 'otool -L <*.dylib>' command and to
# generate a dictionary of KLayout's inter-library dependency.
#
@ -41,81 +41,81 @@ from build4mac_env import *
# :
#
# @return a dictionary
#------------------------------------------------------------------------------
#----------------------------------------------------------------------------------------
def DecomposeLibraryDependency( depstr ):
alllines = depstr.split('\n')
numlines = len(alllines)
dependent = alllines[0].split(':')[0].strip()
supporters = []
for line in alllines[1:]:
supporter = line.strip().split(' ')[0].strip()
if not supporter == '':
supporters.append(supporter)
return { dependent: supporters }
alllines = depstr.split('\n')
numlines = len(alllines)
dependent = alllines[0].split(':')[0].strip()
supporters = []
for line in alllines[1:]:
supporter = line.strip().split(' ')[0].strip()
if not supporter == '':
supporters.append(supporter)
return { dependent: supporters }
#------------------------------------------------------------------------------
#----------------------------------------------------------------------------------------
## To print the contents of a library dependency dictionary
#
# @param[in] depdic dictionary
# @param[in] pathdic path dictionary
# @param[in] namedic dictionary name
#------------------------------------------------------------------------------
#----------------------------------------------------------------------------------------
def PrintLibraryDependencyDictionary( depdic, pathdic, namedic ):
keys = depdic.keys()
print("")
print("##### Contents of <%s> #####:" % namedic )
for key in keys:
supporters = depdic[key]
keyName = os.path.basename(key)
print( " %s: (%s)" % (key, pathdic[keyName]) )
for item in supporters:
itemName = os.path.basename(item)
if itemName != keyName and (itemName in pathdic):
print( " %s (%s)" % (item, pathdic[itemName]) )
keys = depdic.keys()
print("")
print("##### Contents of <%s> #####:" % namedic )
for key in keys:
supporters = depdic[key]
keyName = os.path.basename(key)
print( " %s: (%s)" % (key, pathdic[keyName]) )
for item in supporters:
itemName = os.path.basename(item)
if itemName != keyName and (itemName in pathdic):
print( " %s (%s)" % (item, pathdic[itemName]) )
#------------------------------------------------------------------------------
#----------------------------------------------------------------------------------------
## To set and change identification name of KLayout's dylib
#
# @param[in] libdic inter-library dependency dictionary
#
# @return 0 on success; non-zero on failure
#------------------------------------------------------------------------------
#----------------------------------------------------------------------------------------
def SetChangeIdentificationNameOfDyLib( libdic, pathDic ):
cmdNameId = XcodeToolChain['nameID']
cmdNameChg = XcodeToolChain['nameCH']
dependentLibs = libdic.keys()
cmdNameId = XcodeToolChain['nameID']
cmdNameChg = XcodeToolChain['nameCH']
dependentLibs = libdic.keys()
for lib in dependentLibs:
#-----------------------------------------------------------
# [1] Set the identification name of each dependent library
#-----------------------------------------------------------
nameOld = "%s" % lib
libName = os.path.basename(lib)
nameNew = pathDic[libName]
command = "%s %s %s" % ( cmdNameId, nameNew, nameOld )
if subprocess.call( command, shell=True ) != 0:
msg = "!!! Failed to set the new identification name to <%s> !!!"
print( msg % lib, file=sys.stderr )
return 1
#-------------------------------------------------------------------------
# [2] Make the library aware of the new identifications of all supporters
#-------------------------------------------------------------------------
supporters = libdic[lib]
for sup in supporters:
supName = os.path.basename(sup)
if libName != supName and (supName in pathDic):
nameOld = "%s" % sup
nameNew = pathDic[supName]
command = "%s %s %s %s" % ( cmdNameChg, nameOld, nameNew, lib )
for lib in dependentLibs:
#-----------------------------------------------------------
# [1] Set the identification name of each dependent library
#-----------------------------------------------------------
nameOld = "%s" % lib
libName = os.path.basename(lib)
nameNew = pathDic[libName]
command = "%s %s %s" % ( cmdNameId, nameNew, nameOld )
if subprocess.call( command, shell=True ) != 0:
msg = "!!! Failed to make the library aware of the new identification name <%s> of supporter <%s> !!!"
print( msg % (nameNew, sup), file=sys.stderr )
return 1
# for-lib
return 0
msg = "!!! Failed to set the new identification name to <%s> !!!"
print( msg % lib, file=sys.stderr )
return 1
#------------------------------------------------------------------------------
#-------------------------------------------------------------------------
# [2] Make the library aware of the new identifications of all supporters
#-------------------------------------------------------------------------
supporters = libdic[lib]
for sup in supporters:
supName = os.path.basename(sup)
if libName != supName and (supName in pathDic):
nameOld = "%s" % sup
nameNew = pathDic[supName]
command = "%s %s %s %s" % ( cmdNameChg, nameOld, nameNew, lib )
if subprocess.call( command, shell=True ) != 0:
msg = "!!! Failed to make the library aware of the new identification name <%s> of supporter <%s> !!!"
print( msg % (nameNew, sup), file=sys.stderr )
return 1
# for-lib
return 0
#----------------------------------------------------------------------------------------
## To set the identification names of KLayout's libraries to an executable
# and make the application aware of the library locations
#
@ -146,259 +146,321 @@ def SetChangeIdentificationNameOfDyLib( libdic, pathDic ):
# +-- 'strmxor'
#
# @return 0 on success; non-zero on failure
#------------------------------------------------------------------------------
#----------------------------------------------------------------------------------------
def SetChangeLibIdentificationName( executable, relativedir ):
cmdNameId = XcodeToolChain['nameID']
cmdNameChg = XcodeToolChain['nameCH']
otoolCm = "otool -L %s | grep libklayout" % executable
otoolOut = os.popen( otoolCm ).read()
exedepdic = DecomposeLibraryDependency( executable + ":\n" + otoolOut )
keys = exedepdic.keys()
deplibs = exedepdic[ list(keys)[0] ]
cmdNameId = XcodeToolChain['nameID']
cmdNameChg = XcodeToolChain['nameCH']
otoolCm = "otool -L %s | grep libklayout" % executable
otoolOut = os.popen( otoolCm ).read()
exedepdic = DecomposeLibraryDependency( executable + ":\n" + otoolOut )
keys = exedepdic.keys()
deplibs = exedepdic[ list(keys)[0] ]
for lib in deplibs:
#-----------------------------------------------------------
# [1] Set the identification names for the library
#-----------------------------------------------------------
nameOld = "klayout.app/Contents/Frameworks/%s" % lib
nameNew = "@executable_path/%s/%s" % ( relativedir, lib )
command = "%s %s %s" % ( cmdNameId, nameNew, nameOld )
if subprocess.call( command, shell=True ) != 0:
msg = "!!! Failed to set the new identification name to <%s> !!!"
print( msg % lib, file=sys.stderr )
return 1
#-----------------------------------------------------------
# [2] Make the application aware of the new identification
#-----------------------------------------------------------
nameOld = "%s" % lib
nameNew = "@executable_path/%s/%s" % ( relativedir, lib )
command = "%s %s %s %s" % ( cmdNameChg, nameOld, nameNew, executable )
if subprocess.call( command, shell=True ) != 0:
msg = "!!! Failed to make the application aware of the new identification name <%s> !!!"
print( msg % nameNew, file=sys.stderr )
return 1
# for-lib
return 0
def WalkLibDependencyTree( dylibPath, depth=0, filter_regex=r'\t+/usr/local/opt'):
NOTHINGTODO = [] # return empty list if nothing to do.
cmdNameId = XcodeToolChain['nameID']
cmdNameChg = XcodeToolChain['nameCH']
otoolCm = 'otool -L %s | grep -E "%s"' % (dylibPath, filter_regex)
otoolOut = os.popen( otoolCm ).read()
exedepdic = DecomposeLibraryDependency( dylibPath + ":\n" + otoolOut )
keys = exedepdic.keys()
deplibs = exedepdic[ list(keys)[0] ]
if depth < 5:
if len(deplibs) > 0:
for idx, lib in enumerate(deplibs):
lib = str(lib)
if lib != list(keys)[0]:
deplibs[idx] = WalkLibDependencyTree(lib, depth+1, filter_regex)
if depth == 0:
return deplibs
return exedepdic
else:
raise RuntimeError("Exceeded maximum recursion depth.")
def WalkFrameworkPaths(frameworkPaths, filter_regex=r'\.(so|dylib)$', search_path_filter=r'\t+/usr/local/opt'):
if isinstance(frameworkPaths, str):
frameworkPathsIter = [frameworkPaths]
else:
frameworkPathsIter = frameworkPaths
dependency_dict = dict()
for frameworkPath in frameworkPathsIter:
# print("Calling:", 'find %s -type f | grep -E "%s"' % (frameworkPath, filter_regex))
find_grep_results = os.popen('find %s -type f | grep -E "%s"' % (frameworkPath, filter_regex)).read().split('\n')
framework_files = filter(lambda x: x != '',
map(lambda x: x.strip(),
find_grep_results))
dependency_dict[frameworkPath] = list()
for idx, file in enumerate(framework_files):
dict_file = {file: WalkLibDependencyTree(file, filter_regex=search_path_filter)}
dependency_dict[frameworkPath].append(dict_file)
return dependency_dict
def WalkDictTree(dependencyDict, visited_files):
libNameChanges = list()
for lib, dependencies in dependencyDict.items():
if lib in visited_files:
continue
dependency_list = list()
if isinstance(dependencies, list):
for deplib in dependencies:
if isinstance(deplib, str):
dependency_list.append(deplib)
if deplib not in visited_files:
visited_files.append(deplib)
elif isinstance(deplib, dict):
dependency_list.append(next(iter(deplib)))
libNameChanges.extend(WalkDictTree(deplib, visited_files))
else:
#raise RuntimeError("Unexpected value: %s" % deplib)
pass
else:
raise RuntimeError("Unexpected value: %s" % dependencies)
if len(dependency_list) > 0:
libNameChanges.append((lib, dependency_list))
else:
libNameChanges.append((lib, ))
visited_files.append(lib)
return libNameChanges
def FindFramework(path, root_path):
relPath = os.path.relpath(path, root_path)
return os.path.join(root_path, relPath.split(os.sep)[0])
def ResolveExecutablePath(path, executable_path):
""" Transforms @executable_path into executable_path"""
p = path.replace("@executable_path", "/%s/" % executable_path)
return p
def DetectChanges(frameworkDependencyDict):
visited_files = list()
libNameChanges = list()
for framework, libraries in frameworkDependencyDict.items():
for libraryDict in libraries:
libNameChanges.extend(WalkDictTree(libraryDict, visited_files))
# Changes are stored in libNameChanges in the form of ('lib.dylib', ['dep1.dylib', ...])
return libNameChanges
def PerformChanges(frameworkDependencyDict, replaceFromToPairs=None, executable_path="/tmp/klayout"):
libNameChanges = DetectChanges(frameworkDependencyDict)
cmdNameId = XcodeToolChain['nameID']
cmdNameChg = XcodeToolChain['nameCH']
if replaceFromToPairs is not None:
for libNameChange in libNameChanges:
libNameChangeIterator = iter(libNameChange)
lib = next(libNameChangeIterator)
try:
dependencies = next(libNameChangeIterator)
except StopIteration:
dependencies = list()
for replaceFrom, replaceTo, libdir in replaceFromToPairs:
fileName = ResolveExecutablePath(lib.replace(replaceFrom, replaceTo), executable_path)
if fileName.startswith('/usr'):
# print(f'skipping fileName: {fileName}')
continue
if lib.find(replaceFrom) >= 0:
if libdir:
frameworkPath = FindFramework(lib, replaceFrom)
else:
frameworkPath = lib
destFrameworkPath = frameworkPath.replace(replaceFrom, replaceTo)
destFrameworkPath = ResolveExecutablePath(destFrameworkPath, executable_path)
if not os.path.exists(fileName):
print (lib.replace(replaceFrom, replaceTo), "DOES NOT EXIST")
print ("COPY", frameworkPath, " -> ", destFrameworkPath)
shutil.copytree(frameworkPath, destFrameworkPath)
nameId = lib.replace(replaceFrom, replaceTo)
command = "%s %s %s" % ( cmdNameId, nameId, fileName )
if not os.access(fileName, os.W_OK):
command = "chmod u+w %s; %s; chmod u-w %s" % (fileName, command, fileName)
# print("\t%s" % command)
if subprocess.call( command, shell=True ) != 0:
for lib in deplibs:
#-----------------------------------------------------------
# [1] Set the identification names for the library
#-----------------------------------------------------------
nameOld = "klayout.app/Contents/Frameworks/%s" % lib
nameNew = "@executable_path/%s/%s" % ( relativedir, lib )
command = "%s %s %s" % ( cmdNameId, nameNew, nameOld )
if subprocess.call( command, shell=True ) != 0:
msg = "!!! Failed to set the new identification name to <%s> !!!"
print( msg % fileName, file=sys.stderr )
print( msg % lib, file=sys.stderr )
return 1
for dependency in dependencies:
if dependency.find(replaceFrom) >= 0:
print("\tIn:", fileName)
print("\tRENAME", dependency, " -> ", dependency.replace(replaceFrom, replaceTo))
#-----------------------------------------------------------
# [2] Make the application aware of the new identification
#-----------------------------------------------------------
nameOld = "%s" % lib
nameNew = "@executable_path/%s/%s" % ( relativedir, lib )
command = "%s %s %s %s" % ( cmdNameChg, nameOld, nameNew, executable )
if subprocess.call( command, shell=True ) != 0:
msg = "!!! Failed to make the application aware of the new identification name <%s> !!!"
print( msg % nameNew, file=sys.stderr )
return 1
# for-lib
return 0
# Try changing id first
nameId = dependency.replace(replaceFrom, replaceTo)
command = "%s %s %s" % ( cmdNameId, nameId, fileName)
if not os.access(str(fileName), os.W_OK):
command = "chmod u+w %s; %s; chmod u-w %s" % (fileName, command, fileName)
# print("\t%s" % command)
if subprocess.call( command, shell=True ) != 0:
msg = "!!! Failed to set the new identification name to <%s> !!!"
print( msg % fileName, file=sys.stderr )
return 1
#----------------------------------------------------------------------------------------
## To make a library dependency dictionary by recursively walk down the lib hierarchy
#
# @param[in] dylibPath: dylib path
# @param[in] depth: hierarchy depth (< 5)
# @param[in] filter_regex: filter regular expression
#
# @return a dictionary
#----------------------------------------------------------------------------------------
def WalkLibDependencyTree( dylibPath, depth=0, filter_regex=r'\t+/usr/local/opt' ):
otoolCm = 'otool -L %s | grep -E "%s"' % (dylibPath, filter_regex)
otoolOut = os.popen( otoolCm ).read()
exedepdic = DecomposeLibraryDependency( dylibPath + ":\n" + otoolOut )
keys = exedepdic.keys()
deplibs = exedepdic[ list(keys)[0] ]
# Rename dependencies
nameOld = dependency
nameNew = dependency.replace(replaceFrom, replaceTo)
command = "%s %s %s %s" % ( cmdNameChg, nameOld, nameNew, str(fileName) )
if not os.access(str(fileName), os.W_OK):
command = "chmod u+w %s; %s; chmod u-w %s" % (fileName, command, fileName)
if depth < 5:
if len(deplibs) > 0:
for idx, lib in enumerate(deplibs):
lib = str(lib)
if lib != list(keys)[0]:
deplibs[idx] = WalkLibDependencyTree(lib, depth+1, filter_regex)
if depth == 0:
return deplibs
return exedepdic
else:
raise RuntimeError( "Exceeded maximum recursion depth." )
# print("\t%s" % command)
if subprocess.call( command, shell=True ) != 0:
msg = "!!! Failed to set the new identification name to <%s> !!!"
print( msg % fileName, file=sys.stderr )
return 1
#----------------------------------------------------------------------------------------
## To make a library dependency dictionary by recursively walk down the Framework
#
# @param[in] frameworkPaths: Framework path
# @param[in] filter_regex: filter regular expression
# @param[in] search_path_filter: search path filter regular expression
#
# @return a dictionary
#----------------------------------------------------------------------------------------
def WalkFrameworkPaths( frameworkPaths, filter_regex=r'\.(so|dylib)$',
search_path_filter=r'\t+/usr/local/opt' ):
if isinstance(frameworkPaths, str):
frameworkPathsIter = [frameworkPaths]
else:
frameworkPathsIter = frameworkPaths
#------------------------------------------------------------------------------
dependency_dict = dict()
for frameworkPath in frameworkPathsIter:
# print("Calling:", 'find %s -type f | grep -E "%s"' % (frameworkPath, filter_regex))
find_grep_results = os.popen('find %s -type f | grep -E "%s"' % (frameworkPath, filter_regex)).read().split('\n')
framework_files = filter(lambda x: x != '',
map(lambda x: x.strip(),
find_grep_results))
dependency_dict[frameworkPath] = list()
for idx, file in enumerate(framework_files):
dict_file = {file: WalkLibDependencyTree(file, filter_regex=search_path_filter)}
dependency_dict[frameworkPath].append(dict_file)
return dependency_dict
#----------------------------------------------------------------------------------------
## To make a list of changed libraries
#
# @param[in] dependencyDict: library dependency dictionary
# @param[in] visited_files: list of visited files
#
# @return a list
#----------------------------------------------------------------------------------------
def WalkDictTree( dependencyDict, visited_files ):
libNameChanges = list()
for lib, dependencies in dependencyDict.items():
if lib in visited_files:
continue
dependency_list = list()
if isinstance(dependencies, list):
for deplib in dependencies:
if isinstance(deplib, str):
dependency_list.append(deplib)
if deplib not in visited_files:
visited_files.append(deplib)
elif isinstance(deplib, dict):
dependency_list.append(next(iter(deplib)))
libNameChanges.extend(WalkDictTree(deplib, visited_files))
else:
#raise RuntimeError("Unexpected value: %s" % deplib)
pass
else:
raise RuntimeError("Unexpected value: %s" % dependencies)
if len(dependency_list) > 0:
libNameChanges.append((lib, dependency_list))
else:
libNameChanges.append((lib, ))
visited_files.append(lib)
return libNameChanges
#----------------------------------------------------------------------------------------
## To find the Framework name from a library name
#
# @param[in] path: path to a library
# @param[in] root_path: root path
#
# @return the path to a Framework
#----------------------------------------------------------------------------------------
def FindFramework( path, root_path ):
relPath = os.path.relpath(path, root_path)
frmPath = os.path.join(root_path, relPath.split(os.sep)[0])
#print( "###", frmPath, path, root_path )
return frmPath
#----------------------------------------------------------------------------------------
## To resolve an executable path
#
# @param[in] path: a path to resolve
# @param[in] executable_path: an executable path
#
# @return the resolved path
#----------------------------------------------------------------------------------------
def ResolveExecutablePath( path, executable_path ):
""" Transforms @executable_path into executable_path"""
p = path.replace("@executable_path", "/%s/" % executable_path)
return p
#----------------------------------------------------------------------------------------
## To detect the changed library names
#
# @param[in] frameworkDependencyDict: framework dependency dictionary
#
# @return a list of changes, each of which is stored in the form of
# * ('lib.dylib', ['dep1.dylib', ...])
# OR
# * ('lib.dylib',)
#----------------------------------------------------------------------------------------
def DetectChanges(frameworkDependencyDict):
visited_files = list()
libNameChanges = list()
for framework, libraries in frameworkDependencyDict.items():
for libraryDict in libraries:
libNameChanges.extend(WalkDictTree(libraryDict, visited_files))
return libNameChanges
#----------------------------------------------------------------------------------------
## To perform the required changes
#
# @param[in] frameworkDependencyDict: framework dependency dictionary
# @param[in] replaceFromToPairs: (from, to)-pair for replacement
# @param[in] executable_path: executable path
#
# @return 0 on success; > 0 on failure
#----------------------------------------------------------------------------------------
def PerformChanges( frameworkDependencyDict, replaceFromToPairs=None, executable_path="/tmp/klayout" ):
libNameChanges = DetectChanges(frameworkDependencyDict)
#print(libNameChanges)
cmdNameId = XcodeToolChain['nameID']
cmdNameChg = XcodeToolChain['nameCH']
if replaceFromToPairs is None:
return 0
else:
for libNameChange in libNameChanges:
libNameChangeIterator = iter(libNameChange)
lib = next(libNameChangeIterator)
try:
dependencies = next(libNameChangeIterator)
except StopIteration:
dependencies = list()
for replaceFrom, replaceTo, libdir in replaceFromToPairs:
fileName = ResolveExecutablePath(lib.replace(replaceFrom, replaceTo), executable_path)
if fileName.startswith('/usr'):
# print(f'skipping fileName: {fileName}')
continue
if lib.find(replaceFrom) >= 0:
if libdir:
frameworkPath = FindFramework(lib, replaceFrom)
else:
frameworkPath = lib
destFrameworkPath = frameworkPath.replace(replaceFrom, replaceTo)
destFrameworkPath = ResolveExecutablePath(destFrameworkPath, executable_path)
if not os.path.exists(fileName):
print( " NOT FOUND:", lib.replace(replaceFrom, replaceTo) )
print( " COPYING:", frameworkPath, " -> ", destFrameworkPath )
shutil.copytree(frameworkPath, destFrameworkPath)
nameId = lib.replace(replaceFrom, replaceTo)
command = "%s %s %s" % ( cmdNameId, nameId, fileName )
if not os.access(fileName, os.W_OK):
command = "chmod u+w %s; %s; chmod u-w %s" % (fileName, command, fileName)
# print("\t%s" % command)
if subprocess.call( command, shell=True ) != 0:
msg = "!!! Failed to set the new identification name to <%s> !!!"
print( msg % fileName, file=sys.stderr )
return 1
for dependency in dependencies:
if dependency.find(replaceFrom) >= 0:
print( " IN:", fileName )
print( " RENAMING:", dependency, " -> ", dependency.replace(replaceFrom, replaceTo) )
# Try changing id first
nameId = dependency.replace(replaceFrom, replaceTo)
command = "%s %s %s" % ( cmdNameId, nameId, fileName)
if not os.access(str(fileName), os.W_OK):
command = "chmod u+w %s; %s; chmod u-w %s" % (fileName, command, fileName)
# print("\t%s" % command)
if subprocess.call( command, shell=True ) != 0:
msg = "!!! Failed to set the new identification name to <%s> !!!"
print( msg % fileName, file=sys.stderr )
return 1
# Rename dependencies
nameOld = dependency
nameNew = dependency.replace(replaceFrom, replaceTo)
command = "%s %s %s %s" % ( cmdNameChg, nameOld, nameNew, str(fileName) )
if not os.access(str(fileName), os.W_OK):
command = "chmod u+w %s; %s; chmod u-w %s" % (fileName, command, fileName)
# print("\t%s" % command)
if subprocess.call( command, shell=True ) != 0:
msg = "!!! Failed to set the new identification name to <%s> !!!"
print( msg % fileName, file=sys.stderr )
return 1
return 0
#----------------------------------------------------------------------------------------
## To get KLayout's version from a file; most likely from 'version.sh'
#
# @param[in] verfile version file from which version is retrieved
#
# @return version string
#------------------------------------------------------------------------------
#----------------------------------------------------------------------------------------
def GetKLayoutVersionFrom( verfile='version.h' ):
version = "?.?.?"
try:
fd = open( verfile, "r" )
contents = fd.readlines()
fd.close()
except Exception as e:
version = "?.?.?"
try:
fd = open( verfile, "r" )
contents = fd.readlines()
fd.close()
except Exception as e:
return version
verReg = re.compile( u'(KLAYOUT_VERSION=\")([0-9A-Z_a-z\.]+)(\")' )
for line in contents:
m = verReg.match(line)
if m:
# print(m.group(0)) # KLAYOUT_VERSION="0.26.1"
# print(m.group(1)) # KLAYOUT_VERSION="
# print(m.group(2)) # 0.26.1
# print(m.group(3)) # "
version = m.group(2)
return version
return version
verReg = re.compile( u'(KLAYOUT_VERSION=\")([0-9A-Z_a-z\.]+)(\")' )
for line in contents:
m = verReg.match(line)
if m:
# print(m.group(0)) # KLAYOUT_VERSION="0.26.1"
# print(m.group(1)) # KLAYOUT_VERSION="
# print(m.group(2)) # 0.26.1
# print(m.group(3)) # "
version = m.group(2)
return version
return version
#------------------------------------------------------------------------------
#----------------------------------------------------------------------------------------
## To generate the contents of "Info.plist" file from a template
#
# @param[in] keydic dictionary of four key words ['exe', 'icon', 'bname', 'ver']
# @param[in] templfile template file ("macbuild/Resources/Info.plist.template")
#
# @return generated strings
#------------------------------------------------------------------------------
#----------------------------------------------------------------------------------------
def GenerateInfoPlist( keydic, templfile ):
val_exe = keydic['exe']
val_icon = keydic['icon']
val_bname = keydic['bname']
val_ver = keydic['ver']
val_exe = keydic['exe']
val_icon = keydic['icon']
val_bname = keydic['bname']
val_ver = keydic['ver']
try:
fd = open( templfile, "r" )
template = fd.read()
fd.close()
except Exception as e:
return "???"
try:
fd = open( templfile, "r" )
template = fd.read()
fd.close()
except Exception as e:
return "???"
t = string.Template(template)
s = t.substitute( EXECUTABLE = val_exe,
ICONFILE = val_icon,
BUNDLENAME = val_bname,
VERSION = val_ver)
return s
t = string.Template(template)
s = t.substitute( EXECUTABLE = val_exe,
ICONFILE = val_icon,
BUNDLENAME = val_bname,
VERSION = val_ver)
return s
#----------------
# End of File

278
macbuild/macQAT.py Executable file
View File

@ -0,0 +1,278 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#===============================================================================
# File: "macbuild/macQAT.py"
#
# The top Python script to run "ut_runner" after building KLayout
# (http://www.klayout.de/index.php) version 0.26.1 or later on different Apple
# ßMac OSX platforms.
#
# This script must be copied to a "*.macQAT/" directory to run.
#===============================================================================
from __future__ import print_function # to use print() of Python 3 in Python >= 2.7
import sys
import os
import datetime
from time import sleep
import six
import shutil
import glob
import platform
import optparse
import subprocess
#-------------------------------------------------------------------------------
## To set global variables including present directory and platform info.
#
#-------------------------------------------------------------------------------
def SetGlobals():
global ProjectDir # project directory where "ut_runner" exists
global RunnerUsage # True to print the usage of 'ut_runner'
global Run # True to run this script
global ContinueOnError # True to continue after an error
global TestsExcluded # list of tests to exclude
global Arguments # other arguments
global GitSHA1 # Git's short SHA1 value of the HEAD
global TimeStamp # time stamp
global WorkDir # work directory name
global LogFile # log file name
global Usage # string on usage
# auxiliary variables on platform
global System # 6-tuple from platform.uname()
global Node # - do -
global Release # - do -
global Version # - do -
global Machine # - do -
global Processor # - do -
global Bit # machine bit-size
Usage = "\n"
Usage += "----------------------------------------------------------------------------------------\n"
Usage += "<< Usage of 'macQAT.py' >>\n"
Usage += " for running 'ut_runner' after building KLayout.\n"
Usage += "\n"
Usage += "$ [python] ./macQAT.py \n"
Usage += " option & argument : descriptions | default value\n"
Usage += " -----------------------------------------------------------------+---------------\n"
Usage += " [-u|--usage] : print usage of 'ut_runner'and exit | disabled \n"
Usage += " | \n"
Usage += " <-r|--run> : run this script | disabled \n"
Usage += " [-s|--stop] : stop on error | disabled \n"
Usage += " [-x|--exclude <tests>] : exclude test(s) such as 'pymod,pya' | '' \n"
Usage += " [-a|--args <string>] : arguments other than '-x' and '-c' | '' \n"
Usage += " [-?|--?] : print this usage and exit | disabled \n"
Usage += "--------------------------------------------------------------------+-------------------\n"
ProjectDir = os.getcwd()
RunnerUsage = False
Run = False
ContinueOnError = True
TestsExcluded = list()
Arguments = ""
GitSHA1 = GetGitShortSHA1()
TimeStamp = GetTimeStamp()
WorkDir = "QATest_%s_%s__%s" % (GitSHA1, TimeStamp, os.path.basename(ProjectDir) )
LogFile = WorkDir + ".log"
(System, Node, Release, Version, Machine, Processor) = platform.uname()
#-------------------------------------------------------------------------------
## Get git's short SHA1 value of the HEAD
#
# @return SHA1 value string
#-------------------------------------------------------------------------------
def GetGitShortSHA1():
command = "git rev-parse --short HEAD 2>/dev/null"
sha1val = os.popen( command ).read().strip()
return sha1val
#-------------------------------------------------------------------------------
## Get the time stamp
#
# @return time stamp string
#-------------------------------------------------------------------------------
def GetTimeStamp():
ts = datetime.datetime.today()
return "%04d_%02d%02d_%02d%02d" % (ts.year, ts.month, ts.day, ts.hour, ts.minute)
#-------------------------------------------------------------------------------
## To parse the command line arguments
#
#-------------------------------------------------------------------------------
def ParseCommandLineArguments():
global Usage
global RunnerUsage
global Run
global ContinueOnError
global TestsExcluded
global Arguments
p = optparse.OptionParser( usage=Usage )
p.add_option( '-u', '--usage',
action='store_true',
dest='runner_usage',
default=False,
help="print usage of 'ut_runner' and exit (false)" )
p.add_option( '-r', '--run',
action='store_true',
dest='runme',
default=False,
help='run this script (false)' )
p.add_option( '-s', '--stop',
action='store_true',
dest='stop_on_error',
default=False,
help='stop on error (false)' )
p.add_option( '-x', '--exclude',
dest='exclude_tests',
help="exclude test(s) such as 'pymod,pya' ('')" )
p.add_option( '-a', '--args',
dest='arguments',
help="arguments other than '-x' and '-c' ('')" )
p.add_option( '-?', '--??',
action='store_true',
dest='checkusage',
default=False,
help='check usage (false)' )
p.set_defaults( runner_usage = False,
runme = False,
stop_on_error = False,
exclude_tests = "",
arguments = "",
checkusage = False )
opt, args = p.parse_args()
if opt.checkusage:
print(Usage)
quit()
RunnerUsage = opt.runner_usage
Run = opt.runme
ContinueOnError = not opt.stop_on_error
if not opt.exclude_tests == "":
TestsExcluded = [ item.strip() for item in opt.exclude_tests.split(',') ]
else:
TestsExcluded = []
Arguments = opt.arguments
#-------------------------------------------------------------------------------
## Hide/Show the private directory
#
#-------------------------------------------------------------------------------
def HidePrivateDir():
if os.path.isdir( "../private" ):
os.rename( "../private", "../private.stash" )
def ShowPrivateDir():
if os.path.isdir( "../private.stash" ):
os.rename( "../private.stash", "../private" )
#-------------------------------------------------------------------------------
## Export environment variables for "ut_runner"
#
#-------------------------------------------------------------------------------
def ExportEnvVariables():
global ProjectDir
global WorkDir
global MyEnviron # my environment variables; can be independently used later
# In older versions of subprocess module, 'env=None' argument is not provided
MyEnviron = os.environ.copy()
MyEnviron[ 'TESTSRC' ] = ".."
MyEnviron[ 'TESTTMP' ] = WorkDir
if System == "Darwin":
MyEnviron[ 'DYLD_LIBRARY_PATH' ] = "%s:%s/db_plugins" % (ProjectDir, ProjectDir)
for env in [ 'TESTSRC', 'TESTTMP', 'DYLD_LIBRARY_PATH' ]:
os.environ[env] = MyEnviron[env]
else:
MyEnviron[ 'LD_LIBRARY_PATH' ] = "%s:%s/db_plugins" % (ProjectDir, ProjectDir)
for env in [ 'TESTSRC', 'TESTTMP', 'LD_LIBRARY_PATH' ]:
os.environ[env] = MyEnviron[env]
#-------------------------------------------------------------------------------
## Run the tester
#
# @param[in] command command string to run
# @param[in] logfile log file name
#-------------------------------------------------------------------------------
def RunTester( command, logfile="" ):
if six.PY3:
proc = subprocess.Popen( command.split(), stdout=subprocess.PIPE, \
stderr=subprocess.STDOUT, \
universal_newlines=True )
else:
proc = subprocess.Popen( command.split(), stdout=subprocess.PIPE, \
stderr=subprocess.STDOUT )
if not logfile == "":
with proc.stdout, open( logfile, 'w' ) as file:
for line in proc.stdout:
sys.stdout.write(line)
file.write(line)
proc.wait()
else:
with proc.stdout:
for line in proc.stdout:
sys.stdout.write(line)
proc.wait()
#-------------------------------------------------------------------------------
# Main function
#-------------------------------------------------------------------------------
def Main():
#-------------------------------------------------------
# [1] Initialize
#-------------------------------------------------------
SetGlobals()
ParseCommandLineArguments()
ExportEnvVariables()
#-------------------------------------------------------
# [2] Print the runner's usage
#-------------------------------------------------------
if RunnerUsage:
command = './ut_runner --help-all'
RunTester( command )
quit()
#-------------------------------------------------------
# [3] Run the unit tester
#-------------------------------------------------------
if not Run:
print( "! pass <-r|--run> option to run" )
print(Usage)
quit()
command = './ut_runner'
if ContinueOnError:
command += " -c"
for item in TestsExcluded:
command += ' -x %s' % item
if not Arguments == "":
command += " %s" % Arguments
print( "" )
print( "### Dumping the log to <%s>" % LogFile )
print( "------------------------------------------------------------" )
print( " Git SHA1 = %s" % GitSHA1 )
print( " Time stamp = %s" % TimeStamp )
print( "------------------------------------------------------------" )
sleep( 1.0 )
HidePrivateDir()
RunTester( command, logfile=LogFile )
ShowPrivateDir()
#===================================================================================
if __name__ == "__main__":
Main()
#---------------
# End of file
#---------------

View File

@ -23,6 +23,7 @@ import platform
import optparse
import subprocess
import hashlib
import string
#-------------------------------------------------------------------------------
## To import global dictionaries of different modules and utility functions
@ -41,6 +42,7 @@ def SetGlobals():
global GenOSName # generic OS name
global Platform # platform
global PkgDir # the package directory where "klayout.app" exists
global UnsafePkg # flags whether to proceed to making "invalid" dmg
global OpClean # 'clean' operation
global OpMake # 'make' operation
global DefaultBundleName # the default bundle name 'klayout.app'
@ -87,6 +89,8 @@ def SetGlobals():
Usage += " : <-c|--clean> and <-m|--make> are mutually exclusive | \n"
Usage += " [-b|--bundle <name>] : forcibly use this bundle name in the DMG | '' \n"
Usage += " [-s|--serial <num>] : DMG serial number | 1 \n"
Usage += " <-u|--unsafe> : Ignores a few checks (use with caution) | disabled \n"
Usage += " <-t|--targetdmg> : Specify output .dmg filename | chosen by script \n"
Usage += " [-?|--?] : print this usage and exit | disabled \n"
Usage += "-------------------------------------------------------------------------------------+------------------\n"
@ -131,6 +135,7 @@ def SetGlobals():
sys.exit(1)
PkgDir = ""
UnsafePkg = False
OpClean = False
OpMake = False
DefaultBundleName = "klayout.app"
@ -179,8 +184,8 @@ def SetGlobals():
# The package directory name should look like:
# * ST-qt5MP.pkg.macos-Catalina-release-RsysPsys --- (1)
# * LW-qt5Ana3.pkg.macos-Catalina-release-Rana3Pana3
# * LW-qt5Brew.pkg.macos-Catalina-release-Rhb26Phb37
# * LW-qt5MP.pkg.macos-Catalina-release-Rmp26Pmp37
# * LW-qt5Brew.pkg.macos-Catalina-release-Rhb26Phb38
# * LW-qt5MP.pkg.macos-Catalina-release-Rmp26Pmp38
#
# Generated DMG will be, for example,
# (1) ---> ST-klayout-0.26.1-macOS-Catalina-1-qt5MP-RsysPsys.dmg
@ -189,6 +194,8 @@ def SetGlobals():
# on failure, -1
#------------------------------------------------------------------------------
def CheckPkgDirectory():
global PkgDir
global UnsafePkg
global Platform
global OpClean
global OpMake
@ -221,84 +228,87 @@ def CheckPkgDirectory():
#
# * ST-qt5MP.pkg.macos-Catalina-release-RsysPsys
# * LW-qt5Ana3.pkg.macos-Catalina-release-Rana3Pana3
# * LW-qt5Brew.pkg.macos-Catalina-release-Rhb27Phb37
# * HW-qt5Brew.pkg.macos-Catalina-release-RsysPhb37
# * EX-qt5MP.pkg.macos-Catalina-release-Rmp26Pmp37
# * LW-qt5Brew.pkg.macos-Catalina-release-Rhb27Phb38
# * HW-qt5Brew.pkg.macos-Catalina-release-RsysPhb38
# * EX-qt5MP.pkg.macos-Catalina-release-Rmp26Pmp38
#-----------------------------------------------------------------------------
patQRP = u'(ST|LW|HW|EX)([-])(qt5[0-9A-Za-z]+)([.]pkg[.])([A-Za-z]+[-][A-Za-z]+[-]release[-])([0-9A-Za-z]+)'
regQRP = re.compile(patQRP)
if not regQRP.match(PkgDir):
print( "! Cannot identify (Qt, Ruby, Python) from the package directory name" )
print( "" )
return -1
if UnsafePkg:
print( "! Ignoring..." )
else:
print( "" )
return -1
else:
pkgdirComponents = regQRP.match(PkgDir).groups()
PackagePrefix = pkgdirComponents[0]
QtIdentification = pkgdirComponents[2]
RubyPythonID = pkgdirComponents[5]
#-----------------------------------------------------------------------------
# [3] Check if the "LatestOS" with MacPorts / Homebrew / Anaconda3
#-----------------------------------------------------------------------------
LatestOSMacPorts = Platform == LatestOS
LatestOSMacPorts &= PackagePrefix == "LW"
LatestOSMacPorts &= QtIdentification == "qt5MP"
LatestOSMacPorts &= RubyPythonID == "Rmp26Pmp37"
#-----------------------------------------------------------------------------
# [3] Check if the "LatestOS" with MacPorts / Homebrew / Anaconda3
#-----------------------------------------------------------------------------
LatestOSMacPorts = Platform == LatestOS
LatestOSMacPorts &= PackagePrefix == "LW"
LatestOSMacPorts &= QtIdentification == "qt5MP"
LatestOSMacPorts &= RubyPythonID == "Rmp26Pmp38"
LatestOSHomebrew = Platform == LatestOS
LatestOSHomebrew &= PackagePrefix == "LW"
LatestOSHomebrew &= QtIdentification == "qt5Brew"
LatestOSHomebrew &= RubyPythonID == "Rhb27Phb37"
LatestOSHomebrew = Platform == LatestOS
LatestOSHomebrew &= PackagePrefix == "LW"
LatestOSHomebrew &= QtIdentification == "qt5Brew"
LatestOSHomebrew &= RubyPythonID == "Rhb27Phb38"
LatestOSAnaconda3 = Platform == LatestOS
LatestOSAnaconda3 &= PackagePrefix == "LW"
LatestOSAnaconda3 &= QtIdentification == "qt5Ana3"
LatestOSAnaconda3 &= RubyPythonID == "Rana3Pana3"
LatestOSAnaconda3 = Platform == LatestOS
LatestOSAnaconda3 &= PackagePrefix == "LW"
LatestOSAnaconda3 &= QtIdentification == "qt5Ana3"
LatestOSAnaconda3 &= RubyPythonID == "Rana3Pana3"
if LatestOSMacPorts:
mydic = DicLightWeight["ports"]
srcDir = PkgDir + "/" + mydic["src"]
desDir = PkgDir + "/" + mydic["des"]
if OpMake:
with zipfile.ZipFile( mydic["zip"], 'r' ) as zip_ref:
zip_ref.extractall(PkgDir)
os.rename( srcDir, desDir )
if OpClean:
if os.path.isdir(srcDir):
shutil.rmtree(srcDir)
if os.path.isdir(desDir):
shutil.rmtree(desDir)
Item3AppleScript = mydic["item3"]
if LatestOSMacPorts:
mydic = DicLightWeight["ports"]
srcDir = PkgDir + "/" + mydic["src"]
desDir = PkgDir + "/" + mydic["des"]
if OpMake:
with zipfile.ZipFile( mydic["zip"], 'r' ) as zip_ref:
zip_ref.extractall(PkgDir)
os.rename( srcDir, desDir )
if OpClean:
if os.path.isdir(srcDir):
shutil.rmtree(srcDir)
if os.path.isdir(desDir):
shutil.rmtree(desDir)
Item3AppleScript = mydic["item3"]
if LatestOSHomebrew:
mydic = DicLightWeight["brew"]
srcDir = PkgDir + "/" + mydic["src"]
desDir = PkgDir + "/" + mydic["des"]
if OpMake:
with zipfile.ZipFile( mydic["zip"], 'r' ) as zip_ref:
zip_ref.extractall(PkgDir)
os.rename( srcDir, desDir )
if OpClean:
if os.path.isdir(srcDir):
shutil.rmtree(srcDir)
if os.path.isdir(desDir):
shutil.rmtree(desDir)
Item3AppleScript = mydic["item3"]
if LatestOSHomebrew:
mydic = DicLightWeight["brew"]
srcDir = PkgDir + "/" + mydic["src"]
desDir = PkgDir + "/" + mydic["des"]
if OpMake:
with zipfile.ZipFile( mydic["zip"], 'r' ) as zip_ref:
zip_ref.extractall(PkgDir)
os.rename( srcDir, desDir )
if OpClean:
if os.path.isdir(srcDir):
shutil.rmtree(srcDir)
if os.path.isdir(desDir):
shutil.rmtree(desDir)
Item3AppleScript = mydic["item3"]
if LatestOSAnaconda3:
mydic = DicLightWeight["ana3"]
srcDir = PkgDir + "/" + mydic["src"]
desDir = PkgDir + "/" + mydic["des"]
if OpMake:
with zipfile.ZipFile( mydic["zip"], 'r' ) as zip_ref:
zip_ref.extractall(PkgDir)
os.rename( srcDir, desDir )
if OpClean:
if os.path.isdir(srcDir):
shutil.rmtree(srcDir)
if os.path.isdir(desDir):
shutil.rmtree(desDir)
Item3AppleScript = mydic["item3"]
if LatestOSAnaconda3:
mydic = DicLightWeight["ana3"]
srcDir = PkgDir + "/" + mydic["src"]
desDir = PkgDir + "/" + mydic["des"]
if OpMake:
with zipfile.ZipFile( mydic["zip"], 'r' ) as zip_ref:
zip_ref.extractall(PkgDir)
os.rename( srcDir, desDir )
if OpClean:
if os.path.isdir(srcDir):
shutil.rmtree(srcDir)
if os.path.isdir(desDir):
shutil.rmtree(desDir)
Item3AppleScript = mydic["item3"]
#------------------------------------------------------
# [4] Check the presence of the default bundle
@ -337,6 +347,7 @@ def ParseCommandLineArguments():
global OpMake
global BundleName
global DMGSerialNum
global UnsafePkg
global PackagePrefix
global QtIdentification
global RubyPythonID
@ -369,6 +380,16 @@ def ParseCommandLineArguments():
dest='dmg_serial',
help="DMG serial number" )
p.add_option( '-t', '--targetdmg',
dest='target_dmg',
help="output DMG filename" )
p.add_option( '-z', '--unsafe',
action='store_true',
dest='unsafe',
default=False,
help="If set, do not check whether pkg folder is empty" )
p.add_option( '-?', '--??',
action='store_true',
dest='checkusage',
@ -379,7 +400,9 @@ def ParseCommandLineArguments():
operation_clean = False,
operation_make = False,
bundle_name = "",
target_dmg = "",
dmg_serial = "1",
unsafe = False,
checkusage = False )
#-----------------------------------------------------------
@ -394,6 +417,7 @@ def ParseCommandLineArguments():
OpClean = opt.operation_clean
OpMake = opt.operation_make
DMGSerialNum = int(opt.dmg_serial)
UnsafePkg = opt.unsafe
if not opt.bundle_name == "":
base, ext = os.path.splitext( os.path.basename(opt.bundle_name) )
@ -410,10 +434,13 @@ def ParseCommandLineArguments():
# [2] Check the PKG directory to set QtIdentification, RubyPythonID, and BundleName
#------------------------------------------------------------------------------------
OccupiedDS = CheckPkgDirectory()
if not 0 < OccupiedDS:
if not 0 < OccupiedDS and not UnsafePkg:
print( "! Failed to check the PKG directory" )
print( "" )
quit()
if opt.target_dmg != "":
TargetDMG = opt.target_dmg
else:
TargetDMG = "%s-klayout-%s-%s-%s-%d-%s-%s.dmg" \
% (PackagePrefix, KLVersion, GenOSName, Platform, DMGSerialNum, QtIdentification, RubyPythonID)
@ -663,7 +690,8 @@ def CleanUp(msg=""):
os.chdir(ProjectDir)
dmgs = glob.glob( "*.dmg*" )
for item in dmgs:
os.system( "rm -Rf %s" % item )
print("Removing %s" % item)
os.system( "rm -Rf -- \"%s\"" % item )
#----------------------------------------------------
# [3] Clean up AppleScript if any

View File

@ -50,6 +50,38 @@ PropertiesPage::PropertiesPage (ant::Service *rulers, db::Manager *manager, QWid
p2_to_layout->setEnabled (! readonly());
both_to_layout->setEnabled (! readonly());
if (! readonly ()) {
connect (fmt_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (fmt_x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (fmt_y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (x1, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (x2, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (y1, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (y2, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (style_cb, SIGNAL (activated (int)), this, SIGNAL (edited ()));
connect (outline_cb, SIGNAL (activated (int)), this, SIGNAL (edited ()));
connect (main_position, SIGNAL (activated (int)), this, SIGNAL (edited ()));
connect (main_xalign, SIGNAL (activated (int)), this, SIGNAL (edited ()));
connect (main_yalign, SIGNAL (activated (int)), this, SIGNAL (edited ()));
connect (xlabel_xalign, SIGNAL (activated (int)), this, SIGNAL (edited ()));
connect (xlabel_yalign, SIGNAL (activated (int)), this, SIGNAL (edited ()));
connect (ylabel_xalign, SIGNAL (activated (int)), this, SIGNAL (edited ()));
connect (ylabel_yalign, SIGNAL (activated (int)), this, SIGNAL (edited ()));
} else {
fmt_le->setReadOnly (true);
fmt_x_le->setReadOnly (true);
fmt_y_le->setReadOnly (true);
x1->setReadOnly (true);
x2->setReadOnly (true);
y1->setReadOnly (true);
y2->setReadOnly (true);
}
lay::activate_help_links (help_label);
mp_rulers->clear_highlights ();
@ -91,7 +123,53 @@ PropertiesPage::swap_points_clicked ()
y2->setText (ty2);
db::Transaction t (manager (), tl::to_string (QObject::tr ("Swap ruler points")));
apply ();
emit edited ();
}
void
PropertiesPage::get_points (db::DPoint &p1, db::DPoint &p2)
{
double dx1 = 0.0, dy1 = 0.0, dx2 = 0.0, dy2 = 0.0;
bool has_error = false;
try {
tl::from_string (tl::to_string (x1->text ()), dx1);
lay::indicate_error (x1, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (x1, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (x2->text ()), dx2);
lay::indicate_error (x2, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (x2, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (y1->text ()), dy1);
lay::indicate_error (y1, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (y1, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (y2->text ()), dy2);
lay::indicate_error (y2, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (y2, &ex);
has_error = true;
}
if (has_error) {
throw tl::Exception (tl::to_string (tr ("At least one value is invalid - see highlighted entry fields")));
}
p1 = db::DPoint (dx1, dy1);
p2 = db::DPoint (dx2, dy2);
}
void
@ -104,13 +182,8 @@ PropertiesPage::snap_to_layout_clicked ()
ant::Service *service = dynamic_cast<ant::Service *> (editable ());
tl_assert (service != 0);
double dx1 = 0.0, dy1 = 0.0, dx2 = 0.0, dy2 = 0.0;
tl::from_string (tl::to_string (x1->text ()), dx1);
tl::from_string (tl::to_string (x2->text ()), dx2);
tl::from_string (tl::to_string (y1->text ()), dy1);
tl::from_string (tl::to_string (y2->text ()), dy2);
db::DPoint p1 (dx1, dy1), p2 (dx2, dy2);
db::DPoint p1, p2;
get_points (p1, p2);
ant::Object r = current ();
@ -137,11 +210,11 @@ PropertiesPage::snap_to_layout_clicked ()
while (snap_range < max_range) {
std::pair<bool, db::DPoint> pp = lay::obj_snap (service->view (), snap_p1 ? p2 : p1, snap_p1 ? p1 : p2, g, ac, snap_range);
if (pp.first) {
lay::PointSnapToObjectResult pp = lay::obj_snap (service->view (), snap_p1 ? p2 : p1, snap_p1 ? p1 : p2, g, ac, snap_range);
if (pp.object_snap != lay::PointSnapToObjectResult::NoObject) {
QString xs = tl::to_qstring (tl::micron_to_string (pp.second.x ()));
QString ys = tl::to_qstring (tl::micron_to_string (pp.second.y ()));
QString xs = tl::to_qstring (tl::micron_to_string (pp.snapped_point.x ()));
QString ys = tl::to_qstring (tl::micron_to_string (pp.snapped_point.y ()));
if (sender () == p1_to_layout) {
x1->setText (xs);
@ -152,7 +225,7 @@ PropertiesPage::snap_to_layout_clicked ()
}
db::Transaction t (manager (), tl::to_string (snap_p1 ? QObject::tr ("Snap first ruler point") : QObject::tr ("Snap second ruler point")));
apply ();
emit edited ();
break;
@ -168,16 +241,16 @@ PropertiesPage::snap_to_layout_clicked ()
double snap_range = service->widget ()->mouse_event_trans ().inverted ().ctrans (service->snap_range ());
snap_range *= 0.5;
std::pair<bool, db::DEdge> ee = lay::obj_snap2 (service->view (), p1, p2, g, ac, snap_range, snap_range * 1000.0);
if (ee.first) {
lay::TwoPointSnapToObjectResult ee = lay::obj_snap2 (service->view (), p1, p2, g, ac, snap_range, snap_range * 1000.0);
if (ee.any) {
x1->setText (tl::to_qstring (tl::micron_to_string (ee.second.p1 ().x ())));
y1->setText (tl::to_qstring (tl::micron_to_string (ee.second.p1 ().y ())));
x2->setText (tl::to_qstring (tl::micron_to_string (ee.second.p2 ().x ())));
y2->setText (tl::to_qstring (tl::micron_to_string (ee.second.p2 ().y ())));
x1->setText (tl::to_qstring (tl::micron_to_string (ee.first.x ())));
y1->setText (tl::to_qstring (tl::micron_to_string (ee.first.y ())));
x2->setText (tl::to_qstring (tl::micron_to_string (ee.second.x ())));
y2->setText (tl::to_qstring (tl::micron_to_string (ee.second.y ())));
db::Transaction t (manager (), tl::to_string (QObject::tr ("Snap both ruler points")));
apply ();
emit edited ();
}
@ -268,22 +341,9 @@ PropertiesPage::readonly ()
void
PropertiesPage::apply ()
{
double dx1 = current ().p1 ().x (), dy1 = current ().p1 ().y ();
double dx2 = current ().p2 ().x (), dy2 = current ().p2 ().y ();
// only adjust the values if the text has changed
if (tl::to_qstring (tl::micron_to_string (dx1)) != x1->text ()) {
tl::from_string (tl::to_string (x1->text ()), dx1);
}
if (tl::to_qstring (tl::micron_to_string (dx2)) != x2->text ()) {
tl::from_string (tl::to_string (x2->text ()), dx2);
}
if (tl::to_qstring (tl::micron_to_string (dy1)) != y1->text ()) {
tl::from_string (tl::to_string (y1->text ()), dy1);
}
if (tl::to_qstring (tl::micron_to_string (dy2)) != y2->text ()) {
tl::from_string (tl::to_string (y2->text ()), dy2);
}
db::DPoint p1, p2;
get_points (p1, p2);
std::string fmt = tl::to_string (fmt_le->text ());
std::string fmt_x = tl::to_string (fmt_x_le->text ());
@ -291,7 +351,7 @@ PropertiesPage::apply ()
Object::style_type style = Object::style_type (style_cb->currentIndex ());
Object::outline_type outline = Object::outline_type (outline_cb->currentIndex ());
ant::Object ruler (db::DPoint (dx1, dy1), db::DPoint (dx2, dy2), current ().id (), fmt_x, fmt_y, fmt, style, outline, current ().snap (), current ().angle_constraint ());
ant::Object ruler (p1, p2, current ().id (), fmt_x, fmt_y, fmt, style, outline, current ().snap (), current ().angle_constraint ());
ruler.set_main_position (Object::position_type (main_position->currentIndex ()));
ruler.set_main_xalign (Object::alignment_type (main_xalign->currentIndex ()));

View File

@ -64,6 +64,7 @@ private:
bool m_enable_cb_callback;
const ant::Object &current () const;
void get_points(db::DPoint &p1, db::DPoint &p2);
};
}

View File

@ -706,9 +706,7 @@ View::render (const lay::Viewport &vp, lay::ViewObjectCanvas &canvas)
// ant::Service implementation
Service::Service (db::Manager *manager, lay::LayoutView *view)
: lay::ViewService (view->view_object_widget ()),
lay::Editable (view),
lay::Plugin (view),
: lay::EditorServiceBase (view),
lay::Drawing (1/*number of planes*/, view->drawings ()),
db::Object (manager),
m_halo (true),
@ -801,7 +799,7 @@ Service::configure (const std::string &name, const std::string &value)
m_current_template = n;
} else {
taken = false;
lay::EditorServiceBase::configure (name, value);
}
return taken;
@ -1434,14 +1432,14 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio
double snap_range = widget ()->mouse_event_trans ().inverted ().ctrans (m_snap_range);
snap_range *= 0.5;
std::pair<bool, db::DEdge> ee = lay::obj_snap2 (mp_view, p, g, ac, snap_range, snap_range * 1000.0);
if (ee.first) {
lay::TwoPointSnapToObjectResult ee = lay::obj_snap2 (mp_view, p, g, ac, snap_range, snap_range * 1000.0);
if (ee.any) {
// begin the transaction
tl_assert (! manager ()->transacting ());
manager ()->transaction (tl::to_string (QObject::tr ("Create ruler")));
m_current = ant::Object (ee.second.p1 (), ee.second.p2 (), 0, tpl);
m_current = ant::Object (ee.first, ee.second, 0, tpl);
show_message ();
insert_ruler (m_current, true);
@ -1505,17 +1503,31 @@ Service::create_measure_ruler (const db::DPoint &pt, lay::angle_constraint_type
ant::Template tpl;
std::pair<bool, db::DEdge> ee = lay::obj_snap2 (mp_view, pt, db::DVector (), ac, snap_range, snap_range * 1000.0);
if (ee.first) {
return ant::Object (ee.second.p1 (), ee.second.p2 (), 0, tpl);
lay::TwoPointSnapToObjectResult ee = lay::obj_snap2 (mp_view, pt, db::DVector (), ac, snap_range, snap_range * 1000.0);
if (ee.any) {
return ant::Object (ee.first, ee.second, 0, tpl);
} else {
return ant::Object (pt, pt, 0, tpl);
}
}
bool
bool
Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio)
{
if (prio) {
lay::PointSnapToObjectResult snap_details;
if (m_drawing) {
snap_details = snap2_details (m_p1, p, mp_active_ruler->ruler (), ac_from_buttons (buttons));
} else {
const ant::Template &tpl = current_template ();
snap_details = snap1_details (p, m_obj_snap && tpl.snap ());
}
mouse_cursor_from_snap_details (snap_details);
}
if (m_drawing && prio) {
set_cursor (lay::Cursor::cross);
@ -1532,12 +1544,14 @@ Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio)
void
Service::deactivated ()
{
lay::EditorServiceBase::deactivated ();
drag_cancel ();
clear_transient_selection ();
}
std::pair<bool, db::DPoint>
Service::snap1 (const db::DPoint &p, bool obj_snap)
lay::PointSnapToObjectResult
Service::snap1_details (const db::DPoint &p, bool obj_snap)
{
db::DVector g;
if (m_grid_snap) {
@ -1548,9 +1562,16 @@ Service::snap1 (const db::DPoint &p, bool obj_snap)
return lay::obj_snap (obj_snap ? mp_view : 0, p, g, snap_range);
}
std::pair<bool, db::DPoint>
Service::snap1 (const db::DPoint &p, bool obj_snap)
{
lay::PointSnapToObjectResult res = snap1_details (p, obj_snap);
return std::make_pair (res.object_snap != lay::PointSnapToObjectResult::NoObject, res.snapped_point);
}
std::pair <bool, db::DPoint>
Service::snap2 (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac)
lay::PointSnapToObjectResult
Service::snap2_details (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac)
{
db::DVector g;
if (m_grid_snap) {
@ -1563,6 +1584,13 @@ Service::snap2 (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *o
return lay::obj_snap (m_obj_snap && obj->snap () ? mp_view : 0, p1, p2, g, snap_mode, snap_range);
}
std::pair <bool, db::DPoint>
Service::snap2 (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac)
{
lay::PointSnapToObjectResult res = snap2_details (p1, p2, obj, ac);
return std::make_pair (res.object_snap != lay::PointSnapToObjectResult::NoObject, res.snapped_point);
}
struct RulerIdComp
{

View File

@ -27,9 +27,7 @@
#include "antCommon.h"
#include "layViewObject.h"
#include "layEditable.h"
#include "layPlugin.h"
#include "layEditorServiceBase.h"
#include "layDrawing.h"
#include "laySnap.h"
#include "layAnnotationShapes.h"
@ -180,9 +178,7 @@ private:
// -------------------------------------------------------------
class ANT_PUBLIC Service
: public lay::ViewService,
public lay::Editable,
public lay::Plugin,
: public lay::EditorServiceBase,
public lay::Drawing,
public db::Object
{
@ -529,8 +525,10 @@ private:
unsigned int m_current_template;
std::pair<bool, db::DPoint> snap1 (const db::DPoint &p, bool obj_snap);
lay::PointSnapToObjectResult snap1_details (const db::DPoint &p, bool obj_snap);
std::pair<bool, db::DPoint> snap2 (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac);
lay::PointSnapToObjectResult snap2_details (const db::DPoint &p1, const db::DPoint &p2, const ant::Object *obj, lay::angle_constraint_type ac);
const ant::Template &current_template () const;
void show_message ();

View File

@ -190,6 +190,7 @@ SOURCES = \
gsiDeclDbNetlistCrossReference.cc \
gsiDeclDbLayoutVsSchematic.cc \
dbNetlistObject.cc \
dbD25TechnologyComponent.cc \
gsiDeclDbTexts.cc \
dbTexts.cc \
dbDeepTexts.cc \
@ -354,6 +355,7 @@ HEADERS = \
dbLayoutVsSchematicFormatDefs.h \
dbLayoutVsSchematic.h \
dbNetlistObject.h \
dbD25TechnologyComponent.h \
dbTexts.h \
dbDeepTexts.h \
dbAsIfFlatTexts.h \

View File

@ -506,7 +506,7 @@ Cell::collect_caller_cells (std::set<cell_index_type> &callers, const std::set<c
{
if (levels != 0) {
for (parent_cell_iterator cc = begin_parent_cells (); cc != end_parent_cells (); ++cc) {
if (cone.find (*cc) != cone.end () && callers.find (*cc) == callers.end ()) {
if (cone.find (*cc) != cone.end () && callers.find (*cc) == callers.end () && mp_layout->is_valid_cell_index (*cc)) {
callers.insert (*cc);
mp_layout->cell (*cc).collect_caller_cells (callers, levels < 0 ? levels : levels - 1);
}
@ -519,7 +519,7 @@ Cell::collect_caller_cells (std::set<cell_index_type> &callers, int levels) cons
{
if (levels != 0) {
for (parent_cell_iterator cc = begin_parent_cells (); cc != end_parent_cells (); ++cc) {
if (callers.find (*cc) == callers.end ()) {
if (callers.find (*cc) == callers.end () && mp_layout->is_valid_cell_index (*cc)) {
callers.insert (*cc);
mp_layout->cell (*cc).collect_caller_cells (callers, levels < 0 ? levels : levels - 1);
}
@ -538,7 +538,7 @@ Cell::collect_called_cells (std::set<cell_index_type> &called, int levels) const
{
if (levels != 0) {
for (child_cell_iterator cc = begin_child_cells (); ! cc.at_end (); ++cc) {
if (called.find (*cc) == called.end ()) {
if (called.find (*cc) == called.end () && mp_layout->is_valid_cell_index (*cc)) {
called.insert (*cc);
mp_layout->cell (*cc).collect_called_cells (called, levels < 0 ? levels : levels - 1);
}

View File

@ -29,6 +29,361 @@
namespace db
{
// ---------------------------------------------------------------
// Common reader implementation
static const size_t null_id = std::numeric_limits<size_t>::max ();
CommonReader::CommonReader ()
: m_cc_resolution (AddToCell)
{
// .. nothing yet ..
}
db::cell_index_type
CommonReader::make_cell (db::Layout &layout, const std::string &cn)
{
tl_assert (! cn.empty ());
std::map<std::string, std::pair<size_t, db::cell_index_type> >::iterator iname = m_name_map.find (cn);
if (iname != m_name_map.end ()) {
db::Cell &cell = layout.cell (iname->second.second);
if (! cell.is_ghost_cell ()) {
common_reader_error (tl::sprintf (tl::to_string (tr ("A cell with name %s already exists")), cn));
}
cell.set_ghost_cell (false);
return cell.cell_index ();
} else {
db::cell_index_type ci = layout.add_anonymous_cell ();
m_name_map [cn] = std::make_pair (null_id, ci);
return ci;
}
}
bool
CommonReader::has_cell (const std::string &cn) const
{
return m_name_map.find (cn) != m_name_map.end ();
}
std::pair<bool, db::cell_index_type>
CommonReader::cell_by_name (const std::string &cn) const
{
std::map<std::string, std::pair<size_t, db::cell_index_type> >::const_iterator iname = m_name_map.find (cn);
if (iname != m_name_map.end ()) {
return std::make_pair (true, iname->second.second);
} else {
return std::make_pair (false, size_t (0));
}
}
db::cell_index_type
CommonReader::make_cell (db::Layout &layout, size_t id)
{
tl_assert (id != null_id);
std::map<size_t, std::pair<std::string, db::cell_index_type> >::iterator iid = m_id_map.find (id);
if (iid != m_id_map.end ()) {
db::Cell &cell = layout.cell (iid->second.second);
if (! cell.is_ghost_cell ()) {
common_reader_error (tl::sprintf (tl::to_string (tr ("A cell with ID %ld already exists")), id));
}
cell.set_ghost_cell (false);
return cell.cell_index ();
} else {
db::cell_index_type ci = layout.add_anonymous_cell ();
m_id_map [id] = std::make_pair (std::string (), ci);
return ci;
}
}
bool
CommonReader::has_cell (size_t id) const
{
return m_id_map.find (id) != m_id_map.end ();
}
std::pair<bool, db::cell_index_type>
CommonReader::cell_by_id (size_t id) const
{
std::map<size_t, std::pair<std::string, db::cell_index_type> >::const_iterator iid = m_id_map.find (id);
if (iid != m_id_map.end ()) {
return std::make_pair (true, iid->second.second);
} else {
return std::make_pair (false, size_t (0));
}
}
const std::string &
CommonReader::name_for_id (size_t id) const
{
std::map<size_t, std::string>::const_iterator n = m_name_for_id.find (id);
if (n != m_name_for_id.end ()) {
return n->second;
} else {
static std::string empty;
return empty;
}
}
void
CommonReader::rename_cell (db::Layout &layout, size_t id, const std::string &cn)
{
m_name_for_id.insert (std::make_pair (id, cn));
std::map<size_t, std::pair<std::string, db::cell_index_type> >::iterator iid = m_id_map.find (id);
std::map<std::string, std::pair<size_t, db::cell_index_type> >::iterator iname = m_name_map.find (cn);
if (iid != m_id_map.end () && iname != m_name_map.end ()) {
if (! iid->second.first.empty () && iid->second.first != cn) {
common_reader_error (tl::sprintf (tl::to_string (tr ("Cell named %s with ID %ld was already given name %s")), cn, id, iid->second.first));
}
if (iname->second.second != iid->second.second) {
// Both cells already exist and are not identical: merge ID-declared cell into the name-declared one
layout.force_update ();
merge_cell (layout, iname->second.second, iid->second.second);
iid->second.second = iname->second.second;
}
iid->second.first = cn;
iname->second.first = id;
} else if (iid != m_id_map.end ()) {
m_name_map [cn] = std::make_pair (id, iid->second.second);
iid->second.first = cn;
} else if (iname != m_name_map.end ()) {
m_id_map [id] = std::make_pair (cn, iname->second.second);
iname->second.first = id;
} else {
db::cell_index_type ci = layout.add_anonymous_cell ();
layout.cell (ci).set_ghost_cell (true);
m_id_map [id] = std::make_pair (cn, ci);
m_name_map [cn] = std::make_pair (id, ci);
}
}
db::cell_index_type
CommonReader::cell_for_instance (db::Layout &layout, size_t id)
{
tl_assert (id != null_id);
std::map<size_t, std::pair<std::string, db::cell_index_type> >::iterator iid = m_id_map.find (id);
if (iid != m_id_map.end ()) {
return iid->second.second;
} else {
db::cell_index_type ci = layout.add_anonymous_cell ();
layout.cell (ci).set_ghost_cell (true);
m_id_map [id] = std::make_pair (std::string (), ci);
return ci;
}
}
db::cell_index_type
CommonReader::cell_for_instance (db::Layout &layout, const std::string &cn)
{
tl_assert (! cn.empty ());
std::map<std::string, std::pair<size_t, db::cell_index_type> >::iterator iname = m_name_map.find (cn);
if (iname != m_name_map.end ()) {
return iname->second.second;
} else {
db::cell_index_type ci = layout.add_anonymous_cell ();
layout.cell (ci).set_ghost_cell (true);
m_name_map [cn] = std::make_pair (null_id, ci);
return ci;
}
}
void
CommonReader::merge_cell (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index) const
{
const db::Cell &src_cell = layout.cell (src_cell_index);
db::Cell &target_cell = layout.cell (target_cell_index);
// copy over the instances
for (db::Cell::const_iterator i = src_cell.begin (); ! i.at_end (); ++i) {
// NOTE: cell indexed may be invalid because we delete subcells without update()
if (layout.is_valid_cell_index (i->cell_index ())) {
target_cell.insert (*i);
}
}
merge_cell_without_instances (layout, target_cell_index, src_cell_index);
}
void
CommonReader::merge_cell_without_instances (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index) const
{
const db::Cell &src_cell = layout.cell (src_cell_index);
db::Cell &target_cell = layout.cell (target_cell_index);
// copy over the shapes
for (unsigned int l = 0; l < layout.layers (); ++l) {
if (layout.is_valid_layer (l) && ! src_cell.shapes (l).empty ()) {
target_cell.shapes (l).insert (src_cell.shapes (l));
}
}
// replace all instances of the new cell with the original one
std::vector<std::pair<db::cell_index_type, db::Instance> > parents;
for (db::Cell::parent_inst_iterator pi = src_cell.begin_parent_insts (); ! pi.at_end (); ++pi) {
parents.push_back (std::make_pair (pi->parent_cell_index (), pi->child_inst ()));
}
for (std::vector<std::pair<db::cell_index_type, db::Instance> >::const_iterator p = parents.begin (); p != parents.end (); ++p) {
db::CellInstArray ia = p->second.cell_inst ();
ia.object ().cell_index (target_cell.cell_index ());
layout.cell (p->first).replace (p->second, ia);
}
// finally delete the new cell
layout.delete_cell (src_cell.cell_index ());
}
void
CommonReader::finish (db::Layout &layout)
{
bool any_missing = false;
for (std::map<size_t, std::pair<std::string, db::cell_index_type> >::const_iterator i = m_id_map.begin (); i != m_id_map.end (); ++i) {
if (i->second.first.empty ()) {
common_reader_warn (tl::sprintf (tl::to_string (tr ("No cellname defined for cell name id %ld")), i->first));
any_missing = true;
}
}
if (any_missing) {
common_reader_error (tl::to_string (tr ("Some cell IDs don't have a name (see previous warnings)")));
}
// check if we need to resolve conflicts
bool has_conflict = false;
for (std::map<std::string, std::pair<size_t, db::cell_index_type> >::const_iterator i = m_name_map.begin (); i != m_name_map.end () && ! has_conflict; ++i) {
has_conflict = layout.cell_by_name (i->first.c_str ()).first;
}
if (! has_conflict) {
// no conflict - plain rename
for (std::map<std::string, std::pair<size_t, db::cell_index_type> >::const_iterator i = m_name_map.begin (); i != m_name_map.end () && ! has_conflict; ++i) {
layout.rename_cell (i->second.second, i->first.c_str ());
}
} else {
// elaborate conflict resolution
layout.force_update ();
std::map<db::cell_index_type, std::string> new_cells;
for (std::map<std::string, std::pair<size_t, db::cell_index_type> >::const_iterator i = m_name_map.begin (); i != m_name_map.end (); ++i) {
new_cells.insert (std::make_pair (i->second.second, i->first));
}
std::vector<std::pair<db::cell_index_type, db::cell_index_type> > cells_with_conflict;
// First treat all the cells without conflict
for (db::Layout::bottom_up_iterator bu = layout.begin_bottom_up (); bu != layout.end_bottom_up (); ++bu) {
db::cell_index_type ci_new = *bu;
std::map<db::cell_index_type, std::string>::const_iterator i = new_cells.find (ci_new);
if (i == new_cells.end ()) {
// not a new cell
continue;
}
std::pair<bool, db::cell_index_type> c2n = layout.cell_by_name (i->second.c_str ());
db::cell_index_type ci_org = c2n.second;
// NOTE: proxy cells are never resolved. "RenameCell" is a plain and simple case.
if (c2n.first && m_cc_resolution != RenameCell && ! layout.cell (ci_org).is_proxy ()) {
cells_with_conflict.push_back (std::make_pair (ci_new, ci_org));
} else {
layout.rename_cell (ci_new, layout.uniquify_cell_name (i->second.c_str ()).c_str ());
}
}
// Then treat all the cells with conflict
for (std::vector<std::pair<db::cell_index_type, db::cell_index_type> >::const_iterator cc = cells_with_conflict.begin (); cc != cells_with_conflict.end (); ++cc) {
db::cell_index_type ci_new = cc->first;
db::cell_index_type ci_org = cc->second;
// we have a cell conflict
if (m_cc_resolution == OverwriteCell && ! layout.cell (ci_new).is_ghost_cell ()) {
if (! layout.cell (ci_org).begin ().at_end ()) {
// NOTE: because prune_subcells needs the parents for sub cells and we are going do delete
// the current cell, we cannot save the "update()" just by traversing bottom-up.
layout.force_update ();
layout.prune_subcells (ci_org);
}
layout.cell (ci_org).clear_shapes ();
merge_cell (layout, ci_org, ci_new);
} else if (m_cc_resolution == SkipNewCell && ! layout.cell (ci_org).is_ghost_cell ()) {
layout.prune_subcells (ci_new);
layout.cell (ci_new).clear_shapes ();
// NOTE: ignore instances -> this saves us a layout update
merge_cell_without_instances (layout, ci_org, ci_new);
} else {
merge_cell (layout, ci_org, ci_new);
}
}
}
}
// ---------------------------------------------------------------
// Common format declaration

View File

@ -31,6 +31,128 @@
namespace db
{
/**
* @brief A common reader base for GDS2 and OASIS providing common services for both readers
*/
class DB_PUBLIC CommonReader
: public ReaderBase
{
public:
/**
* @brief The CellConflictResolution enum
*/
enum CellConflictResolution
{
AddToCell = 0,
OverwriteCell = 1,
SkipNewCell = 2,
RenameCell = 3
};
/**
* @brief Constructor
*/
CommonReader ();
/**
* @brief Sets the cell name conflict resolution mode
*/
void set_cell_conflict_resolution (CellConflictResolution cc_resolution)
{
m_cc_resolution = cc_resolution;
}
/**
* @brief Sets the cell name conflict resolution mode
*/
CellConflictResolution cell_conflict_resolution () const
{
return m_cc_resolution;
}
/**
* @brief Make a cell from a name
*/
db::cell_index_type make_cell (db::Layout &layout, const std::string &cn);
/**
* @brief Returns true, if there is a cell with the given name already
*/
bool has_cell (const std::string &cn) const;
/**
* @brief Returns a pair with a bool (indicating whether the cell name is known) and the cell index for this name
*/
std::pair<bool, db::cell_index_type> cell_by_name (const std::string &name) const;
/**
* @brief Make a cell from an ID (OASIS)
*/
db::cell_index_type make_cell (db::Layout &layout, size_t id);
/**
* @brief Returns true, if there is a cell with the given ID alreay
*/
bool has_cell (size_t id) const;
/**
* @brief Returns a pair with a bool (indicating whether the cell ID is known) and the cell index for this ID
*/
std::pair<bool, db::cell_index_type> cell_by_id (size_t id) const;
/**
* @brief Registers a cell name for an ID
*/
void rename_cell (db::Layout &layout, size_t id, const std::string &cn);
/**
* @brief Gets the name for a given cell ID if known, otherwise returns an empty string
*/
const std::string &name_for_id (size_t id) const;
/**
* @brief Returns a cell reference by ID
* If the cell does not exist, it's created. It is marked as ghost cell until
* "make_cell" is called.
*/
db::cell_index_type cell_for_instance (db::Layout &layout, size_t id);
/**
* @brief Returns a cell reference by name
* Same as the previous method, but acting on cell names.
*/
db::cell_index_type cell_for_instance (db::Layout &layout, const std::string &cn);
/**
* @brief Finishes the reading process
*
* This method will first check if all cells IDs got a name.
* After this, the cells are renamed and cell conflict resolution will happen in the
* specified way (cell_conflict_resolution attribute).
*/
void finish (db::Layout &layout);
protected:
virtual void common_reader_error (const std::string &msg) = 0;
virtual void common_reader_warn (const std::string &msg) = 0;
/**
* @brief Merge (and delete) the src_cell into target_cell
*/
void merge_cell (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index) const;
/**
* @brief Merge (and delete) the src_cell into target_cell without instances
*/
void merge_cell_without_instances (db::Layout &layout, db::cell_index_type target_cell_index, db::cell_index_type src_cell_index) const;
private:
std::map<size_t, std::pair<std::string, db::cell_index_type> > m_id_map;
std::map<std::string, std::pair<size_t, db::cell_index_type> > m_name_map;
std::map<size_t, std::string> m_name_for_id;
CellConflictResolution m_cc_resolution;
};
/**
* @brief Structure that holds the GDS2 and OASIS specific options for the reader
*/
@ -44,7 +166,8 @@ public:
CommonReaderOptions ()
: create_other_layers (true),
enable_text_objects (true),
enable_properties (true)
enable_properties (true),
cell_conflict_resolution (CommonReader::AddToCell)
{
// .. nothing yet ..
}
@ -83,6 +206,22 @@ public:
*/
bool enable_properties;
/**
* @brief Specifies the cell merge behavior
*
* This enum controls how cells are read if a cell with the requested name already
* exists.
*
* AddToCell In this mode, instances or shapes are added to any existing cell
* OverwriteCell Overwrite existing cell. If the existing cell has children, those are removed unless used otherwise
* SkipNewCell Ignore the new cell and it's children
* RenameCell Rename the new cell
*
* If the existing opr the new cell is a ghost cell, AddToCell is applied always. In other words,
* ghost cells are always merged.
*/
CommonReader::CellConflictResolution cell_conflict_resolution;
/**
* @brief Implementation of FormatSpecificReaderOptions
*/

View File

@ -0,0 +1,332 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "dbD25TechnologyComponent.h"
#include "tlClassRegistry.h"
#include "tlString.h"
namespace db
{
std::string d25_component_name ()
{
return std::string ("d25");
}
std::string d25_description ()
{
return tl::to_string (tr ("Z stack (2.5d)"));
}
// -----------------------------------------------------------------------------------------
// D25LayerInfo implementation
D25LayerInfo::D25LayerInfo ()
: m_layer (), m_zstart (0.0), m_zstop (0.0)
{
// .. nothing yet ..
}
D25LayerInfo::~D25LayerInfo ()
{
// .. nothing yet ..
}
D25LayerInfo::D25LayerInfo (const D25LayerInfo &other)
{
operator= (other);
}
D25LayerInfo &
D25LayerInfo::operator= (const D25LayerInfo &other)
{
if (this != &other) {
m_layer = other.m_layer;
m_zstart = other.m_zstart;
m_zstop = other.m_zstop;
}
return *this;
}
bool
D25LayerInfo::operator== (const D25LayerInfo &other) const
{
return fabs (m_zstart - other.m_zstart) < db::epsilon && fabs (m_zstop - other.m_zstop) < db::epsilon;
}
void
D25LayerInfo::set_layer (const db::LayerProperties &l)
{
m_layer = l;
}
void
D25LayerInfo::set_layer_from_string (const std::string &l)
{
db::LayerProperties lp;
tl::Extractor ex (l.c_str ());
try {
lp.read (ex);
} catch (tl::Exception &) {
// ignore errors for now.
}
m_layer = lp;
}
std::string
D25LayerInfo::layer_as_string () const
{
return m_layer.to_string ();
}
void
D25LayerInfo::set_zstart (double z0)
{
m_zstart = z0;
}
void
D25LayerInfo::set_zstop (double z1)
{
m_zstop = z1;
}
// -----------------------------------------------------------------------------------
// D25TechnologyComponent implementation
D25TechnologyComponent::D25TechnologyComponent ()
: db::TechnologyComponent (d25_component_name (), d25_description ())
{
// provide some explanation for the initialization
m_src =
"# Provide z stack information here\n"
"# Each line is one layer. The specification consists of a layer specification, a colon and arguments.\n"
"# The arguments are named (like \"x=...\") or in serial. Parameters are separated by comma or blanks.\n"
"# Named arguments are:\n"
"#\n"
"# zstart The lower z position of the extruded layer in µm\n"
"# zstop The upper z position of the extruded layer in µm\n"
"# height The height of the extruded layer in µm\n"
"#\n"
"# 'height', 'zstart' and 'zstop' can be used in any combination. If no value is given for 'zstart', "
"# the upper level of the previous layer will be used.\n"
"#\n"
"# If a single unnamed parameter is given, it corresponds to 'height'. Two parameters correspond to\n"
"# 'zstart' and 'zstop'.\n"
"#\n"
"# Examples:\n"
"# 1: 0.5 1.5 # extrude layer 1/0 from 0.5 to 1.5 vertically\n"
"# 1/0: 0.5 1.5 # same with explicit datatype\n"
"# 1: zstop=1.5, zstart=0.5 # same with named parameters\n"
"# 1: height=1.0, zstop=1.5 # same with z stop minus height\n"
"# 1: 1.0 zstop=1.5 # same with height as unnamed parameter\n"
;
}
D25TechnologyComponent::D25TechnologyComponent (const D25TechnologyComponent &d)
: db::TechnologyComponent (d25_component_name (), d25_description ())
{
m_layers = d.m_layers;
m_src = d.m_src;
}
void
D25TechnologyComponent::compile_from_source (const std::string &src)
{
int current_line = 0;
m_layers.clear ();
try {
std::vector<std::string> lines = tl::split (src, "\n");
for (std::vector<std::string>::const_iterator l = lines.begin (); l != lines.end (); ++l) {
++current_line;
tl::Extractor ex (l->c_str ());
if (ex.test ("#")) {
// ignore comments
} else if (ex.at_end ()) {
// ignore empty lines
} else {
db::D25LayerInfo info;
if (! m_layers.empty ()) {
info.set_zstart (m_layers.back ().zstop ());
info.set_zstop (m_layers.back ().zstop ());
}
tl::Variant z0, z1, h;
std::vector<double> args;
db::LayerProperties lp;
lp.read (ex);
info.set_layer (lp);
ex.expect (":");
while (! ex.at_end ()) {
if (ex.test ("#")) {
break;
}
double pv = 0.0;
std::string pn;
if (ex.try_read_name (pn)) {
ex.expect ("=");
ex.read (pv);
} else {
ex.read (pv);
}
ex.test (",");
if (pn.empty ()) {
args.push_back (pv);
} else if (pn == "zstart") {
z0 = pv;
} else if (pn == "zstop") {
z1 = pv;
} else if (pn == "height") {
h = pv;
} else {
throw tl::Exception (tl::to_string (tr ("Invalid parameter name: ")) + pn);
}
}
if (args.size () == 0) {
if (z0.is_nil () && z1.is_nil ()) {
if (! h.is_nil ()) {
info.set_zstop (info.zstart () + h.to_double ());
}
} else if (z0.is_nil ()) {
info.set_zstop (z1.to_double ());
if (! h.is_nil ()) {
info.set_zstart (info.zstop () - h.to_double ());
}
} else if (z1.is_nil ()) {
info.set_zstart (z0.to_double ());
if (! h.is_nil ()) {
info.set_zstop (info.zstart () + h.to_double ());
}
} else {
info.set_zstart (z0.to_double ());
info.set_zstop (z1.to_double ());
}
} else if (args.size () == 1) {
if (! h.is_nil ()) {
if (! z0.is_nil ()) {
throw tl::Exception (tl::to_string (tr ("Rundundant parameters: zstart already given")));
}
if (! z1.is_nil ()) {
throw tl::Exception (tl::to_string (tr ("Rundundant parameters: zstop implicitly given")));
}
info.set_zstart (args[0]);
info.set_zstop (args[0] + h.to_double ());
} else {
if (! z1.is_nil ()) {
throw tl::Exception (tl::to_string (tr ("Rundundant parameters: zstop implicitly given")));
}
info.set_zstop ((! z0.is_nil () ? z0.to_double () : info.zstart ()) + args[0]);
}
} else if (args.size () == 2) {
if (! z0.is_nil ()) {
throw tl::Exception (tl::to_string (tr ("Rundundant parameters: zstart already given")));
}
if (! z1.is_nil ()) {
throw tl::Exception (tl::to_string (tr ("Rundundant parameters: zstop already given")));
}
if (! h.is_nil ()) {
throw tl::Exception (tl::to_string (tr ("Rundundant parameters: height implicitly given")));
}
info.set_zstart (args[0]);
info.set_zstop (args[1]);
} else {
throw tl::Exception (tl::to_string (tr ("Too many parameters (max 2)")));
}
m_layers.push_back (info);
}
}
} catch (tl::Exception &ex) {
throw tl::Exception (ex.msg () + tl::sprintf (tl::to_string (tr (" in line %d")), current_line));
}
m_src = src;
}
std::string
D25TechnologyComponent::to_string () const
{
std::string res;
for (const_iterator i = begin (); i != end (); ++i) {
if (! res.empty ()) {
res += "\n";
}
res += i->layer ().to_string () + ": zstart=" + tl::to_string (i->zstart ()) + ", zstop=" + tl::to_string (i->zstop ());
}
return res;
}
// -----------------------------------------------------------------------------------
// D25TechnologyComponent technology component registration
class D25TechnologyComponentProvider
: public db::TechnologyComponentProvider
{
public:
D25TechnologyComponentProvider ()
: db::TechnologyComponentProvider ()
{
// .. nothing yet ..
}
virtual db::TechnologyComponent *create_component () const
{
return new D25TechnologyComponent ();
}
virtual tl::XMLElementBase *xml_element () const
{
return new db::TechnologyComponentXMLElement<D25TechnologyComponent> (d25_component_name (),
tl::make_element ((D25TechnologyComponent::const_iterator (D25TechnologyComponent::*) () const) &D25TechnologyComponent::begin, (D25TechnologyComponent::const_iterator (D25TechnologyComponent::*) () const) &D25TechnologyComponent::end, &D25TechnologyComponent::add, "layer",
tl::make_member (&D25LayerInfo::layer_as_string, &D25LayerInfo::set_layer_from_string, "layer") +
tl::make_member (&D25LayerInfo::zstart, &D25LayerInfo::set_zstart, "zstart") +
tl::make_member (&D25LayerInfo::zstop, &D25LayerInfo::set_zstop, "zstop")
) +
tl::make_member (&D25TechnologyComponent::src, &D25TechnologyComponent::set_src, "src")
);
}
};
static tl::RegisteredClass<db::TechnologyComponentProvider> tc_decl (new D25TechnologyComponentProvider (), 3100, d25_component_name ().c_str ());
}

View File

@ -0,0 +1,154 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_dbD25TechnologyComponent
#define HDR_dbD25TechnologyComponent
#include "dbTechnology.h"
#include "dbLayerProperties.h"
namespace db
{
class DB_PUBLIC D25LayerInfo
{
public:
D25LayerInfo ();
~D25LayerInfo ();
D25LayerInfo (const D25LayerInfo &other);
D25LayerInfo &operator= (const D25LayerInfo &other);
bool operator== (const D25LayerInfo &other) const;
const db::LayerProperties &layer () const
{
return m_layer;
}
void set_layer_from_string (const std::string &l);
std::string layer_as_string () const;
void set_layer (const db::LayerProperties &l);
double zstart () const
{
return m_zstart;
}
void set_zstart (double z0);
double zstop () const
{
return m_zstop;
}
void set_zstop (double z1);
private:
db::LayerProperties m_layer;
double m_zstart, m_zstop;
};
class DB_PUBLIC D25TechnologyComponent
: public db::TechnologyComponent
{
public:
D25TechnologyComponent ();
D25TechnologyComponent (const D25TechnologyComponent &d);
typedef std::list<D25LayerInfo> layers_type;
typedef layers_type::const_iterator const_iterator;
typedef layers_type::iterator iterator;
void compile_from_source (const std::string &src);
const_iterator begin () const
{
return m_layers.begin ();
}
iterator begin ()
{
return m_layers.begin ();
}
const_iterator end () const
{
return m_layers.end ();
}
iterator end ()
{
return m_layers.end ();
}
void clear ()
{
m_layers.clear ();
}
void erase (iterator p)
{
m_layers.erase (p);
}
void insert (iterator p, const D25LayerInfo &info)
{
m_layers.insert (p, info);
}
void add (const D25LayerInfo &info)
{
m_layers.push_back (info);
}
size_t size () const
{
return m_layers.size ();
}
const std::string &src () const
{
return m_src;
}
// for persistency only, use "compile_from_source" to read from a source string
void set_src (const std::string &s)
{
m_src = s;
}
std::string to_string () const;
db::TechnologyComponent *clone () const
{
return new D25TechnologyComponent (*this);
}
private:
layers_type m_layers;
std::string m_src;
};
}
#endif

View File

@ -931,7 +931,7 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout
cm = m_delivery_mapping_cache.insert (std::make_pair (key, db::CellMapping ())).first;
// collects the cell mappings we skip because they are variants (variant building or box variants)
std::map<db::cell_index_type, std::pair<db::cell_index_type, std::set<db::Box> > > cm_skipped_variants;
std::map<db::cell_index_type, db::HierarchyBuilder::CellMapKey> cm_skipped_variants;
if (into_layout == original_builder.source ().layout () && &into_layout->cell (into_cell) == original_builder.source ().top_cell ()) {
@ -945,14 +945,14 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout
HierarchyBuilder::cell_map_type::const_iterator mm = m;
++mm;
bool skip = original_builder.is_variant (m->second); // skip variant cells
while (mm != original_builder.end_cell_map () && mm->first.first == m->first.first) {
while (mm != original_builder.end_cell_map () && mm->first.original_cell == m->first.original_cell) {
// we have cell (box) variants and cannot simply map
++mm;
skip = true;
}
if (! skip) {
cm->second.map (m->second, m->first.first);
cm->second.map (m->second, m->first.original_cell);
} else {
for (HierarchyBuilder::cell_map_type::const_iterator n = m; n != mm; ++n) {
tl_assert (cm_skipped_variants.find (n->second) == cm_skipped_variants.end ());
@ -988,17 +988,19 @@ DeepShapeStore::cell_mapping_to_original (unsigned int layout_index, db::Layout
db::cell_index_type var_org = original_builder.original_target_for_variant (np->first);
std::map<db::cell_index_type, std::pair<db::cell_index_type, std::set<db::Box> > >::const_iterator icm = cm_skipped_variants.find (var_org);
std::map<db::cell_index_type, db::HierarchyBuilder::CellMapKey>::const_iterator icm = cm_skipped_variants.find (var_org);
if (icm != cm_skipped_variants.end ()) {
// create the variant clone in the original layout too and delete this cell
VariantsCollectorBase::copy_shapes (*into_layout, np->second, icm->second.first);
cells_to_delete.insert (icm->second.first);
VariantsCollectorBase::copy_shapes (*into_layout, np->second, icm->second.original_cell);
cells_to_delete.insert (icm->second.original_cell);
// forget the original cell (now separated into variants) and map the variants back into the
// DSS layout
original_builder.unmap (icm->second);
original_builder.map (std::make_pair (np->second, icm->second.second), np->first);
db::HierarchyBuilder::CellMapKey k = icm->second;
k.original_cell = np->second;
original_builder.map (k, np->first);
// forget the variant as now it's a real cell in the source layout
original_builder.unregister_variant (np->first);

View File

@ -799,10 +799,18 @@ struct cluster_building_receiver
typedef std::set<size_t> global_nets;
typedef std::pair<shape_vector, global_nets> cluster_value;
cluster_building_receiver (const db::Connectivity &conn)
: mp_conn (&conn)
cluster_building_receiver (const db::Connectivity &conn, const tl::equivalence_clusters<size_t> *attr_equivalence)
: mp_conn (&conn), mp_attr_equivalence (attr_equivalence)
{
// .. nothing yet..
if (mp_attr_equivalence) {
// cache the global nets per attribute equivalence cluster
for (size_t gid = 0; gid < conn.global_nets (); ++gid) {
tl::equivalence_clusters<size_t>::cluster_id_type cl = attr_equivalence->cluster_id (global_net_id_to_attr (gid));
if (cl > 0) {
m_global_nets_by_attribute_cluster [cl].insert (gid);
}
}
}
}
void generate_clusters (local_clusters<T> &clusters)
@ -817,7 +825,32 @@ struct cluster_building_receiver
cluster->add_attr (s->second.second);
}
cluster->set_global_nets (c->second);
std::set<size_t> global_nets = c->second;
// Add the global nets we derive from the attribute equivalence (join_nets of labelled vs.
// global nets)
if (mp_attr_equivalence) {
for (typename shape_vector::const_iterator s = c->first.begin (); s != c->first.end (); ++s) {
size_t a = s->second.second;
if (a == 0) {
continue;
}
tl::equivalence_clusters<size_t>::cluster_id_type cl = mp_attr_equivalence->cluster_id (a);
if (cl > 0) {
std::map<size_t, std::set<size_t> >::const_iterator gn = m_global_nets_by_attribute_cluster.find (cl);
if (gn != m_global_nets_by_attribute_cluster.end ()) {
global_nets.insert (gn->second.begin (), gn->second.end ());
}
}
}
}
cluster->set_global_nets (global_nets);
}
}
@ -913,6 +946,8 @@ private:
std::map<const T *, typename std::list<cluster_value>::iterator> m_shape_to_clusters;
std::map<size_t, typename std::list<cluster_value>::iterator> m_global_to_clusters;
std::list<cluster_value> m_clusters;
std::map<size_t, std::set<size_t> > m_global_nets_by_attribute_cluster;
const tl::equivalence_clusters<size_t> *mp_attr_equivalence;
void join (typename std::list<cluster_value>::iterator ic1, typename std::list<cluster_value>::iterator ic2)
{
@ -1035,7 +1070,7 @@ local_clusters<T>::build_clusters (const db::Cell &cell, const db::Connectivity
}
}
cluster_building_receiver<T, box_type> rec (conn);
cluster_building_receiver<T, box_type> rec (conn, attr_equivalence);
bs.process (rec, 1 /*==touching*/, bc);
rec.generate_clusters (*this);
@ -1048,36 +1083,27 @@ template <class T>
void
local_clusters<T>::apply_attr_equivalences (const tl::equivalence_clusters<size_t> &attr_equivalence)
{
tl::equivalence_clusters<size_t> eq;
// collect all local attributes (the ones which are present in attr_equivalence) into "eq"
// and form equivalences for multi-attribute clusters.
for (const_iterator c = begin (); c != end (); ++c) {
typename local_cluster<T>::attr_iterator a0;
for (typename local_cluster<T>::attr_iterator a = c->begin_attr (); a != c->end_attr (); ++a) {
if (attr_equivalence.has_attribute (*a)) {
if (a0 == typename local_cluster<T>::attr_iterator ()) {
a0 = a;
}
eq.same (*a0, *a);
}
}
}
// apply the equivalences implied by attr_equivalence
eq.apply_equivalences (attr_equivalence);
// identify the layout clusters joined into one attribute cluster and join them
std::map<tl::equivalence_clusters<size_t>::cluster_id_type, std::set<size_t> > c2c;
for (const_iterator c = begin (); c != end (); ++c) {
for (typename local_cluster<T>::attr_iterator a = c->begin_attr (); a != c->end_attr (); ++a) {
tl::equivalence_clusters<size_t>::cluster_id_type cl = attr_equivalence.cluster_id (*a);
if (cl > 0) {
c2c [cl].insert (c->id ());
}
}
for (typename local_cluster<T>::global_nets_iterator g = c->begin_global_nets (); g != c->end_global_nets (); ++g) {
size_t a = global_net_id_to_attr (*g);
tl::equivalence_clusters<size_t>::cluster_id_type cl = attr_equivalence.cluster_id (a);
if (cl > 0) {
c2c [cl].insert (c->id ());
}
}
}
for (std::map<tl::equivalence_clusters<size_t>::cluster_id_type, std::set<size_t> >::const_iterator c = c2c.begin (); c != c2c.end (); ++c) {

View File

@ -1396,11 +1396,11 @@ private:
/**
* @brief A helper function generating an attribute ID from a property ID
* This function is used to provide a generic attribute wrapping a property ID and a text ID.
* This function is used to provide a generic attribute wrapping a property ID, text ID and global net ID
*/
inline size_t prop_id_to_attr (db::properties_id_type id)
{
return size_t (id) * 2;
return size_t (id) * 4;
}
/**
@ -1408,7 +1408,7 @@ inline size_t prop_id_to_attr (db::properties_id_type id)
*/
inline bool is_prop_id_attr (size_t attr)
{
return (attr & 1) == 0;
return (attr & 3) == 0;
}
/**
@ -1416,7 +1416,32 @@ inline bool is_prop_id_attr (size_t attr)
*/
inline db::properties_id_type prop_id_from_attr (size_t attr)
{
return attr / 2;
return attr / 4;
}
/**
* @brief A helper function generating an attribute ID from a global net ID
* This function is used to provide a generic attribute wrapping a property ID, text ID and global net ID
*/
inline size_t global_net_id_to_attr (size_t id)
{
return size_t (id) * 4 + 2;
}
/**
* @brief Gets a value indicating whether the attribute is a global net ID
*/
inline bool is_global_net_id_attr (size_t attr)
{
return (attr & 3) == 2;
}
/**
* @brief Gets the global net ID from an attribute
*/
inline size_t global_net_id_from_attr (size_t attr)
{
return attr / 4;
}
/**
@ -1424,9 +1449,18 @@ inline db::properties_id_type prop_id_from_attr (size_t attr)
*/
inline size_t text_ref_to_attr (const db::Text *tr)
{
// NOTE: pointers are 32bit aligned, hence the lower two bits are 0
return size_t (tr) + 1;
}
/**
* @brief Gets a value indicating whether the attribute is a StringRef
*/
inline bool is_text_ref_attr (size_t attr)
{
return (attr & 3) == 1;
}
/**
* @brief Gets the text value from an attribute ID
*/

View File

@ -62,6 +62,14 @@ compare_iterators_with_respect_to_target_hierarchy (const db::RecursiveShapeIter
return iter1.max_depth () < iter2.max_depth () ? -1 : 1;
}
// take potential selection of cells into account
if (iter1.disables () != iter2.disables ()) {
return iter1.disables () < iter2.disables () ? -1 : 1;
}
if (iter1.enables () != iter2.enables ()) {
return iter1.enables () < iter2.enables () ? -1 : 1;
}
// if a region is set, the hierarchical appearance is the same only if the layers and
// complex region are identical
if ((iter1.region () == db::Box::world ()) != (iter2.region () == db::Box::world ())) {
@ -238,11 +246,11 @@ HierarchyBuilder::begin (const RecursiveShapeIterator *iter)
return;
}
std::pair<db::cell_index_type, std::set<db::Box> > key (iter->top_cell ()->cell_index (), std::set<db::Box> ());
CellMapKey key (iter->top_cell ()->cell_index (), false, std::set<db::Box> ());
m_cm_entry = m_cell_map.find (key);
if (m_cm_entry == m_cell_map.end ()) {
db::cell_index_type new_top_index = mp_target->add_cell (iter->layout ()->cell_name (key.first));
db::cell_index_type new_top_index = mp_target->add_cell (iter->layout ()->cell_name (key.original_cell));
m_cm_entry = m_cell_map.insert (std::make_pair (key, new_top_index)).first;
}
@ -300,27 +308,47 @@ HierarchyBuilder::leave_cell (const RecursiveShapeIterator * /*iter*/, const db:
m_cell_stack.pop_back ();
}
db::cell_index_type
HierarchyBuilder::make_cell_variant (const HierarchyBuilder::CellMapKey &key, const std::string &cell_name)
{
m_cm_entry = m_cell_map.find (key);
m_cm_new_entry = false;
db::cell_index_type new_cell;
if (m_cm_entry == m_cell_map.end ()) {
std::string cn = cell_name;
if (! key.clip_region.empty ()) {
cn += "$CLIP_VAR";
}
if (key.inactive) {
cn += "$DIS";
}
new_cell = mp_target->add_cell (cn.c_str ());
m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first;
m_cm_new_entry = true;
m_cells_to_be_filled.insert (new_cell);
} else {
new_cell = m_cm_entry->second;
}
return new_cell;
}
HierarchyBuilder::new_inst_mode
HierarchyBuilder::new_inst (const RecursiveShapeIterator *iter, const db::CellInstArray &inst, const db::Box & /*region*/, const box_tree_type * /*complex_region*/, bool all)
{
if (all) {
std::pair<db::cell_index_type, std::set<db::Box> > key (inst.object ().cell_index (), std::set<db::Box> ());
m_cm_entry = m_cell_map.find (key);
m_cm_new_entry = false;
if (m_cm_entry == m_cell_map.end ()) {
db::cell_index_type new_cell = mp_target->add_cell (iter->layout ()->cell_name (inst.object ().cell_index ()));
m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first;
m_cm_new_entry = true;
m_cells_to_be_filled.insert (new_cell);
}
CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst.object ().cell_index ()), std::set<db::Box> ());
db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst.object ().cell_index ()));
// for new cells, create this instance
if (m_cell_stack.back ().first) {
db::CellInstArray new_inst (inst, &mp_target->array_repository ());
new_inst.object () = db::CellInst (m_cm_entry->second);
new_inst.object () = db::CellInst (new_cell);
new_inst.transform_into (m_trans);
for (std::vector<db::Cell *>::const_iterator c = m_cell_stack.back ().second.begin (); c != m_cell_stack.back ().second.end (); ++c) {
(*c)->insert (new_inst);
@ -353,24 +381,12 @@ HierarchyBuilder::new_inst_member (const RecursiveShapeIterator *iter, const db:
return false;
}
std::pair<db::cell_index_type, std::set<db::Box> > key (inst.object ().cell_index (), clip_variant.second);
m_cm_entry = m_cell_map.find (key);
m_cm_new_entry = false;
if (m_cm_entry == m_cell_map.end ()) {
std::string suffix;
if (! key.second.empty ()) {
suffix = "$CLIP_VAR";
}
db::cell_index_type new_cell = mp_target->add_cell ((std::string (iter->layout ()->cell_name (inst.object ().cell_index ())) + suffix).c_str ());
m_cm_entry = m_cell_map.insert (std::make_pair (key, new_cell)).first;
m_cm_new_entry = true;
m_cells_to_be_filled.insert (new_cell);
}
CellMapKey key (inst.object ().cell_index (), iter->is_child_inactive (inst.object ().cell_index ()), clip_variant.second);
db::cell_index_type new_cell = make_cell_variant (key, iter->layout ()->cell_name (inst.object ().cell_index ()));
// for a new cell, create this instance
if (m_cell_stack.back ().first) {
db::CellInstArray new_inst (db::CellInst (m_cm_entry->second), trans);
db::CellInstArray new_inst (db::CellInst (new_cell), trans);
new_inst.transform_into (m_trans);
for (std::vector<db::Cell *>::const_iterator c = m_cell_stack.back ().second.begin (); c != m_cell_stack.back ().second.end (); ++c) {
(*c)->insert (new_inst);

View File

@ -225,8 +225,36 @@ class DB_PUBLIC HierarchyBuilder
: public db::RecursiveShapeReceiver
{
public:
struct CellMapKey
{
CellMapKey ()
: original_cell (0), inactive (false)
{ }
typedef std::map<std::pair<db::cell_index_type, std::set<db::Box> >, db::cell_index_type> cell_map_type;
CellMapKey (db::cell_index_type _original_cell, bool _inactive, const std::set<db::Box> &_clip_region)
: original_cell (_original_cell), inactive (_inactive), clip_region (_clip_region)
{ }
bool operator== (const CellMapKey &other) const
{
return original_cell == other.original_cell && inactive == other.inactive && clip_region == other.clip_region;
}
bool operator< (const CellMapKey &other) const
{
if (original_cell != other.original_cell) { return original_cell < other.original_cell; }
if (inactive != other.inactive) { return inactive < other.inactive; }
if (clip_region != other.clip_region) { return clip_region < other.clip_region; }
return false;
}
db::cell_index_type original_cell;
bool inactive;
std::set<db::Box> clip_region;
};
typedef std::map<CellMapKey, db::cell_index_type> cell_map_type;
typedef std::map<db::cell_index_type, std::vector<db::cell_index_type> > original_target_to_variants_map_type;
typedef std::map<db::cell_index_type, db::cell_index_type> variant_to_original_target_map_type;
@ -346,6 +374,8 @@ public:
db::cell_index_type original_target_for_variant (db::cell_index_type ci) const;
private:
db::cell_index_type make_cell_variant (const HierarchyBuilder::CellMapKey &key, const std::string &cell_name);
tl::weak_ptr<db::Layout> mp_target;
HierarchyBuilderShapeReceiver *mp_pipe;
bool m_initial_pass;

View File

@ -1068,14 +1068,41 @@ Layout::add_cell (const char *name)
return new_index;
}
cell_index_type
Layout::add_anonymous_cell ()
{
std::string b;
// create a new cell
cell_index_type new_index = allocate_new_cell ();
cell_type *new_cell = new cell_type (new_index, *this);
m_cells.push_back_ptr (new_cell);
m_cell_ptrs [new_index] = new_cell;
// enter it's index and cell_name
register_cell_name (0, new_index);
if (manager () && manager ()->transacting ()) {
manager ()->queue (this, new NewRemoveCellOp (new_index, m_cell_names [new_index], false /*new*/, 0));
}
return new_index;
}
void
Layout::register_cell_name (const char *name, cell_index_type ci)
{
// enter it's index and cell_name
char *cp;
cp = new char [strlen (name) + 1];
strcpy (cp, name);
if (name == 0) {
cp = new char [1];
*cp = 0;
} else {
cp = new char [strlen (name) + 1];
strcpy (cp, name);
}
while (m_cell_names.size () < ci) {
char *e = new char [1];
@ -1090,7 +1117,9 @@ Layout::register_cell_name (const char *name, cell_index_type ci)
m_cell_names.push_back (cp);
}
m_cell_map.insert (std::make_pair (cp, ci));
if (name) {
m_cell_map.insert (std::make_pair (cp, ci));
}
}
void

View File

@ -727,7 +727,17 @@ public:
*/
cell_index_type add_cell (const char *name = 0);
/**
/**
* @brief Add a cell without a name
*
* The cell is created, but cannot be found by name. The name returned is an empty string.
* The cell is created with the purpose of being renamed later.
*
* @return The index of the new cell
*/
cell_index_type add_anonymous_cell ();
/**
* @brief Rename a cell
*
* Rename the cell with the given id.
@ -1065,8 +1075,7 @@ public:
* @brief Delete the subcells of the given cells which are not used otherwise
*
* All subcells referenced directy or indirectly but not used otherwise
* are deleted as well. This basically prunes the cell tree by this cell.
* All instances of this cell are deleted as well.
* are deleted as well.
* This method is more efficent than calling prune_subcells for single cells multiple times.
*
* @param from A begin iterator delivering the cell id's to delete
@ -1085,8 +1094,7 @@ public:
* @brief Delete the subcells of the given cells which are not used otherwise
*
* All subcells referenced directy or indirectly but not used otherwise
* are deleted as well. This basically prunes the cell tree by this cell.
* All instances of this cell are deleted as well.
* are deleted as well.
* This method is more efficent than calling prune_subcells for single cells multiple times.
*
* @param cells A set of cell id's to prune

View File

@ -51,7 +51,7 @@ void NetlistExtractor::set_include_floating_subcircuits (bool f)
}
static void
build_net_name_equivalence (const db::Layout *layout, db::property_names_id_type net_name_id, const std::string &joined_net_names, tl::equivalence_clusters<size_t> &eq)
build_net_name_equivalence (const db::Layout *layout, const db::Connectivity &conn, db::property_names_id_type net_name_id, const std::string &joined_net_names, tl::equivalence_clusters<size_t> &eq)
{
std::map<std::string, std::set<size_t> > prop_by_name;
tl::GlobPattern jn_pattern (joined_net_names);
@ -67,6 +67,14 @@ build_net_name_equivalence (const db::Layout *layout, db::property_names_id_type
}
}
// include pseudo-attributes for global nets to implement "join_with" for global nets
for (size_t gid = 0; gid < conn.global_nets (); ++gid) {
const std::string &gn = conn.global_net_name (gid);
if (jn_pattern.match (gn)) {
prop_by_name [gn].insert (db::global_net_id_to_attr (gid));
}
}
const db::repository<db::Text> &text_repository = layout->shape_repository ().repository (db::object_tag<db::Text> ());
for (db::repository<db::Text>::iterator t = text_repository.begin (); t != text_repository.end (); ++t) {
std::string nn = t->string ();
@ -107,12 +115,12 @@ NetlistExtractor::extract_nets (const db::DeepShapeStore &dss, unsigned int layo
std::map<db::cell_index_type, tl::equivalence_clusters<size_t> > net_name_equivalence;
if (m_text_annot_name_id.first) {
if (! m_joined_net_names.empty ()) {
build_net_name_equivalence (mp_layout, m_text_annot_name_id.second, m_joined_net_names, net_name_equivalence [hier_clusters_type::top_cell_index]);
build_net_name_equivalence (mp_layout, conn, m_text_annot_name_id.second, m_joined_net_names, net_name_equivalence [hier_clusters_type::top_cell_index]);
}
for (std::list<std::pair<std::string, std::string> >::const_iterator m = m_joined_net_names_per_cell.begin (); m != m_joined_net_names_per_cell.end (); ++m) {
std::pair<bool, db::cell_index_type> cp = mp_layout->cell_by_name (m->first.c_str ());
if (cp.first) {
build_net_name_equivalence (mp_layout, m_text_annot_name_id.second, m->second, net_name_equivalence [cp.second]);
build_net_name_equivalence (mp_layout, conn, m_text_annot_name_id.second, m->second, net_name_equivalence [cp.second]);
}
}
}
@ -313,7 +321,7 @@ void NetlistExtractor::collect_labels (const connected_clusters_type &clusters,
}
} else {
} else if (db::is_text_ref_attr (*a)) {
net_names.insert (db::text_from_attr (*a));

View File

@ -104,46 +104,66 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
std::string cn = model;
db::DeviceClass *cls = circuit->netlist ()->device_class_by_name (cn);
if (cls) {
if (element == "R") {
// use given class
} else if (element == "R") {
if (cn.empty ()) {
cn = "RES";
if (cls) {
if (! dynamic_cast<db::DeviceClassResistor *>(cls)) {
error (tl::sprintf (tl::to_string (tr ("Class %s not a resistor device class as required by 'R' element")), cn));
}
} else {
if (cn.empty ()) {
cn = "RES";
}
cls = make_device_class<db::DeviceClassResistor> (circuit, cn);
}
cls = make_device_class<db::DeviceClassResistor> (circuit, cn);
// Apply multiplier
value /= mult;
} else if (element == "L") {
if (cn.empty ()) {
cn = "IND";
if (cls) {
if (! dynamic_cast<db::DeviceClassInductor *>(cls)) {
error (tl::sprintf (tl::to_string (tr ("Class %s not a inductor device class as required by 'L' element")), cn));
}
} else {
if (cn.empty ()) {
cn = "IND";
}
cls = make_device_class<db::DeviceClassInductor> (circuit, cn);
}
cls = make_device_class<db::DeviceClassInductor> (circuit, cn);
// Apply multiplier
value /= mult;
} else if (element == "C") {
if (cn.empty ()) {
cn = "CAP";
if (cls) {
if (! dynamic_cast<db::DeviceClassCapacitor *>(cls)) {
error (tl::sprintf (tl::to_string (tr ("Class %s not a capacitor device class as required by 'C' element")), cn));
}
} else {
if (cn.empty ()) {
cn = "CAP";
}
cls = make_device_class<db::DeviceClassCapacitor> (circuit, cn);
}
cls = make_device_class<db::DeviceClassCapacitor> (circuit, cn);
// Apply multiplier
value *= mult;
} else if (element == "D") {
if (cn.empty ()) {
cn = "DIODE";
if (cls) {
if (! dynamic_cast<db::DeviceClassDiode *>(cls)) {
error (tl::sprintf (tl::to_string (tr ("Class %s not a diode device class as required by 'D' element")), cn));
}
} else {
if (cn.empty ()) {
cn = "DIODE";
}
cls = make_device_class<db::DeviceClassDiode> (circuit, cn);
}
cls = make_device_class<db::DeviceClassDiode> (circuit, cn);
// Apply multiplier to "A"
std::map<std::string, double>::iterator p;
@ -154,18 +174,30 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
} else if (element == "Q") {
if (nets.size () == 3) {
if (cn.empty ()) {
cn = "BJT3";
}
cls = make_device_class<db::DeviceClassBJT3Transistor> (circuit, cn);
} else if (nets.size () == 4) {
if (cn.empty ()) {
cn = "BJT4";
}
cls = make_device_class<db::DeviceClassBJT4Transistor> (circuit, cn);
} else {
if (nets.size () != 3 && nets.size () != 4) {
error (tl::to_string (tr ("'Q' element needs to have 3 or 4 terminals")));
} else if (cls) {
if (nets.size () == 3) {
if (! dynamic_cast<db::DeviceClassBJT3Transistor *>(cls)) {
error (tl::sprintf (tl::to_string (tr ("Class %s not a 3-terminal BJT device class as required by 'Q' element")), cn));
}
} else {
if (! dynamic_cast<db::DeviceClassBJT4Transistor *>(cls)) {
error (tl::sprintf (tl::to_string (tr ("Class %s not a 4-terminal BJT device class as required by 'Q' element")), cn));
}
}
} else {
if (nets.size () == 3) {
if (cn.empty ()) {
cn = "BJT3";
}
cls = make_device_class<db::DeviceClassBJT3Transistor> (circuit, cn);
} else {
if (cn.empty ()) {
cn = "BJT4";
}
cls = make_device_class<db::DeviceClassBJT4Transistor> (circuit, cn);
}
}
// Apply multiplier to "AE"
@ -177,22 +209,28 @@ bool NetlistSpiceReaderDelegate::element (db::Circuit *circuit, const std::strin
} else if (element == "M") {
if (nets.size () == 4) {
if (cn.empty ()) {
cn = "MOS4";
if (cls) {
if (! dynamic_cast<db::DeviceClassMOS4Transistor *>(cls)) {
error (tl::sprintf (tl::to_string (tr ("Class %s not a 4-terminal MOS device class as required by 'M' element")), cn));
}
cls = make_device_class<db::DeviceClassMOS4Transistor> (circuit, cn);
// Apply multiplier to "W"
std::map<std::string, double>::iterator p;
p = params.find ("W");
if (p != params.end ()) {
p->second *= mult;
}
} else {
error (tl::to_string (tr ("'M' element needs to have 4 terminals")));
if (nets.size () == 4) {
if (cn.empty ()) {
cn = "MOS4";
}
cls = make_device_class<db::DeviceClassMOS4Transistor> (circuit, cn);
} else {
error (tl::to_string (tr ("'M' element needs to have 4 terminals")));
}
}
// Apply multiplier to "W"
std::map<std::string, double>::iterator p;
p = params.find ("W");
if (p != params.end ()) {
p->second *= mult;
}
} else {
error (tl::sprintf (tl::to_string (tr ("Not a known element type: '%s'")), element));
}

View File

@ -864,10 +864,9 @@ RecursiveShapeIterator::new_cell (RecursiveShapeReceiver *receiver) const
m_layer = m_layers.front ();
}
if (! m_start.empty () && m_start.find (cell_index ()) != m_start.end ()) {
set_inactive (false);
} else if (! m_stop.empty () && m_stop.find (cell_index ()) != m_stop.end ()) {
set_inactive (true);
bool new_cell_inactive = is_child_inactive (cell_index ());
if (is_inactive () != new_cell_inactive) {
set_inactive (new_cell_inactive);
}
new_layer ();
@ -975,6 +974,18 @@ RecursiveShapeIterator::is_outside_complex_region (const db::Box &box) const
}
}
bool
RecursiveShapeIterator::is_child_inactive (db::cell_index_type new_child) const
{
bool inactive = is_inactive ();
if (! m_start.empty () && m_start.find (new_child) != m_start.end ()) {
inactive = false;
} else if (! m_stop.empty () && m_stop.find (new_child) != m_stop.end ()) {
inactive = true;
}
return inactive;
}
void
RecursiveShapeIterator::push (RecursiveShapeReceiver *receiver)
{

View File

@ -704,6 +704,19 @@ public:
*/
void push (RecursiveShapeReceiver *receiver);
/**
* @brief Returns a value indicating whether the current cell is inactive (disabled)
*/
bool is_inactive () const
{
return (reinterpret_cast<size_t> (mp_cell) & size_t (1)) != 0;
}
/**
* @brief Returns a value indicating whether a new child cell of the current cell will be inactive
*/
bool is_child_inactive (db::cell_index_type new_child) const;
private:
std::vector<unsigned int> m_layers;
bool m_has_layers;
@ -760,11 +773,6 @@ private:
bool is_outside_complex_region (const db::Box &box) const;
bool is_inactive () const
{
return (reinterpret_cast<size_t> (mp_cell) & size_t (1)) != 0;
}
void set_inactive (bool a) const
{
size_t c = reinterpret_cast<size_t> (mp_cell);

View File

@ -174,9 +174,15 @@ Shapes::do_insert (const Shapes &d)
if (layout () == d.layout ()) {
// both shape containers reside in the same repository space - simply copy
m_layers.reserve (d.m_layers.size ());
for (tl::vector<LayerBase *>::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) {
m_layers.push_back ((*l)->clone (this, manager ()));
if (m_layers.empty ()) {
m_layers.reserve (d.m_layers.size ());
for (tl::vector<LayerBase *>::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) {
m_layers.push_back ((*l)->clone (this, manager ()));
}
} else {
for (tl::vector<LayerBase *>::const_iterator l = d.m_layers.begin (); l != d.m_layers.end (); ++l) {
(*l)->insert_into (this);
}
}
} else if (layout () == 0) {

View File

@ -494,6 +494,7 @@ public:
virtual void transform_into (Shapes *target, const Trans &trans, GenericRepository &rep, ArrayRepository &array_rep, pm_delegate_type &pm) const = 0;
virtual void transform_into (Shapes *target, const ICplxTrans &trans, GenericRepository &rep, ArrayRepository &array_rep) const = 0;
virtual void transform_into (Shapes *target, const ICplxTrans &trans, GenericRepository &rep, ArrayRepository &array_rep, pm_delegate_type &pm) const = 0;
virtual void insert_into (Shapes *target) = 0;
virtual void deref_into (Shapes *target) = 0;
virtual void deref_into (Shapes *target, pm_delegate_type &pm) = 0;
virtual void deref_and_transform_into (Shapes *target, const Trans &trans) = 0;

View File

@ -834,6 +834,13 @@ layer_class<Sh, StableTag>::transform_into (Shapes *target, const ICplxTrans &tr
}
}
template <class Sh, class StableTag>
void
layer_class<Sh, StableTag>::insert_into (Shapes *target)
{
target->insert (m_layer.begin (), m_layer.end ());
}
template <class Sh, class StableTag>
void
layer_class<Sh, StableTag>::deref_into (Shapes *target)
@ -870,6 +877,7 @@ layer_class<Sh, StableTag>::deref_and_transform_into (Shapes *target, const Tran
{
deref_and_transform_into_shapes deref_op (target);
for (typename layer_type::iterator s = m_layer.begin (); s != m_layer.end (); ++s) {
deref_op (*s, trans, pm);
}
}

View File

@ -149,6 +149,7 @@ public:
virtual void transform_into (Shapes *target, const Trans &trans, GenericRepository &rep, ArrayRepository &array_rep, pm_delegate_type &pm) const;
virtual void transform_into (Shapes *target, const ICplxTrans &trans, GenericRepository &rep, ArrayRepository &array_rep) const;
virtual void transform_into (Shapes *target, const ICplxTrans &trans, GenericRepository &rep, ArrayRepository &array_rep, pm_delegate_type &pm) const;
virtual void insert_into (Shapes *target);
virtual void deref_into (Shapes *target);
virtual void deref_into (Shapes *target, pm_delegate_type &pm);
virtual void deref_and_transform_into (Shapes *target, const Trans &trans);

View File

@ -1491,6 +1491,7 @@ Class<db::Cell> decl_Cell ("db", "Cell",
"If no property with that key exists, it will create one. Using that method is more "
"convenient than creating a new property set with a new ID and assigning that properties ID.\n"
"This method may change the properties ID. "
"Note: GDS only supports integer keys. OASIS supports numeric and string keys. "
"\n"
"This method has been introduced in version 0.23."
) +
@ -3453,6 +3454,7 @@ Class<db::Instance> decl_Instance ("db", "Instance",
"If no property with that key exists, it will create one. Using that method is more "
"convenient than creating a new property set with a new ID and assigning that properties ID.\n"
"This method may change the properties ID. "
"Note: GDS only supports integer keys. OASIS supports numeric and string keys. "
"Calling this method may invalidate any iterators. It should not be called inside a "
"loop iterating over instances.\n"
"\n"

View File

@ -25,6 +25,7 @@
#include "dbCommonReader.h"
#include "dbLoadLayoutOptions.h"
#include "gsiDecl.h"
#include "gsiEnums.h"
namespace dn
{
@ -84,6 +85,16 @@ static void set_properties_enabled (db::LoadLayoutOptions *options, bool l)
options->get_options<db::CommonReaderOptions> ().enable_properties = l;
}
static db::CommonReader::CellConflictResolution get_cell_conflict_resolution (const db::LoadLayoutOptions *options)
{
return options->get_options<db::CommonReaderOptions> ().cell_conflict_resolution;
}
static void set_cell_conflict_resolution (db::LoadLayoutOptions *options, db::CommonReader::CellConflictResolution cc)
{
options->get_options<db::CommonReaderOptions> ().cell_conflict_resolution = cc;
}
// extend lay::LoadLayoutOptions with the Common options
static
gsi::ClassExt<db::LoadLayoutOptions> common_reader_options (
@ -156,10 +167,56 @@ gsi::ClassExt<db::LoadLayoutOptions> common_reader_options (
"@param enabled True, if properties should be read."
"\n"
"Starting with version 0.25 this option only applies to GDS2 and OASIS format. Other formats provide their own configuration."
) +
gsi::method_ext ("cell_conflict_resolution", &get_cell_conflict_resolution,
"@brief Gets the cell conflict resolution mode\n"
"\n"
"Multiple layout files can be collected into a single Layout object by reading file after file into the Layout object. "
"Cells with same names are considered a conflict. This mode indicates how such conflicts are resolved. See \\LoadLayoutOptions::CellConflictResolution "
"for the values allowed. The default mode is \\LoadLayoutOptions::CellConflictResolution#AddToCell.\n"
"\n"
"This option has been introduced in version 0.27."
) +
gsi::method_ext ("cell_conflict_resolution=", &set_cell_conflict_resolution, gsi::arg ("mode"),
"@brief Sets the cell conflict resolution mode\n"
"\n"
"See \\cell_conflict_resolution for details about this option.\n"
"\n"
"This option has been introduced in version 0.27."
),
""
);
gsi::EnumIn<db::LoadLayoutOptions, db::CommonReader::CellConflictResolution> decl_dbCommonReader_CellConflictResolution ("db", "CellConflictResolution",
gsi::enum_const ("AddToCell", db::CommonReader::AddToCell,
"@brief Add content to existing cell\n"
"This is the mode use in before version 0.27. Content of new cells is simply added to existing cells with the same name."
) +
gsi::enum_const ("OverwriteCell", db::CommonReader::OverwriteCell,
"@brief The old cell is overwritten entirely (including child cells which are not used otherwise)\n"
) +
gsi::enum_const ("SkipNewCell", db::CommonReader::SkipNewCell,
"@brief The new cell is skipped entirely (including child cells which are not used otherwise)\n"
) +
gsi::enum_const ("RenameCell", db::CommonReader::RenameCell,
"@brief The new cell will be renamed to become unique\n"
),
"@brief This enum specifies how cell conflicts are handled if a layout read into another layout and a cell name conflict arises. "
"Until version 0.26.8 and before, the mode was always 'AddToCell'. On reading, a cell was 'reopened' when encountering a cell name "
"which already existed. This mode is still the default. The other modes are made available to support other ways of merging layouts.\n"
"\n"
"Proxy cells are never modified in the existing layout. Proxy cells are always local to their layout file. So if the existing cell is "
"a proxy cell, the new cell will be renamed.\n"
"\n"
"If the new or existing cell is a ghost cell, both cells are merged always.\n"
"\n"
"This enum was introduced in version 0.27.\n"
);
// Inject the NetlistCrossReference::Status declarations into NetlistCrossReference:
gsi::ClassExt<db::LoadLayoutOptions> inject_CellConflictResolution_in_parent (decl_dbCommonReader_CellConflictResolution.defs ());
}

View File

@ -1028,11 +1028,12 @@ Class<db::Layout> decl_Layout ("db", "Layout",
"This method has been introduced in version 0.24."
) +
gsi::method_ext ("set_property", &set_layout_property, gsi::arg ("key"), gsi::arg ("value"),
"@brief Set the user property with the given key to the given value\n"
"@brief Sets the user property with the given key to the given value\n"
"This method is a convenience method that sets the property with the given key to the given value. "
"If no property with that key exists, it will create one. Using that method is more "
"convenient than creating a new property set with a new ID and assigning that properties ID.\n"
"This method may change the properties ID. "
"Note: GDS only supports integer keys. OASIS supports numeric and string keys. "
"\n"
"This method has been introduced in version 0.24."
) +

View File

@ -1283,6 +1283,7 @@ Class<db::Shape> decl_Shape ("db", "Shape",
"If no property with that key exists, it will create one. Using that method is more "
"convenient than creating a new property set with a new ID and assigning that properties ID.\n"
"This method may change the properties ID. "
"Note: GDS only supports integer keys. OASIS supports numeric and string keys. "
"Calling this method will invalidate any iterators. It should not be called inside a "
"loop iterating over shapes.\n"
"\n"

View File

@ -0,0 +1,73 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "dbD25TechnologyComponent.h"
#include "tlUnitTest.h"
TEST(1)
{
db::D25TechnologyComponent comp;
comp.compile_from_source ("1/0: 1.0 1.5 # a comment");
EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5");
comp.compile_from_source ("1/0: zstart=1.0 zstop=1.5");
EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5");
comp.compile_from_source ("1/0: zstart=1.0 height=0.5");
EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5");
comp.compile_from_source ("1/0: 1.0 height=0.5");
EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5");
comp.compile_from_source ("1/0: zstop=1.5 height=0.5");
EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5");
comp.compile_from_source ("1/0: zstart=1.0 zstop=1.5\nname: height=3");
EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5\nname: zstart=1.5, zstop=4.5");
comp.compile_from_source ("1/0: zstart=1.0 zstop=1.5\nname: zstart=4.0 height=3\n\n# a comment line");
EXPECT_EQ (comp.to_string (), "1/0: zstart=1, zstop=1.5\nname: zstart=4, zstop=7");
try {
comp.compile_from_source ("blabla");
EXPECT_EQ (false, true);
} catch (...) { }
try {
comp.compile_from_source ("1/0: 1 2 3");
EXPECT_EQ (false, true);
} catch (...) { }
try {
comp.compile_from_source ("1/0: foo=1 bar=2");
EXPECT_EQ (false, true);
} catch (...) { }
try {
comp.compile_from_source ("1/0: 1;2");
EXPECT_EQ (false, true);
} catch (...) { }
}

View File

@ -606,8 +606,8 @@ TEST(BasicAnd9)
// from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than
// 6. And the shapes from top inside the ring are not seen by the RING's subject shapes.
"TOP[1] 0 insts, 0 shapes (1 times)\n"
"RING[1] 0 insts, 0 shapes (1 times)\n"
"CHILD1[1] 0 insts, 4 shapes (2 times)\n"
"RING[1] 0 insts, 0 shapes (1 times)\n"
);
}
@ -622,8 +622,8 @@ TEST(BasicNot9)
// from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than
// 6. And the shapes from top inside the ring are not seen by the RING's subject shapes.
"TOP[1] 0 insts, 0 shapes (1 times)\n"
"RING[1] 0 insts, 0 shapes (1 times)\n"
"CHILD1[1] 0 insts, 4 shapes (2 times)\n"
"RING[1] 0 insts, 0 shapes (1 times)\n"
);
}
@ -746,8 +746,8 @@ TEST(BasicAndWithSize9)
// from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than
// 6. And the shapes from top inside the ring are not seen by the RING's subject shapes.
"TOP[1] 0 insts, 0 shapes (1 times)\n"
"RING[1] 0 insts, 0 shapes (1 times)\n"
"CHILD1[1] 0 insts, 6 shapes (2 times)\n"
"RING[1] 0 insts, 0 shapes (1 times)\n"
);
}
@ -762,8 +762,8 @@ TEST(BasicNotWithSize9)
// from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than
// 6. And the shapes from top inside the ring are not seen by the RING's subject shapes.
"TOP[1] 0 insts, 0 shapes (1 times)\n"
"RING[1] 0 insts, 0 shapes (1 times)\n"
"CHILD1[1] 0 insts, 6 shapes (2 times)\n"
"RING[1] 0 insts, 0 shapes (1 times)\n"
);
}
@ -898,8 +898,8 @@ TEST(TwoInputsAnd9)
// from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than
// 6. And the shapes from top inside the ring are not seen by the RING's subject shapes.
"TOP[1] 0 insts, 0 shapes (1 times)\n"
"RING[1] 1 insts, 0 shapes (1 times)\n"
"CHILD1[1] 0 insts, 4 shapes (2 times)\n"
"RING[1] 1 insts, 0 shapes (1 times)\n"
);
}
@ -914,8 +914,8 @@ TEST(TwoInputsNot9)
// from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than
// 6. And the shapes from top inside the ring are not seen by the RING's subject shapes.
"TOP[1] 0 insts, 0 shapes (1 times)\n"
"RING[1] 1 insts, 0 shapes (1 times)\n"
"CHILD1[1] 0 insts, 4 shapes (2 times)\n"
"RING[1] 1 insts, 0 shapes (1 times)\n"
);
}
@ -1038,8 +1038,8 @@ TEST(TwoInputsAndWithSize9)
// from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than
// 6. And the shapes from top inside the ring are not seen by the RING's subject shapes.
"TOP[1] 0 insts, 0 shapes (1 times)\n"
"RING[1] 1 insts, 0 shapes (1 times)\n"
"CHILD1[1] 0 insts, 6 shapes (2 times)\n"
"RING[1] 1 insts, 0 shapes (1 times)\n"
);
}
@ -1054,8 +1054,8 @@ TEST(TwoInputsNotWithSize9)
// from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than
// 6. And the shapes from top inside the ring are not seen by the RING's subject shapes.
"TOP[1] 0 insts, 0 shapes (1 times)\n"
"RING[1] 1 insts, 0 shapes (1 times)\n"
"CHILD1[1] 0 insts, 6 shapes (2 times)\n"
"RING[1] 1 insts, 0 shapes (1 times)\n"
);
}
@ -1130,8 +1130,8 @@ TEST(BasicSelfOverlap9)
// from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than
// 6. And the shapes from top inside the ring are not seen by the RING's subject shapes.
"TOP[1] 0 insts, 0 shapes (1 times)\n"
"RING[1] 0 insts, 1 shapes (1 times)\n"
"CHILD1[1] 0 insts, 4 shapes (2 times)\n"
"RING[1] 0 insts, 1 shapes (1 times)\n"
);
}
@ -1200,8 +1200,8 @@ TEST(BasicSelfOverlapWithSize9)
// from atop the CHILD cell don't interact with shapes inside CHILD, so there are 4 shapes rather than
// 6. And the shapes from top inside the ring are not seen by the RING's subject shapes.
"TOP[1] 0 insts, 0 shapes (1 times)\n"
"RING[1] 0 insts, 1 shapes (1 times)\n"
"CHILD1[1] 0 insts, 6 shapes (2 times)\n"
"RING[1] 0 insts, 1 shapes (1 times)\n"
);
}

View File

@ -3156,3 +3156,232 @@ TEST(12_FlattenCircuitDoesFlattenLayout)
db::compare_layouts (_this, ly, au);
}
TEST(13_JoinNets)
{
db::Layout ly;
db::LayerMap lmap;
unsigned int nwell = define_layer (ly, lmap, 1);
unsigned int active = define_layer (ly, lmap, 2);
unsigned int pplus = define_layer (ly, lmap, 10);
unsigned int nplus = define_layer (ly, lmap, 11);
unsigned int poly = define_layer (ly, lmap, 3);
unsigned int poly_lbl = define_layer (ly, lmap, 3, 1);
unsigned int diff_cont = define_layer (ly, lmap, 4);
unsigned int poly_cont = define_layer (ly, lmap, 5);
unsigned int metal1 = define_layer (ly, lmap, 6);
unsigned int metal1_lbl = define_layer (ly, lmap, 6, 1);
unsigned int via1 = define_layer (ly, lmap, 7);
unsigned int metal2 = define_layer (ly, lmap, 8);
unsigned int metal2_lbl = define_layer (ly, lmap, 8, 1);
{
db::LoadLayoutOptions options;
options.get_options<db::CommonReaderOptions> ().layer_map = lmap;
options.get_options<db::CommonReaderOptions> ().create_other_layers = false;
std::string fn (tl::testsrc ());
fn = tl::combine_path (fn, "testdata");
fn = tl::combine_path (fn, "algo");
fn = tl::combine_path (fn, "device_extract_l13.gds");
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly, options);
}
db::Cell &tc = ly.cell (*ly.begin_top_down ());
db::LayoutToNetlist l2n (db::RecursiveShapeIterator (ly, tc, std::set<unsigned int> ()));
std::auto_ptr<db::Region> rbulk (l2n.make_layer ("bulk"));
std::auto_ptr<db::Region> rnwell (l2n.make_layer (nwell, "nwell"));
std::auto_ptr<db::Region> ractive (l2n.make_layer (active, "active"));
std::auto_ptr<db::Region> rpplus (l2n.make_layer (pplus, "pplus"));
std::auto_ptr<db::Region> rnplus (l2n.make_layer (nplus, "nplus"));
std::auto_ptr<db::Region> rpoly (l2n.make_polygon_layer (poly, "poly"));
std::auto_ptr<db::Texts> rpoly_lbl (l2n.make_text_layer (poly_lbl, "poly_lbl"));
std::auto_ptr<db::Region> rdiff_cont (l2n.make_polygon_layer (diff_cont, "diff_cont"));
std::auto_ptr<db::Region> rpoly_cont (l2n.make_polygon_layer (poly_cont, "poly_cont"));
std::auto_ptr<db::Region> rmetal1 (l2n.make_polygon_layer (metal1, "metal1"));
std::auto_ptr<db::Texts> rmetal1_lbl (l2n.make_text_layer (metal1_lbl, "metal1_lbl"));
std::auto_ptr<db::Region> rvia1 (l2n.make_polygon_layer (via1, "via1"));
std::auto_ptr<db::Region> rmetal2 (l2n.make_polygon_layer (metal2, "metal2"));
std::auto_ptr<db::Texts> rmetal2_lbl (l2n.make_text_layer (metal2_lbl, "metal2_lbl"));
// derived regions
db::Region ractive_in_nwell = *ractive & *rnwell;
db::Region rpactive = ractive_in_nwell & *rpplus;
db::Region rntie = ractive_in_nwell & *rnplus;
db::Region rpgate = rpactive & *rpoly;
db::Region rpsd = rpactive - rpgate;
db::Region ractive_outside_nwell = *ractive - *rnwell;
db::Region rnactive = ractive_outside_nwell & *rnplus;
db::Region rptie = ractive_outside_nwell & *rpplus;
db::Region rngate = rnactive & *rpoly;
db::Region rnsd = rnactive - rngate;
// return the computed layers into the original layout and write it for debugging purposes
unsigned int lgate = ly.insert_layer (db::LayerProperties (20, 0)); // 20/0 -> Gate
unsigned int lsd = ly.insert_layer (db::LayerProperties (21, 0)); // 21/0 -> Source/Drain
unsigned int lpdiff = ly.insert_layer (db::LayerProperties (22, 0)); // 22/0 -> P Diffusion
unsigned int lndiff = ly.insert_layer (db::LayerProperties (23, 0)); // 23/0 -> N Diffusion
unsigned int lptie = ly.insert_layer (db::LayerProperties (24, 0)); // 24/0 -> P Tie
unsigned int lntie = ly.insert_layer (db::LayerProperties (25, 0)); // 25/0 -> N Tie
rpgate.insert_into (&ly, tc.cell_index (), lgate);
rngate.insert_into (&ly, tc.cell_index (), lgate);
rpsd.insert_into (&ly, tc.cell_index (), lsd);
rnsd.insert_into (&ly, tc.cell_index (), lsd);
rpsd.insert_into (&ly, tc.cell_index (), lpdiff);
rnsd.insert_into (&ly, tc.cell_index (), lndiff);
rpsd.insert_into (&ly, tc.cell_index (), lptie);
rnsd.insert_into (&ly, tc.cell_index (), lntie);
db::NetlistDeviceExtractorMOS4Transistor pmos_ex ("PMOS");
db::NetlistDeviceExtractorMOS4Transistor nmos_ex ("NMOS");
// device extraction
db::NetlistDeviceExtractor::input_layers dl;
dl["SD"] = &rpsd;
dl["G"] = &rpgate;
dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes
dl["W"] = rnwell.get ();
l2n.extract_devices (pmos_ex, dl);
dl["SD"] = &rnsd;
dl["G"] = &rngate;
dl["P"] = rpoly.get (); // not needed for extraction but to return terminal shapes
dl["W"] = rbulk.get ();
l2n.extract_devices (nmos_ex, dl);
// net extraction
l2n.register_layer (rpsd, "psd");
l2n.register_layer (rnsd, "nsd");
l2n.register_layer (rptie, "ptie");
l2n.register_layer (rntie, "ntie");
// Intra-layer
l2n.connect (rpsd);
l2n.connect (rnsd);
l2n.connect (*rnwell);
l2n.connect (*rpoly);
l2n.connect (*rdiff_cont);
l2n.connect (*rpoly_cont);
l2n.connect (*rmetal1);
l2n.connect (*rvia1);
l2n.connect (*rmetal2);
l2n.connect (rptie);
l2n.connect (rntie);
// Inter-layer
l2n.connect (rpsd, *rdiff_cont);
l2n.connect (rnsd, *rdiff_cont);
l2n.connect (*rpoly, *rpoly_cont);
l2n.connect (*rpoly_cont, *rmetal1);
l2n.connect (*rdiff_cont, *rmetal1);
l2n.connect (*rdiff_cont, rptie);
l2n.connect (*rdiff_cont, rntie);
l2n.connect (*rnwell, rntie);
l2n.connect (*rmetal1, *rvia1);
l2n.connect (*rvia1, *rmetal2);
l2n.connect (*rpoly, *rpoly_lbl); // attaches labels
l2n.connect (*rmetal1, *rmetal1_lbl); // attaches labels
l2n.connect (*rmetal2, *rmetal2_lbl); // attaches labels
// Global
l2n.connect_global (rntie, "VDD");
l2n.connect_global (*rnwell, "VDD");
l2n.connect_global (rptie, "VSS");
l2n.connect_global (*rbulk, "VSS");
// Extract with joining VSS and VDD
l2n.extract_netlist ("{VSS,VDD}");
// debug layers produced for nets
// 201/0 -> Well
// 203/0 -> Poly
// 204/0 -> Diffusion contacts
// 205/0 -> Poly contacts
// 206/0 -> Metal1
// 207/0 -> Via1
// 208/0 -> Metal2
// 210/0 -> N source/drain
// 211/0 -> P source/drain
// 212/0 -> N tie
// 213/0 -> P tie
std::map<const db::Region *, unsigned int> dump_map;
dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (210, 0));
dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (211, 0));
dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (212, 0));
dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (213, 0));
dump_map [rbulk.get () ] = ly.insert_layer (db::LayerProperties (214, 0));
dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (201, 0));
dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (203, 0));
dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (204, 0));
dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (205, 0));
dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (206, 0));
dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (207, 0));
dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (208, 0));
// write nets to layout
db::CellMapping cm = l2n.cell_mapping_into (ly, tc);
dump_nets_to_layout (l2n, ly, dump_map, cm);
dump_map.clear ();
dump_map [&rpsd ] = ly.insert_layer (db::LayerProperties (310, 0));
dump_map [&rnsd ] = ly.insert_layer (db::LayerProperties (311, 0));
dump_map [&rptie ] = ly.insert_layer (db::LayerProperties (312, 0));
dump_map [&rntie ] = ly.insert_layer (db::LayerProperties (313, 0));
dump_map [rbulk.get () ] = ly.insert_layer (db::LayerProperties (314, 0));
dump_map [rnwell.get () ] = ly.insert_layer (db::LayerProperties (301, 0));
dump_map [rpoly.get () ] = ly.insert_layer (db::LayerProperties (303, 0));
dump_map [rdiff_cont.get ()] = ly.insert_layer (db::LayerProperties (304, 0));
dump_map [rpoly_cont.get ()] = ly.insert_layer (db::LayerProperties (305, 0));
dump_map [rmetal1.get () ] = ly.insert_layer (db::LayerProperties (306, 0));
dump_map [rvia1.get () ] = ly.insert_layer (db::LayerProperties (307, 0));
dump_map [rmetal2.get () ] = ly.insert_layer (db::LayerProperties (308, 0));
dump_recursive_nets_to_layout (l2n, ly, dump_map, cm);
// compare netlist as string
CHECKPOINT ();
db::compare_netlist (_this, *l2n.netlist (),
"circuit RINGO ();\n"
" subcircuit INV2 $1 (IN=$I7,$2=FB,OUT=OSC,$4=VSS,$5=VDD,VDD=VDD,VSS=VSS);\n"
" subcircuit INV2 $2 (IN=FB,$2=$I34,OUT=$I17,$4=VSS,$5=VDD,VDD=VDD,VSS=VSS);\n"
" subcircuit INV2 $3 (IN=$3,$2=$I38,OUT=$I4,$4=VSS,$5=VDD,VDD=VDD,VSS=VSS);\n"
" subcircuit INV2 $4 (IN=$I2,$2=$I37,OUT=$3,$4=VSS,$5=VDD,VDD=VDD,VSS=VSS);\n"
" subcircuit INV2 $5 (IN=$I4,$2=$I39,OUT=$I5,$4=VSS,$5=VDD,VDD=VDD,VSS=VSS);\n"
" subcircuit INV2 $6 (IN=$I5,$2=$I40,OUT=$I6,$4=VSS,$5=VDD,VDD=VDD,VSS=VSS);\n"
" subcircuit INV2 $7 (IN=$I6,$2=$I41,OUT=$I7,$4=VSS,$5=VDD,VDD=VDD,VSS=VSS);\n"
" subcircuit INV2 $8 (IN=$I17,$2=$I35,OUT=$I1,$4=VSS,$5=VDD,VDD=VDD,VSS=VSS);\n"
" subcircuit INV2 $9 (IN=$I1,$2=$I36,OUT=$I2,$4=VSS,$5=VDD,VDD=VDD,VSS=VSS);\n"
"end;\n"
"circuit INV2 (IN=IN,$2=$2,OUT=OUT,$4=$4,$5=$5,VDD=VDD,VSS=VSS);\n"
" device PMOS $1 (S=$2,G=IN,D=$5,B=VDD) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n"
" device PMOS $2 (S=$5,G=$2,D=OUT,B=VDD) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n"
" device NMOS $3 (S=$2,G=IN,D=$4,B=VSS) (L=0.25,W=0.95,AS=0.49875,AD=0.26125,PS=2.95,PD=1.5);\n"
" device NMOS $4 (S=$4,G=$2,D=OUT,B=VSS) (L=0.25,W=0.95,AS=0.26125,AD=0.49875,PS=1.5,PD=2.95);\n"
" subcircuit TRANS $1 ($1=$2,$2=$4,$3=IN);\n"
" subcircuit TRANS $2 ($1=$2,$2=$5,$3=IN);\n"
" subcircuit TRANS $3 ($1=$5,$2=OUT,$3=$2);\n"
" subcircuit TRANS $4 ($1=$4,$2=OUT,$3=$2);\n"
"end;\n"
"circuit TRANS ($1=$1,$2=$2,$3=$3);\n"
"end;\n"
);
// compare the collected test data
std::string au = tl::testsrc ();
au = tl::combine_path (au, "testdata");
au = tl::combine_path (au, "algo");
au = tl::combine_path (au, "device_extract_au13_circuits.gds");
db::compare_layouts (_this, ly, au);
}

View File

@ -359,28 +359,75 @@ TEST(9_DeviceMultipliers)
std::string path = tl::combine_path (tl::combine_path (tl::combine_path (tl::testsrc (), "testdata"), "algo"), "nreader9.cir");
db::NetlistSpiceReader reader;
tl::InputStream is (path);
reader.read (is, nl);
{
db::NetlistSpiceReader reader;
tl::InputStream is (path);
reader.read (is, nl);
std::string nl_string = nl.to_string ();
// normalization of exponential representation:
nl_string = tl::replaced (nl_string, "e-009", "e-09");
std::string nl_string = nl.to_string ();
// normalization of exponential representation:
nl_string = tl::replaced (nl_string, "e-009", "e-09");
EXPECT_EQ (nl_string,
"circuit .TOP ();\n"
" device RES $1 (A='1',B='2') (R=850,L=0,W=0,A=0,P=0);\n"
" device RES $2 (A='3',B='4') (R=1700,L=0,W=0,A=0,P=0);\n"
" device NMOS $1 (S='1',G='2',D='3',B='4') (L=7,W=4,AS=0,AD=0,PS=0,PD=0);\n"
" device PMOS $2 (S='1',G='2',D='3',B='4') (L=7,W=2,AS=0,AD=0,PS=0,PD=0);\n"
" device CAP $1 (A='1',B='2') (C=2e-09,A=0,P=0);\n"
" device CAP $2 (A='3',B='4') (C=1e-09,A=0,P=0);\n"
" device DIODE $1 (A='1',C='2') (A=20,P=0);\n"
" device DIODE $2 (A='3',C='4') (A=10,P=0);\n"
" device BIP $1 (C='1',B='2',E='3',S='4') (AE=20,PE=0,AB=0,PB=0,AC=0,PC=0,NE=1);\n"
" device BIP $2 (C='1',B='2',E='3',S='4') (AE=10,PE=0,AB=0,PB=0,AC=0,PC=0,NE=1);\n"
"end;\n"
);
EXPECT_EQ (nl_string,
"circuit .TOP ();\n"
" device RES $1 (A='1',B='2') (R=850,L=0,W=0,A=0,P=0);\n"
" device RES $2 (A='3',B='4') (R=1700,L=0,W=0,A=0,P=0);\n"
" device RMODEL $3 (A='1',B='2') (R=850,L=0,W=0,A=0,P=0);\n"
" device RMODEL $4 (A='3',B='4') (R=1700,L=0,W=0,A=0,P=0);\n"
" device NMOS $1 (S='1',G='2',D='3',B='4') (L=7,W=4,AS=0,AD=0,PS=0,PD=0);\n"
" device PMOS $2 (S='1',G='2',D='3',B='4') (L=7,W=2,AS=0,AD=0,PS=0,PD=0);\n"
" device CAP $1 (A='1',B='2') (C=2e-09,A=0,P=0);\n"
" device CAP $2 (A='3',B='4') (C=1e-09,A=0,P=0);\n"
" device CMODEL $3 (A='1',B='2') (C=2e-09,A=0,P=0);\n"
" device CMODEL $4 (A='3',B='4') (C=1e-09,A=0,P=0);\n"
" device IND $1 (A='1',B='2') (L=5e-10);\n"
" device IND $2 (A='3',B='4') (L=1e-09);\n"
" device LMODEL $3 (A='1',B='2') (L=5e-10);\n"
" device LMODEL $4 (A='3',B='4') (L=1e-09);\n"
" device DIODE $1 (A='1',C='2') (A=20,P=0);\n"
" device DIODE $2 (A='3',C='4') (A=10,P=0);\n"
" device BIP $1 (C='1',B='2',E='3',S='4') (AE=20,PE=0,AB=0,PB=0,AC=0,PC=0,NE=1);\n"
" device BIP $2 (C='1',B='2',E='3',S='4') (AE=10,PE=0,AB=0,PB=0,AC=0,PC=0,NE=1);\n"
"end;\n"
);
}
db::Circuit *top = nl.circuit_by_name (".TOP");
nl.remove_circuit (top);
// read once again, this time with known classes (must not trigger issue-652)
{
db::NetlistSpiceReader reader;
tl::InputStream is (path);
reader.read (is, nl);
std::string nl_string = nl.to_string ();
// normalization of exponential representation:
nl_string = tl::replaced (nl_string, "e-009", "e-09");
EXPECT_EQ (nl_string,
"circuit .TOP ();\n"
" device RES $1 (A='1',B='2') (R=850,L=0,W=0,A=0,P=0);\n"
" device RES $2 (A='3',B='4') (R=1700,L=0,W=0,A=0,P=0);\n"
" device RMODEL $3 (A='1',B='2') (R=850,L=0,W=0,A=0,P=0);\n"
" device RMODEL $4 (A='3',B='4') (R=1700,L=0,W=0,A=0,P=0);\n"
" device NMOS $1 (S='1',G='2',D='3',B='4') (L=7,W=4,AS=0,AD=0,PS=0,PD=0);\n"
" device PMOS $2 (S='1',G='2',D='3',B='4') (L=7,W=2,AS=0,AD=0,PS=0,PD=0);\n"
" device CAP $1 (A='1',B='2') (C=2e-09,A=0,P=0);\n"
" device CAP $2 (A='3',B='4') (C=1e-09,A=0,P=0);\n"
" device CMODEL $3 (A='1',B='2') (C=2e-09,A=0,P=0);\n"
" device CMODEL $4 (A='3',B='4') (C=1e-09,A=0,P=0);\n"
" device IND $1 (A='1',B='2') (L=5e-10);\n"
" device IND $2 (A='3',B='4') (L=1e-09);\n"
" device LMODEL $3 (A='1',B='2') (L=5e-10);\n"
" device LMODEL $4 (A='3',B='4') (L=1e-09);\n"
" device DIODE $1 (A='1',C='2') (A=20,P=0);\n"
" device DIODE $2 (A='3',C='4') (A=10,P=0);\n"
" device BIP $1 (C='1',B='2',E='3',S='4') (AE=20,PE=0,AB=0,PB=0,AC=0,PC=0,NE=1);\n"
" device BIP $2 (C='1',B='2',E='3',S='4') (AE=10,PE=0,AB=0,PB=0,AC=0,PC=0,NE=1);\n"
"end;\n"
);
}
}
TEST(10_SubcircuitsNoPins)

View File

@ -34,6 +34,7 @@ SOURCES = \
dbPolygonToolsTests.cc \
dbTechnologyTests.cc \
dbStreamLayerTests.cc \
dbD25TechnologyComponentTests.cc \
dbVectorTests.cc \
dbVariableWidthPathTests.cc \
dbTransTests.cc \

View File

@ -1352,7 +1352,7 @@ module DRC
# @/code
#
# (Technically, the cheat code block is a Ruby Proc and cannot create variables
# outside it's scope. Hence the results of this code block have to be passed
# outside its scope. Hence the results of this code block have to be passed
# through the "cheat" method).
#
# To apply cheats for device extraction, use the following scheme:
@ -2003,7 +2003,7 @@ CODE
sel.each do |s|
if s == "-"
iter.unselect_cells(cell.cell_index)
iter.unselect_cells([cell_index])
elsif s == "-*"
iter.unselect_all_cells
elsif s == "+*"

View File

@ -162,7 +162,7 @@ module DRC
# @li \global#capacitor_with_bulk - A capacitor with a separate bulk terminal @/li
# @/ul
#
# Each device class (e.g. n-MOS/p-MOS or high Vt/low Vt) needs it's own instance
# Each device class (e.g. n-MOS/p-MOS or high Vt/low Vt) needs its own instance
# of device extractor. The device extractor beside the algorithm and specific
# extraction settings defines the name of the device to be built.
#

View File

@ -22,14 +22,27 @@ module DRC
@layout_var = layout_var
@path = path
@cell = cell
@inside = nil
@box = nil
@layers = nil
@sel = []
@clip = false
@overlapping = false
@tmp_layers = []
end
# Conceptual deep copy (not including the temp layers)
def dup
d = DRCSource::new(@engine, @layout, @layout_var, @cell, @path)
d._init_internal(@box ? @box.dup : nil, @sel.dup, @clip, @overlapping)
d
end
# internal copy initialization
def _init_internal(box, sel, clip, overlapping)
@box = box
@sel = sel
@clip = clip
@overlapping = overlapping
end
# %DRC%
# @name layout
@ -139,19 +152,34 @@ module DRC
# code:
#
# @code
# layout_with_selection = layout.select("-TOP", "+B")
# l1 = layout_with_selection.input(1, 0)
# layout_with_selection = source.select("-TOP", "+B")
# l1 = source.input(1, 0)
# ...
# @/code
#
# Please note that the sample above will deliver the children of "B" because there is
# nothing said about how to proceed with cells other than "TOP" or "B".
# The following code will just select "B" without it's children, because in the
# nothing said about how to proceed with cells other than "TOP" or "B". Conceptually,
# the instantiation path of a cell will be matched against the different filters in the
# order they are given.
# A matching negative expression will disable the cell, a matching positive expression
# will enable the cell. Hence, every cell that has a "B" in the instantiation path
# is enabled.
#
# The following code will just select "B" without its children, because in the
# first "-*" selection, all cells including the children of "B" are disabled:
#
# @code
# layout_with_selection = layout.select("-*", "+B")
# l1 = layout_with_selection.input(1, 0)
# layout_with_selection = source.select("-*", "+B")
# l1 = source.input(1, 0)
# ...
# @/code
#
# The short form "-" will disable the top cell. This code is identical to the first example
# and will start with a disabled top cell regardless of its name:
#
# @code
# layout_with_selection = source.select("-", "+B")
# l1 = source.input(1, 0)
# ...
# @/code

View File

@ -756,6 +756,47 @@ TEST(16_issue570)
db::compare_layouts (_this, layout, au, db::NoNormalization);
}
// Problems with Source#select
TEST(17_issue570)
{
std::string rs = tl::testsrc ();
rs += "/testdata/drc/drcSimpleTests_17.drc";
std::string input = tl::testsrc ();
input += "/testdata/drc/drcSimpleTests_17.gds";
std::string au = tl::testsrc ();
au += "/testdata/drc/drcSimpleTests_au17.gds";
std::string output = this->tmp_file ("tmp.gds");
{
// Set some variables
lym::Macro config;
config.set_text (tl::sprintf (
"$drc_test_source = '%s'\n"
"$drc_test_target = '%s'\n"
, input, output)
);
config.set_interpreter (lym::Macro::Ruby);
EXPECT_EQ (config.run (), 0);
}
lym::Macro drc;
drc.load_from (rs);
EXPECT_EQ (drc.run (), 0);
db::Layout layout;
{
tl::InputStream stream (output);
db::Reader reader (stream);
reader.read (layout);
}
db::compare_layouts (_this, layout, au, db::NoNormalization);
}
TEST(18_forget)
{
std::string rs = tl::testsrc ();

View File

@ -1,7 +1,8 @@
<ui version="4.0" >
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AlignOptionsDialog</class>
<widget class="QDialog" name="AlignOptionsDialog" >
<property name="geometry" >
<widget class="QDialog" name="AlignOptionsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
@ -9,26 +10,26 @@
<height>392</height>
</rect>
</property>
<property name="windowTitle" >
<property name="windowTitle">
<string>Alignment Options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" >
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox" >
<property name="title" >
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Horizontal alignment</string>
</property>
<layout class="QGridLayout" name="gridLayout_3" >
<item row="0" column="0" >
<widget class="QRadioButton" name="h_none_rb" >
<property name="text" >
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QRadioButton" name="h_none_rb">
<property name="text">
<string/>
</property>
<property name="icon" >
<iconset resource="layResources.qrc" >
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/align_none.png</normaloff>:/align_none.png</iconset>
</property>
<property name="iconSize" >
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
@ -36,16 +37,16 @@
</property>
</widget>
</item>
<item row="0" column="2" >
<widget class="QRadioButton" name="h_left_rb" >
<property name="text" >
<item row="0" column="2">
<widget class="QRadioButton" name="h_left_rb">
<property name="text">
<string/>
</property>
<property name="icon" >
<iconset resource="layResources.qrc" >
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/align_left.png</normaloff>:/align_left.png</iconset>
</property>
<property name="iconSize" >
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
@ -53,16 +54,16 @@
</property>
</widget>
</item>
<item row="0" column="4" >
<widget class="QRadioButton" name="h_center_rb" >
<property name="text" >
<item row="0" column="4">
<widget class="QRadioButton" name="h_center_rb">
<property name="text">
<string/>
</property>
<property name="icon" >
<iconset resource="layResources.qrc" >
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/align_hcenter.png</normaloff>:/align_hcenter.png</iconset>
</property>
<property name="iconSize" >
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
@ -70,16 +71,16 @@
</property>
</widget>
</item>
<item row="0" column="6" >
<widget class="QRadioButton" name="h_right_rb" >
<property name="text" >
<item row="0" column="6">
<widget class="QRadioButton" name="h_right_rb">
<property name="text">
<string/>
</property>
<property name="icon" >
<iconset resource="layResources.qrc" >
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/align_right.png</normaloff>:/align_right.png</iconset>
</property>
<property name="iconSize" >
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
@ -87,12 +88,12 @@
</property>
</widget>
</item>
<item row="0" column="7" >
<spacer name="horizontalSpacer_3" >
<property name="orientation" >
<item row="0" column="7">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0" >
<property name="sizeHint" stdset="0">
<size>
<width>243</width>
<height>20</height>
@ -100,55 +101,55 @@
</property>
</spacer>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="label_5" >
<property name="text" >
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>none</string>
</property>
<property name="alignment" >
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="2" >
<widget class="QLabel" name="label_6" >
<property name="text" >
<item row="1" column="2">
<widget class="QLabel" name="label_6">
<property name="text">
<string>left</string>
</property>
<property name="alignment" >
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="4" >
<widget class="QLabel" name="label_7" >
<property name="text" >
<item row="1" column="4">
<widget class="QLabel" name="label_7">
<property name="text">
<string>center</string>
</property>
<property name="alignment" >
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="6" >
<widget class="QLabel" name="label_8" >
<property name="text" >
<item row="1" column="6">
<widget class="QLabel" name="label_8">
<property name="text">
<string>right</string>
</property>
<property name="alignment" >
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1" >
<spacer name="horizontalSpacer" >
<property name="orientation" >
<item row="0" column="1">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType" >
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0" >
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
@ -156,15 +157,15 @@
</property>
</spacer>
</item>
<item row="0" column="3" >
<spacer name="horizontalSpacer_4" >
<property name="orientation" >
<item row="0" column="3">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType" >
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0" >
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
@ -172,15 +173,15 @@
</property>
</spacer>
</item>
<item row="0" column="5" >
<spacer name="horizontalSpacer_5" >
<property name="orientation" >
<item row="0" column="5">
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType" >
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0" >
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
@ -192,21 +193,21 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2" >
<property name="title" >
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Vertical alignment</string>
</property>
<layout class="QGridLayout" name="gridLayout_2" >
<item row="0" column="0" >
<widget class="QRadioButton" name="v_none_rb" >
<property name="text" >
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QRadioButton" name="v_none_rb">
<property name="text">
<string/>
</property>
<property name="icon" >
<iconset resource="layResources.qrc" >
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/align_none.png</normaloff>:/align_none.png</iconset>
</property>
<property name="iconSize" >
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
@ -214,16 +215,16 @@
</property>
</widget>
</item>
<item row="0" column="2" >
<widget class="QRadioButton" name="v_top_rb" >
<property name="text" >
<item row="0" column="2">
<widget class="QRadioButton" name="v_top_rb">
<property name="text">
<string/>
</property>
<property name="icon" >
<iconset resource="layResources.qrc" >
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/align_top.png</normaloff>:/align_top.png</iconset>
</property>
<property name="iconSize" >
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
@ -231,16 +232,16 @@
</property>
</widget>
</item>
<item row="0" column="4" >
<widget class="QRadioButton" name="v_center_rb" >
<property name="text" >
<item row="0" column="4">
<widget class="QRadioButton" name="v_center_rb">
<property name="text">
<string/>
</property>
<property name="icon" >
<iconset resource="layResources.qrc" >
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/align_vcenter.png</normaloff>:/align_vcenter.png</iconset>
</property>
<property name="iconSize" >
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
@ -248,12 +249,12 @@
</property>
</widget>
</item>
<item row="0" column="7" >
<spacer name="horizontalSpacer_2" >
<property name="orientation" >
<item row="0" column="7">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0" >
<property name="sizeHint" stdset="0">
<size>
<width>243</width>
<height>34</height>
@ -261,56 +262,56 @@
</property>
</spacer>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="label" >
<property name="text" >
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>none</string>
</property>
<property name="alignment" >
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="2" >
<widget class="QLabel" name="label_2" >
<property name="text" >
<item row="1" column="2">
<widget class="QLabel" name="label_2">
<property name="text">
<string>top</string>
</property>
<property name="alignment" >
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="4" >
<widget class="QLabel" name="label_3" >
<property name="text" >
<item row="1" column="4">
<widget class="QLabel" name="label_3">
<property name="text">
<string>center</string>
</property>
<property name="alignment" >
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="6" >
<widget class="QLabel" name="label_4" >
<property name="text" >
<item row="1" column="6">
<widget class="QLabel" name="label_4">
<property name="text">
<string>bottom</string>
</property>
<property name="alignment" >
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="6" >
<widget class="QRadioButton" name="v_bottom_rb" >
<property name="text" >
<item row="0" column="6">
<widget class="QRadioButton" name="v_bottom_rb">
<property name="text">
<string/>
</property>
<property name="icon" >
<iconset resource="layResources.qrc" >
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/align_bottom.png</normaloff>:/align_bottom.png</iconset>
</property>
<property name="iconSize" >
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
@ -318,15 +319,15 @@
</property>
</widget>
</item>
<item row="0" column="1" >
<spacer name="horizontalSpacer_6" >
<property name="orientation" >
<item row="0" column="1">
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType" >
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0" >
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
@ -334,15 +335,15 @@
</property>
</spacer>
</item>
<item row="0" column="3" >
<spacer name="horizontalSpacer_7" >
<property name="orientation" >
<item row="0" column="3">
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType" >
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0" >
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
@ -350,15 +351,15 @@
</property>
</spacer>
</item>
<item row="0" column="5" >
<spacer name="horizontalSpacer_8" >
<property name="orientation" >
<item row="0" column="5">
<spacer name="horizontalSpacer_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType" >
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0" >
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
@ -370,21 +371,21 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3" >
<property name="title" >
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Layers for alignment of instances</string>
</property>
<layout class="QGridLayout" name="gridLayout" >
<item row="1" column="0" >
<widget class="QRadioButton" name="visible_layers_rb" >
<property name="text" >
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QRadioButton" name="visible_layers_rb">
<property name="text">
<string>Use visible layers only</string>
</property>
</widget>
</item>
<item row="0" column="0" >
<widget class="QRadioButton" name="all_layers_rb" >
<property name="text" >
<item row="0" column="0">
<widget class="QRadioButton" name="all_layers_rb">
<property name="text">
<string>Use all layers</string>
</property>
</widget>
@ -393,11 +394,11 @@
</widget>
</item>
<item>
<spacer name="verticalSpacer" >
<property name="orientation" >
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0" >
<property name="sizeHint" stdset="0">
<size>
<width>488</width>
<height>16</height>
@ -406,11 +407,11 @@
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox" >
<property name="orientation" >
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons" >
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
@ -431,7 +432,7 @@
<tabstop>buttonBox</tabstop>
</tabstops>
<resources>
<include location="layResources.qrc" />
<include location="../../lay/lay/layResources.qrc"/>
</resources>
<connections>
<connection>
@ -440,11 +441,11 @@
<receiver>AlignOptionsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel" >
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel" >
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
@ -456,11 +457,11 @@
<receiver>AlignOptionsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel" >
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel" >
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>

View File

@ -0,0 +1,715 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DistributeOptionsDialog</class>
<widget class="QDialog" name="DistributeOptionsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>689</width>
<height>574</height>
</rect>
</property>
<property name="windowTitle">
<string>Distribution Options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="h_distribute">
<property name="title">
<string>Horizontal distribution</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="label_9">
<property name="text">
<string>The pitch specifies the offset at which the objects are placed relative to each other. The space is the minimum distance between the objects.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_10">
<property name="text">
<string>Pitch</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="h_pitch">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_12">
<property name="text">
<string>µm</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_9">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_11">
<property name="text">
<string>Space</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="h_space">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_13">
<property name="text">
<string>µm</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="v_distribute">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Vertical distribution</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_18">
<property name="text">
<string>Pitch</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="v_pitch">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_19">
<property name="text">
<string>µm</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_12">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_20">
<property name="text">
<string>Space</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="v_space">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_21">
<property name="text">
<string>µm</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_13">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_22">
<property name="text">
<string>The pitch specifies the offset at which the objects are placed relative to each other. The space is the minimum distance between the objects.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Horizonal alignment</string>
</property>
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<widget class="QRadioButton" name="h_none_rb">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/align_none.png</normaloff>:/align_none.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item row="0" column="1">
<spacer name="horizontalSpacer_14">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="2">
<widget class="QRadioButton" name="h_left_rb">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/align_left.png</normaloff>:/align_left.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item row="0" column="3">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="4">
<widget class="QRadioButton" name="h_center_rb">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/align_hcenter.png</normaloff>:/align_hcenter.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item row="0" column="5">
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="6">
<widget class="QRadioButton" name="h_right_rb">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/align_right.png</normaloff>:/align_right.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item row="0" column="7">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>245</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_23">
<property name="text">
<string>none</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_6">
<property name="text">
<string>left</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QLabel" name="label_7">
<property name="text">
<string>center</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="6">
<widget class="QLabel" name="label_8">
<property name="text">
<string>right</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Vertical alignment</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QRadioButton" name="v_none_rb">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/align_none.png</normaloff>:/align_none.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item row="0" column="1">
<spacer name="horizontalSpacer_15">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="2">
<widget class="QRadioButton" name="v_top_rb">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/align_top.png</normaloff>:/align_top.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item row="0" column="3">
<spacer name="horizontalSpacer_8">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="4">
<widget class="QRadioButton" name="v_center_rb">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/align_vcenter.png</normaloff>:/align_vcenter.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item row="0" column="5">
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="6">
<widget class="QRadioButton" name="v_bottom_rb">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/align_bottom.png</normaloff>:/align_bottom.png</iconset>
</property>
<property name="iconSize">
<size>
<width>32</width>
<height>32</height>
</size>
</property>
</widget>
</item>
<item row="0" column="7">
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>245</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>none</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_14">
<property name="text">
<string>top</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QLabel" name="label_15">
<property name="text">
<string>center</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="6">
<widget class="QLabel" name="label_17">
<property name="text">
<string>bottom</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>For the computation of cell instance bounding boxes ...</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QRadioButton" name="visible_layers_rb">
<property name="text">
<string>Use visible layers only</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QRadioButton" name="all_layers_rb">
<property name="text">
<string>Use all layers</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>488</width>
<height>16</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>all_layers_rb</tabstop>
<tabstop>visible_layers_rb</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources>
<include location="../../lay/lay/layResources.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>DistributeOptionsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>DistributeOptionsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -1,139 +0,0 @@
<ui version="4.0" >
<class>EditorOptionsDialog</class>
<widget class="QDialog" name="EditorOptionsDialog" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>642</width>
<height>572</height>
</rect>
</property>
<property name="windowTitle" >
<string>Object Editor Options</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QTabWidget" name="pages" >
<property name="currentIndex" >
<number>0</number>
</property>
<widget class="QWidget" name="tab_2" >
<attribute name="title" >
<string>Tab 2</string>
</attribute>
</widget>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType" >
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" >
<size>
<width>20</width>
<height>8</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QFrame" name="frame" >
<property name="frameShape" >
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow" >
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="ok_pb" >
<property name="text" >
<string>Ok</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="apply_pb" >
<property name="text" >
<string>Apply</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancel_pb" >
<property name="text" >
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>cancel_pb</sender>
<signal>clicked()</signal>
<receiver>EditorOptionsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel" >
<x>506</x>
<y>388</y>
</hint>
<hint type="destinationlabel" >
<x>276</x>
<y>205</y>
</hint>
</hints>
</connection>
<connection>
<sender>ok_pb</sender>
<signal>clicked()</signal>
<receiver>EditorOptionsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel" >
<x>344</x>
<y>388</y>
</hint>
<hint type="destinationlabel" >
<x>276</x>
<y>205</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -6,10 +6,16 @@
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>417</height>
<width>400</width>
<height>446</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Ignored">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
@ -18,387 +24,449 @@
<number>6</number>
</property>
<property name="leftMargin">
<number>9</number>
<number>0</number>
</property>
<property name="topMargin">
<number>9</number>
<number>0</number>
</property>
<property name="rightMargin">
<number>9</number>
<number>0</number>
</property>
<property name="bottomMargin">
<number>9</number>
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Snapping</string>
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<layout class="QGridLayout">
<property name="leftMargin">
<number>9</number>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>446</height>
</rect>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="0" column="4">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>148</width>
<height>16</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="2">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>16</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="3">
<widget class="QLineEdit" name="edit_grid_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Enter the grid in micron. Can be anisotropic (&quot;gx,gy&quot;)</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Grid</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Objects </string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="grid_cb">
<item>
<property name="text">
<string>No grid</string>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Snapping</string>
</property>
</item>
<item>
<property name="text">
<string>Global grid</string>
<layout class="QGridLayout" name="_5">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Grid</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="grid_cb">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<item>
<property name="text">
<string>No grid</string>
</property>
</item>
<item>
<property name="text">
<string>Global grid</string>
</property>
</item>
<item>
<property name="text">
<string>Other grid ...</string>
</property>
</item>
</widget>
</item>
<item row="1" column="1" colspan="3">
<widget class="QCheckBox" name="snap_objects_cbx">
<property name="text">
<string>Snap to other objects</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Objects </string>
</property>
</widget>
</item>
<item row="0" column="3">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>148</width>
<height>16</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="edit_grid_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Enter the grid in micron. Can be anisotropic (&quot;gx,gy&quot;)</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Angle Constraints</string>
</property>
</item>
<item>
<property name="text">
<string>Other grid ...</string>
<layout class="QGridLayout" name="_2">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Movements </string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="move_angle_cb">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<item>
<property name="text">
<string>Any Direction</string>
</property>
</item>
<item>
<property name="text">
<string>Diagonal</string>
</property>
</item>
<item>
<property name="text">
<string>Manhattan</string>
</property>
</item>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Connections </string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="conn_angle_cb">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<item>
<property name="text">
<string>Any Angle</string>
</property>
</item>
<item>
<property name="text">
<string>Diagonal</string>
</property>
</item>
<item>
<property name="text">
<string>Manhattan</string>
</property>
</item>
</widget>
</item>
<item row="1" column="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Hierarchical Features</string>
</property>
</item>
</widget>
</item>
<item row="1" column="1" colspan="4">
<widget class="QCheckBox" name="snap_objects_cbx">
<property name="text">
<string>Snap to other objects</string>
</property>
</widget>
</item>
</layout>
<layout class="QGridLayout" name="_3">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Copy mode</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Shallow select</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QComboBox" name="hier_copy_mode_cbx">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<item>
<property name="text">
<string>Shallow mode (instance only)</string>
</property>
</item>
<item>
<property name="text">
<string>Deep mode (instance and cell)</string>
</property>
</item>
<item>
<property name="text">
<string>Ask</string>
</property>
</item>
</widget>
</item>
<item row="1" column="3">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1" colspan="3">
<widget class="QCheckBox" name="hier_sel_cbx">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Select top level objects only</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Instance Display</string>
</property>
<layout class="QHBoxLayout" name="_4">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item>
<widget class="QCheckBox" name="show_shapes_cbx">
<property name="text">
<string>Show shapes while moving (max.</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="max_shapes_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_13">
<property name="text">
<string> shapes)</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="spacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>121</width>
<height>70</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Angle Constraints</string>
</property>
<layout class="QGridLayout">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Movements </string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Connections </string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="conn_angle_cb">
<item>
<property name="text">
<string>Any Angle</string>
</property>
</item>
<item>
<property name="text">
<string>Diagonal</string>
</property>
</item>
<item>
<property name="text">
<string>Manhattan</string>
</property>
</item>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="move_angle_cb">
<item>
<property name="text">
<string>Any Direction</string>
</property>
</item>
<item>
<property name="text">
<string>Diagonal</string>
</property>
</item>
<item>
<property name="text">
<string>Manhattan</string>
</property>
</item>
</widget>
</item>
<item row="0" column="2" rowspan="2">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>257</width>
<height>41</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Hierarchical Features</string>
</property>
<layout class="QGridLayout">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Copy mode</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Shallow select</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QComboBox" name="hier_copy_mode_cbx">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContentsOnFirstShow</enum>
</property>
<item>
<property name="text">
<string>Shallow mode (instance only)</string>
</property>
</item>
<item>
<property name="text">
<string>Deep mode (instance and cell)</string>
</property>
</item>
<item>
<property name="text">
<string>Ask</string>
</property>
</item>
</widget>
</item>
<item row="1" column="3">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1" colspan="3">
<widget class="QCheckBox" name="hier_sel_cbx">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Select top level objects only</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Instance Display</string>
</property>
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item>
<widget class="QCheckBox" name="show_shapes_cbx">
<property name="text">
<string>Show shapes when moving (max.</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="max_shapes_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_13">
<property name="text">
<string>shapes)</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>121</width>
<height>51</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<tabstops>
<tabstop>scrollArea</tabstop>
<tabstop>grid_cb</tabstop>
<tabstop>edit_grid_le</tabstop>
<tabstop>snap_objects_cbx</tabstop>
<tabstop>conn_angle_cb</tabstop>
<tabstop>move_angle_cb</tabstop>
<tabstop>hier_sel_cbx</tabstop>
<tabstop>hier_copy_mode_cbx</tabstop>
<tabstop>show_shapes_cbx</tabstop>
<tabstop>max_shapes_le</tabstop>
</tabstops>

View File

@ -6,10 +6,16 @@
<rect>
<x>0</x>
<y>0</y>
<width>680</width>
<height>574</height>
<width>358</width>
<height>496</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Ignored">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
@ -18,163 +24,218 @@
<number>6</number>
</property>
<property name="leftMargin">
<number>9</number>
<number>0</number>
</property>
<property name="topMargin">
<number>9</number>
<number>0</number>
</property>
<property name="rightMargin">
<number>9</number>
<number>0</number>
</property>
<property name="bottomMargin">
<number>9</number>
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame">
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
<enum>QFrame::Plain</enum>
</property>
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>358</width>
<height>496</height>
</rect>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Cell </string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="cell_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="browse_pb">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_13">
<property name="text">
<string> Library </string>
</property>
</widget>
</item>
<item>
<widget class="lay::LibrarySelectionComboBox" name="lib_cbx">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>522</width>
<height>8</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QTabWidget" name="param_tab_widget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Geometry</string>
</attribute>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>9</number>
<number>4</number>
</property>
<property name="topMargin">
<number>9</number>
<number>4</number>
</property>
<property name="rightMargin">
<number>9</number>
<number>4</number>
</property>
<property name="bottomMargin">
<number>9</number>
<number>4</number>
</property>
<item>
<property name="spacing">
<number>2</number>
</property>
<item row="0" column="0">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Library</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="lay::LibrarySelectionComboBox" name="lib_cbx">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Cell </string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QToolButton" name="browse_pb">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/find.png</normaloff>:/find.png</iconset>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="cell_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="clearButtonEnabled">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<spacer name="spacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>4</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="place_origin_cb">
<property name="text">
<string>Place origin of cell</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Rotation / Scaling</string>
</property>
<layout class="QGridLayout">
<layout class="QGridLayout" name="_2">
<property name="leftMargin">
<number>9</number>
<number>4</number>
</property>
<property name="topMargin">
<number>9</number>
<number>4</number>
</property>
<property name="rightMargin">
<number>9</number>
<number>4</number>
</property>
<property name="bottomMargin">
<number>9</number>
<number>4</number>
</property>
<property name="spacing">
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="0" column="1">
<widget class="QLineEdit" name="scale_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@ -194,14 +255,14 @@
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Scaling factor (magnification)</string>
<string>Scaling factor</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="angle_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@ -229,10 +290,17 @@
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_17">
<property name="text">
<string>(magnification)</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<item row="5" column="0">
<widget class="QGroupBox" name="array_grp">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@ -246,33 +314,36 @@
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QGridLayout">
<layout class="QGridLayout" name="_3">
<property name="leftMargin">
<number>9</number>
<number>4</number>
</property>
<property name="topMargin">
<number>9</number>
<number>4</number>
</property>
<property name="rightMargin">
<number>9</number>
<number>4</number>
</property>
<property name="bottomMargin">
<number>9</number>
<number>4</number>
</property>
<property name="spacing">
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="2" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string> Column vector (x,y)</string>
<string> Column step</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLineEdit" name="column_x_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@ -292,7 +363,7 @@
<item row="2" column="4">
<widget class="QLineEdit" name="column_y_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@ -312,7 +383,7 @@
<item row="1" column="4">
<widget class="QLineEdit" name="row_y_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@ -332,7 +403,7 @@
<item row="1" column="2">
<widget class="QLineEdit" name="row_x_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@ -352,14 +423,14 @@
<item row="1" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string> Row vector (x,y)</string>
<string> Row step</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string> Rows/Columns</string>
<string>Dimension</string>
</property>
</widget>
</item>
@ -368,12 +439,15 @@
<property name="text">
<string>columns =</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QLineEdit" name="columns_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@ -385,74 +459,89 @@
<property name="text">
<string>rows = </string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="rows_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item row="3" column="0" colspan="5">
<item row="4" column="0" colspan="5">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Warning: although row and column vectors can be arbitrary combination,
some design systems only accept orthogonal (rectangular) arrays.</string>
<string>Warning: although row and column vectors can be arbitrary combination, some design systems only accept orthogonal (rectangular) arrays.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>4</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<spacer>
<item row="6" column="0">
<spacer name="spacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
<height>120</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>4</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="pcell_tab">
<attribute name="title">
<string>PCell</string>
</attribute>
</widget>
</widget>
</item>
<item>
<widget class="QCheckBox" name="place_origin_cb">
<property name="text">
<string>Place origin of cell</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>50</width>
<height>8</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
@ -462,22 +551,8 @@ some design systems only accept orthogonal (rectangular) arrays.</string>
<header>layWidgets.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>cell_le</tabstop>
<tabstop>browse_pb</tabstop>
<tabstop>lib_cbx</tabstop>
<tabstop>param_tab_widget</tabstop>
<tabstop>scale_le</tabstop>
<tabstop>angle_le</tabstop>
<tabstop>mirror_cbx</tabstop>
<tabstop>rows_le</tabstop>
<tabstop>columns_le</tabstop>
<tabstop>row_x_le</tabstop>
<tabstop>row_y_le</tabstop>
<tabstop>column_x_le</tabstop>
<tabstop>column_y_le</tabstop>
<tabstop>place_origin_cb</tabstop>
</tabstops>
<resources/>
<resources>
<include location="../../lay/lay/layResources.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditorOptionsInstPCellParam</class>
<widget class="QWidget" name="EditorOptionsInstPCellParam">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>358</width>
<height>481</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Ignored">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -1,7 +1,8 @@
<ui version="4.0" >
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditorOptionsPath</class>
<widget class="QWidget" name="EditorOptionsPath" >
<property name="geometry" >
<widget class="QWidget" name="EditorOptionsPath">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
@ -9,228 +10,264 @@
<height>289</height>
</rect>
</property>
<property name="windowTitle" >
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame_2" >
<property name="frameShape" >
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow" >
<enum>QFrame::Raised</enum>
<property name="widgetResizable">
<bool>true</bool>
</property>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>357</width>
<height>289</height>
</rect>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QLabel" name="label_2" >
<property name="text" >
<string> Width </string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="width_le" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>0</hsizetype>
<vsizetype>0</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3" >
<property name="text" >
<string>micron</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>21</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="_2">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Width</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="width_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>µm</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>21</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Extensions</string>
</property>
<layout class="QGridLayout" name="_3">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="2" column="1">
<widget class="QLabel" name="label_8">
<property name="text">
<string> end =</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="start_ext_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QLabel" name="label_5">
<property name="text">
<string>µm</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLineEdit" name="end_ext_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QComboBox" name="type_cb">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Flush</string>
</property>
</item>
<item>
<property name="text">
<string>Square</string>
</property>
</item>
<item>
<property name="text">
<string>Variable</string>
</property>
</item>
<item>
<property name="text">
<string>Round</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Variable </string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QLabel" name="label_4">
<property name="text">
<string>µm</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_6">
<property name="text">
<string>start =</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Type</string>
</property>
</widget>
</item>
<item row="1" column="4">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<spacer name="spacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>271</width>
<height>123</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>5</hsizetype>
<vsizetype>0</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title" >
<string>Extensions</string>
</property>
<layout class="QGridLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item rowspan="3" row="0" column="4" >
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>21</width>
<height>81</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="3" >
<widget class="QLabel" name="label_5" >
<property name="text" >
<string>micron</string>
</property>
</widget>
</item>
<item row="1" column="3" >
<widget class="QLabel" name="label_4" >
<property name="text" >
<string>micron</string>
</property>
</widget>
</item>
<item row="2" column="2" >
<widget class="QLineEdit" name="end_ext_le" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>0</hsizetype>
<vsizetype>0</vsizetype>
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="1" >
<widget class="QLabel" name="label_8" >
<property name="text" >
<string> end =</string>
</property>
</widget>
</item>
<item row="1" column="1" >
<widget class="QLabel" name="label_6" >
<property name="text" >
<string>start =</string>
</property>
</widget>
</item>
<item row="1" column="2" >
<widget class="QLineEdit" name="start_ext_le" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>0</hsizetype>
<vsizetype>0</vsizetype>
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="label_7" >
<property name="text" >
<string>Variable </string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2" >
<widget class="QComboBox" name="type_cb" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>5</hsizetype>
<vsizetype>0</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text" >
<string>Flush</string>
</property>
</item>
<item>
<property name="text" >
<string>Square</string>
</property>
</item>
<item>
<property name="text" >
<string>Variable</string>
</property>
</item>
<item>
<property name="text" >
<string>Round</string>
</property>
</item>
</widget>
</item>
<item row="0" column="0" >
<widget class="QLabel" name="label" >
<property name="text" >
<string>Type</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<size>
<width>271</width>
<height>63</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<tabstops>
<tabstop>width_le</tabstop>
<tabstop>type_cb</tabstop>
<tabstop>start_ext_le</tabstop>
<tabstop>end_ext_le</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -1,7 +1,8 @@
<ui version="4.0" >
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditorOptionsText</class>
<widget class="QWidget" name="EditorOptionsText" >
<property name="geometry" >
<widget class="QWidget" name="EditorOptionsText">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
@ -9,225 +10,288 @@
<height>243</height>
</rect>
</property>
<property name="windowTitle" >
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
<layout class="QVBoxLayout">
<property name="spacing">
<number>2</number>
</property>
<property name="spacing" >
<number>6</number>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame" >
<property name="frameShape" >
<widget class="QScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow" >
<enum>QFrame::Raised</enum>
<property name="widgetResizable">
<bool>true</bool>
</property>
<layout class="QGridLayout" >
<property name="margin" >
<number>9</number>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>542</width>
<height>243</height>
</rect>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item row="2" column="0" >
<widget class="QLabel" name="label_4" >
<property name="text" >
<string>Alignment</string>
</property>
</widget>
</item>
<item row="2" column="1" >
<widget class="QLabel" name="label_8" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>0</hsizetype>
<vsizetype>5</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text" >
<string>h =</string>
</property>
</widget>
</item>
<item row="3" column="0" >
<widget class="QLabel" name="Text size" >
<property name="text" >
<string>Text size</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="4" >
<widget class="QLineEdit" name="text_le" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>0</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="0" >
<widget class="QLabel" name="label_2" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>0</hsizetype>
<vsizetype>5</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text" >
<string>Text</string>
</property>
</widget>
</item>
<item row="4" column="1" colspan="4" >
<widget class="QLabel" name="label_3" >
<property name="text" >
<string>Hint: orientation, alignments and size cannot be saved to OASIS files
Enable a vector font and text scaling in the setup dialog
to show text objects scaled and rotated</string>
</property>
</widget>
</item>
<item row="3" column="1" colspan="2" >
<widget class="QLineEdit" name="size_le" />
</item>
<item row="3" column="3" colspan="2" >
<widget class="QLabel" name="label" >
<property name="text" >
<string>(Leave empty for default)</string>
</property>
</widget>
</item>
<item row="2" column="4" >
<widget class="QComboBox" name="valign_cbx" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>5</hsizetype>
<vsizetype>0</vsizetype>
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text" >
<string>(Default)</string>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
</item>
<item>
<property name="text" >
<string>Top</string>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</item>
<item>
<property name="text" >
<string>Center</string>
<layout class="QGridLayout" name="_2">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<item row="2" column="3">
<widget class="QLabel" name="label_9">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>v =</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="5">
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="0">
<widget class="QLabel" name="Text size">
<property name="text">
<string>Text size</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Text</string>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QComboBox" name="valign_cbx">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>(Default)</string>
</property>
</item>
<item>
<property name="text">
<string>Top</string>
</property>
</item>
<item>
<property name="text">
<string>Center</string>
</property>
</item>
<item>
<property name="text">
<string>Bottom</string>
</property>
</item>
</widget>
</item>
<item row="2" column="2">
<widget class="QComboBox" name="halign_cbx">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>(Default)</string>
</property>
</item>
<item>
<property name="text">
<string>Left</string>
</property>
</item>
<item>
<property name="text">
<string>Center</string>
</property>
</item>
<item>
<property name="text">
<string>Right</string>
</property>
</item>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="label_8">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>h =</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Alignment</string>
</property>
</widget>
</item>
<item row="2" column="5">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1" colspan="5">
<widget class="QLineEdit" name="text_le">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="4" column="1" colspan="5">
<widget class="QFrame" name="frame_2">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="size_le"/>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>(empty for default)</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="5" column="1" colspan="5">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Hint: orientation, alignments and size cannot be saved to OASIS files. Enable a vector font and text scaling in the setup dialog to show text objects scaled and rotated.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<spacer name="spacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</item>
<item>
<property name="text" >
<string>Bottom</string>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>77</height>
</size>
</property>
</item>
</widget>
</item>
<item row="2" column="3" >
<widget class="QLabel" name="label_9" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>0</hsizetype>
<vsizetype>5</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text" >
<string>v =</string>
</property>
</widget>
</item>
<item row="2" column="2" >
<widget class="QComboBox" name="halign_cbx" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>5</hsizetype>
<vsizetype>0</vsizetype>
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text" >
<string>(Default)</string>
</property>
</item>
<item>
<property name="text" >
<string>Left</string>
</property>
</item>
<item>
<property name="text" >
<string>Center</string>
</property>
</item>
<item>
<property name="text" >
<string>Right</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0" colspan="5" >
<spacer>
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType" >
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" >
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
</layout>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<tabstops>
<tabstop>text_le</tabstop>
<tabstop>halign_cbx</tabstop>
<tabstop>valign_cbx</tabstop>
<tabstop>size_le</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -1,73 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PCellParametersDialog</class>
<widget class="QDialog" name="PCellParametersDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>469</width>
<height>429</height>
</rect>
</property>
<property name="windowTitle">
<string>PCell Parameters</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="edt::PCellParametersPage" name="parameters" native="true"/>
</item>
<item>
<widget class="QDialogButtonBox" name="buttons">
<property name="standardButtons">
<set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
<class>edt::PCellParametersPage</class>
<extends>QWidget</extends>
<header>edtPCellParametersPage.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>buttons</sender>
<signal>rejected()</signal>
<receiver>PCellParametersDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>321</x>
<y>405</y>
</hint>
<hint type="destinationlabel">
<x>337</x>
<y>423</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttons</sender>
<signal>accepted()</signal>
<receiver>PCellParametersDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>427</x>
<y>405</y>
</hint>
<hint type="destinationlabel">
<x>443</x>
<y>425</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -21,7 +21,8 @@ HEADERS = \
edtServiceImpl.h \
edtUtils.h \
edtCommon.h \
edtPCellParametersDialog.h
edtDistribute.h \
edtRecentConfigurationPage.h
FORMS = \
AlignOptionsDialog.ui \
@ -29,7 +30,6 @@ FORMS = \
CopyModeDialog.ui \
ChangeLayerOptionsDialog.ui \
EditablePathPropertiesPage.ui \
EditorOptionsDialog.ui \
EditorOptionsGeneric.ui \
EditorOptionsInst.ui \
EditorOptionsPath.ui \
@ -42,7 +42,8 @@ FORMS = \
PolygonPropertiesPage.ui \
RoundCornerOptionsDialog.ui \
TextPropertiesPage.ui \
PCellParametersDialog.ui
DistributeOptionsDialog.ui \
EditorOptionsInstPCellParam.ui
SOURCES = \
edtConfig.cc \
@ -59,7 +60,8 @@ SOURCES = \
edtServiceImpl.cc \
edtUtils.cc \
gsiDeclEdt.cc \
edtPCellParametersDialog.cc
edtDistribute.cc \
edtRecentConfigurationPage.cc
INCLUDEPATH += $$TL_INC $$GSI_INC $$LAYBASIC_INC $$DB_INC
DEPENDPATH += $$TL_INC $$GSI_INC $$LAYBASIC_INC $$DB_INC

View File

@ -326,6 +326,93 @@ AlignOptionsDialog::exec_dialog (lay::LayoutView * /*view*/, int &hmode, int &vm
}
}
// --------------------------------------------------------------------------------
// DistributeOptionsDialog implementation
DistributeOptionsDialog::DistributeOptionsDialog (QWidget *parent)
: QDialog (parent)
{
setObjectName (QString::fromUtf8 ("change_layer_options_dialog"));
Ui::DistributeOptionsDialog::setupUi (this);
}
DistributeOptionsDialog::~DistributeOptionsDialog ()
{
// .. nothing yet ..
}
bool
DistributeOptionsDialog::exec_dialog (lay::LayoutView * /*view*/, bool &hdistribute, int &hmode, double &hpitch, double &hspace, bool &vdistribute, int &vmode, double &vpitch, double &vspace, bool &visible_layers)
{
QRadioButton *hmode_buttons [] = { this->h_none_rb, this->h_left_rb, this->h_center_rb, this->h_right_rb };
QRadioButton *vmode_buttons [] = { this->v_none_rb, this->v_top_rb, this->v_center_rb, this->v_bottom_rb };
QRadioButton *layers_buttons [] = { this->all_layers_rb, this->visible_layers_rb };
this->h_distribute->setChecked (hdistribute);
for (int i = 1; i < 4; ++i) {
hmode_buttons [i]->setChecked (hmode == i);
}
this->h_space->setText (tl::to_qstring (tl::micron_to_string (hspace)));
this->h_pitch->setText (tl::to_qstring (tl::micron_to_string (hpitch)));
this->v_distribute->setChecked (vdistribute);
for (int i = 1; i < 4; ++i) {
vmode_buttons [i]->setChecked (vmode == i);
}
this->v_space->setText (tl::to_qstring (tl::micron_to_string (vspace)));
this->v_pitch->setText (tl::to_qstring (tl::micron_to_string (vpitch)));
for (int i = 0; i < 2; ++i) {
layers_buttons [i]->setChecked (int (visible_layers) == i);
}
if (QDialog::exec ()) {
hdistribute = this->h_distribute->isChecked ();
hmode = -1;
for (int i = 1; i < 4; ++i) {
if (hmode_buttons [i]->isChecked ()) {
hmode = i;
}
}
hspace = 0.0;
tl::from_string (tl::to_string (this->h_space->text ()), hspace);
hpitch = 0.0;
tl::from_string (tl::to_string (this->h_pitch->text ()), hpitch);
vdistribute = this->v_distribute->isChecked ();
vmode = -1;
for (int i = 1; i < 4; ++i) {
if (vmode_buttons [i]->isChecked ()) {
vmode = i;
}
}
vspace = 0.0;
tl::from_string (tl::to_string (this->v_space->text ()), vspace);
vpitch = 0.0;
tl::from_string (tl::to_string (this->v_pitch->text ()), vpitch);
visible_layers = false;
for (int i = 0; i < 2; ++i) {
if (layers_buttons [i]->isChecked ()) {
visible_layers = (i != 0);
}
}
return true;
} else {
return false;
}
}
// --------------------------------------------------------------------------------
// MakeCellOptionsDialog implementation

View File

@ -37,6 +37,7 @@
#include "ui_InstantiationForm.h"
#include "ui_ChangeLayerOptionsDialog.h"
#include "ui_AlignOptionsDialog.h"
#include "ui_DistributeOptionsDialog.h"
#include "ui_CopyModeDialog.h"
#include "ui_MakeCellOptionsDialog.h"
#include "ui_MakeArrayOptionsDialog.h"
@ -127,6 +128,22 @@ public:
bool exec_dialog (lay::LayoutView *view, int &hmode, int &vmode, bool &visible_layers);
};
/**
* @brief Distribute function options dialog
*/
class DistributeOptionsDialog
: public QDialog,
public Ui::DistributeOptionsDialog
{
Q_OBJECT
public:
DistributeOptionsDialog (QWidget *parent);
virtual ~DistributeOptionsDialog ();
bool exec_dialog (lay::LayoutView *view, bool &hdistribute, int &hmode, double &hpitch, double &hspace, bool &vdistribute, int &vmode, double &vpitch, double &vspace, bool &visible_layers);
};
/**
* @brief Options dialog for the "make cell" function
*/

View File

@ -0,0 +1,27 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "edtDistribute.h"
// .. nothing yet (all in header) ..

476
src/edt/edt/edtDistribute.h Normal file
View File

@ -0,0 +1,476 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_edtDistribute
#define HDR_edtDistribute
#include "dbBox.h"
#include "dbTypes.h"
#include "tlIntervalMap.h"
namespace edt
{
/**
* @brief Gets the box position by reference position
*/
template <class Box, bool horizontally>
typename Box::coord_type box_position (const Box &box, int ref)
{
if (horizontally) {
if (ref < 0) {
return box.left ();
} else if (ref == 0) {
return box.center ().x ();
} else {
return box.right ();
}
} else {
if (ref < 0) {
return box.bottom ();
} else if (ref == 0) {
return box.center ().y ();
} else {
return box.top ();
}
}
}
/**
* @brief Compares boxes by their reference position
*/
template <class Box, class Value, bool horizontally>
class box_compare
{
public:
typedef typename Box::coord_type coord_type;
box_compare (int ref)
: m_ref (ref)
{
// .. nothing yet ..
}
bool operator() (const std::pair<Box, Value> &a, const std::pair<Box, Value> &b) const
{
coord_type ca = box_position<Box, horizontally> (a.first, m_ref);
coord_type cb = box_position<Box, horizontally> (b.first, m_ref);
if (! db::coord_traits<coord_type>::equal (ca, cb)) {
return db::coord_traits<coord_type>::less (ca, cb);
} else {
coord_type ca2 = box_position<Box, !horizontally> (a.first, m_ref);
coord_type cb2 = box_position<Box, !horizontally> (b.first, m_ref);
return db::coord_traits<coord_type>::less (ca2, cb2);
}
}
private:
int m_ref;
};
/**
* @brief Does some heuristic binning of coordinates
*/
template <class Box, bool horizontally>
void do_bin (typename std::vector<std::pair<Box, size_t> >::const_iterator b, typename std::vector<std::pair<Box, size_t> >::const_iterator e, int ref, std::vector<std::vector<size_t> > &bins)
{
typedef typename Box::coord_type coord_type;
// determine maximum distance between adjacent coordinates
coord_type max_dist = 0;
for (typename std::vector<std::pair<Box, size_t> >::const_iterator i = b + 1; i != e; ++i) {
max_dist = std::max (max_dist, box_position<Box, horizontally> (i->first, ref) - box_position<Box, horizontally> ((i - 1)->first, ref));
}
// heuristically, everything that has a distance of less than 1/3 of the maximum distance falls into one bin
coord_type bin_start = box_position<Box, horizontally> (b->first, ref);
bins.push_back (std::vector<size_t> ());
bins.back ().push_back (b->second);
coord_type thr = max_dist / 3;
for (typename std::vector<std::pair<Box, size_t> >::const_iterator i = b + 1; i != e; ++i) {
coord_type c = box_position<Box, horizontally> (i->first, ref);
if (c - bin_start > thr) {
// start a new bin
bins.push_back (std::vector<size_t> ());
bin_start = c;
}
bins.back ().push_back (i->second);
}
}
/**
* @brief Computes the effective box width (rounded to pitch, space added)
*/
template <class Box, bool horizontal>
inline typename Box::coord_type eff_dim (const Box &box, typename Box::coord_type pitch, typename Box::coord_type space)
{
typedef typename Box::coord_type coord_type;
coord_type d = (horizontal ? box.width () : box.height ()) + space;
if (pitch > 0) {
d = db::coord_traits<coord_type>::rounded (ceil (double (d) / double (pitch) - 1e-10) * double (pitch));
}
return d;
}
template <class Coord>
struct max_coord_join_op
{
void operator() (Coord &a, const Coord &b) const
{
a = std::max (a, b);
}
};
/**
* @brief Implements an algorithm for 2d-distributing rectangular objects
*/
template <class Box, class Value>
class distributed_placer
{
public:
typedef typename Box::coord_type coord_type;
typedef std::vector<std::pair<Box, Value> > objects;
typedef typename objects::const_iterator iterator;
/**
* @brief Constructor
*/
distributed_placer ()
{
// .. nothing yet ..
}
/**
* @brief Reserves space for n objects
*/
void reserve (size_t n)
{
m_objects.reserve (n);
}
/**
* @brief Inserts a new object
*/
void insert (const Box &box, const Value &value)
{
tl_assert (! box.empty ());
m_objects.push_back (std::make_pair (box, value));
}
/**
* @brief Stored objects iterator: begin
*/
iterator begin () const
{
return m_objects.begin ();
}
/**
* @brief Stored objects iterator: end
*/
iterator end () const
{
return m_objects.end ();
}
/**
* @brief Distributes the stored objects in vertical direction only
*
* @param ref The reference location (-1: bottom, 0: center, 1: top)
* @param refp The alignment in the other (horizontal) direction (-1: left, 0: center, 1: right, other: leave as is)
* @param pitch The distribution pitch (grid) or 0 for no pitch
* @param space The minimum space between the objects
*/
void distribute_v (int ref, int refp, coord_type pitch, coord_type space)
{
do_distribute_1d<false> (ref, refp, pitch, space);
}
/**
* @brief Distributes the stored objects in horizontal direction only
*
* @param ref The reference location (-1: left, 0: center, 1: right)
* @param refp The alignment in the other (vertical) direction (-1: bottom, 0: center, 1: top, other: leave as is)
* @param pitch The distribution pitch (grid) or 0 for no pitch
* @param space The minimum space between the objects
*/
void distribute_h (int ref, int refp, coord_type pitch, coord_type space)
{
do_distribute_1d<true> (ref, refp, pitch, space);
}
/**
* @brief Distributes the stored objects in horizontal and vertical direction
*
* @param href The horizontal reference location (-1: left, 0: center, 1: right)
* @param hpitch The horizontal distribution pitch (grid) or 0 for no pitch
* @param hspace The horizontal minimum space between the objects
* @param vref The vertical reference location (-1: bottom, 0: center, 1: top)
* @param vpitch The vertical distribution pitch (grid) or 0 for no pitch
* @param vspace The vertical minimum space between the objects
*/
void distribute_matrix (int href, coord_type hpitch, coord_type hspace, int vref, coord_type vpitch, coord_type vspace)
{
if (m_objects.size () < 2) {
return;
}
// The algorithm is this:
// 1.) Bin the boxes according to their positions in horizontal and vertical direction.
// This forms the potential columns and rows
// 2.) Compute the row and column widths and heights as the maximum of their content
// 3.) position the objects inside these cells
std::vector<std::pair<Box, size_t> > indexed_boxes;
indexed_boxes.reserve (m_objects.size ());
Box all;
for (typename objects::iterator i = m_objects.begin (); i != m_objects.end (); ++i) {
all += i->first;
}
size_t n = 0;
for (typename objects::iterator i = m_objects.begin (); i != m_objects.end (); ++i, ++n) {
indexed_boxes.push_back (std::make_pair (i->first, n));
}
std::vector<std::vector<size_t> > hbins, vbins;
std::sort (indexed_boxes.begin (), indexed_boxes.end (), box_compare<Box, size_t, true> (href));
do_bin<Box, true> (indexed_boxes.begin (), indexed_boxes.end (), href, hbins);
std::sort (indexed_boxes.begin (), indexed_boxes.end (), box_compare<Box, size_t, false> (vref));
do_bin<Box, false> (indexed_boxes.begin (), indexed_boxes.end (), vref, vbins);
// rewrite the bins to cell occupation lists
std::vector<std::vector<std::vector<size_t> > > cells;
cells.resize (hbins.size ());
for (size_t i = 0; i < hbins.size (); ++i) {
cells [i].resize (vbins.size ());
}
{
std::vector<size_t> hbin_for_index;
hbin_for_index.resize (indexed_boxes.size (), size_t (0));
for (std::vector<std::vector<size_t> >::const_iterator i = hbins.begin (); i != hbins.end (); ++i) {
for (std::vector<size_t>::const_iterator j = i->begin (); j != i->end (); ++j) {
hbin_for_index [*j] = i - hbins.begin ();
}
}
for (std::vector<std::vector<size_t> >::const_iterator i = vbins.begin (); i != vbins.end (); ++i) {
for (std::vector<size_t>::const_iterator j = i->begin (); j != i->end (); ++j) {
cells [hbin_for_index [*j]][i - vbins.begin ()].push_back (*j);
}
}
}
// initialize the cell widths
std::vector<coord_type> cell_widths, cell_heights;
cell_widths.resize (hbins.size (), 0);
cell_heights.resize (vbins.size (), 0);
// compute the cell widths as the maximum of the content
for (std::vector<std::vector<std::vector<size_t> > >::const_iterator i = cells.begin (); i != cells.end (); ++i) {
for (std::vector<std::vector<size_t> >::const_iterator j = i->begin (); j != i->end (); ++j) {
coord_type wcell = 0, hcell = 0;
for (std::vector<size_t>::const_iterator k = j->begin (); k != j->end (); ++k) {
// NOTE: intra-cell objects are distributed horizontally
wcell += eff_dim<Box, true> (m_objects [*k].first, hpitch, hspace);
hcell = std::max (hcell, eff_dim<Box, false> (m_objects [*k].first, vpitch, vspace));
}
cell_widths [i - cells.begin ()] = std::max (cell_widths [i - cells.begin ()], wcell);
cell_heights [j - i->begin ()] = std::max (cell_heights [j - i->begin ()], hcell);
}
}
// Compute the columns and row positions
std::vector<coord_type> cell_xpos, cell_ypos;
cell_xpos.reserve (cell_widths.size ());
cell_ypos.reserve (cell_heights.size ());
coord_type x = 0, y = 0;
for (typename std::vector<coord_type>::const_iterator i = cell_widths.begin (); i != cell_widths.end (); ++i) {
cell_xpos.push_back (x);
x += *i;
}
for (typename std::vector<coord_type>::const_iterator i = cell_heights.begin (); i != cell_heights.end (); ++i) {
cell_ypos.push_back (y);
y += *i;
}
// Compute the actual coordinates of the objects inside the cells
for (std::vector<std::vector<std::vector<size_t> > >::const_iterator i = cells.begin (); i != cells.end (); ++i) {
for (std::vector<std::vector<size_t> >::const_iterator j = i->begin (); j != i->end (); ++j) {
coord_type wcell = 0;
for (std::vector<size_t>::const_iterator k = j->begin (); k != j->end (); ++k) {
// NOTE: intra-cell objects are distributed horizontally
wcell += eff_dim<Box, true> (m_objects [*k].first, hpitch, hspace);
}
coord_type x = cell_xpos [i - cells.begin ()];
if (href == 0) {
x += (cell_widths [i - cells.begin ()] - wcell) / 2;
} else if (href > 0) {
x += (cell_widths [i - cells.begin ()] - wcell);
}
for (std::vector<size_t>::const_iterator k = j->begin (); k != j->end (); ++k) {
coord_type w = eff_dim<Box, true> (m_objects [*k].first, hpitch, hspace);
coord_type h = eff_dim<Box, false> (m_objects [*k].first, vpitch, vspace);
coord_type y = cell_ypos [j - i->begin ()];
if (vref == 0) {
y += (cell_heights [j - i->begin ()] - h) / 2;
} else if (href > 0) {
y += (cell_heights [j - i->begin ()] - h);
}
m_objects [*k].first.move (db::point<coord_type> (x, y) - m_objects [*k].first.p1 ());
// NOTE: intra-cell objects are distributed horizontally
x += w;
}
}
}
// Final adjustments - align the whole matrix with the original bounding box
Box new_all;
for (typename objects::iterator i = m_objects.begin (); i != m_objects.end (); ++i, ++n) {
new_all += i->first;
}
coord_type dh = box_position<Box, true> (all, href) - box_position<Box, true> (new_all, href);
coord_type dv = box_position<Box, false> (all, vref) - box_position<Box, false> (new_all, vref);
db::vector<coord_type> mv (dh, dv);
for (typename objects::iterator i = m_objects.begin (); i != m_objects.end (); ++i) {
i->first.move (mv);
}
}
private:
objects m_objects;
template <bool horizontally>
void do_distribute_1d (int ref, int refp, coord_type pitch, coord_type space)
{
if (m_objects.size () < 2) {
return;
}
Box all;
for (typename objects::const_iterator i = m_objects.begin () + 1; i != m_objects.end (); ++i) {
all += i->first;
}
std::sort (m_objects.begin (), m_objects.end (), box_compare<Box, Value, horizontally> (ref));
Box current = m_objects.front ().first;
coord_type p0 = box_position<Box, horizontally> (current, ref);
for (typename objects::iterator i = m_objects.begin () + 1; i != m_objects.end (); ++i) {
coord_type p = box_position<Box, horizontally> (i->first, -1);
coord_type offset = box_position<Box, horizontally> (i->first, ref) - p;
coord_type pnew = box_position<Box, horizontally> (current, 1) + space;
if (db::coord_traits<coord_type>::less (0, pitch)) {
pnew = coord_type (ceil (double (pnew + offset - p0) / double (pitch) - 1e-10)) * pitch - offset + p0;
}
db::vector<coord_type> mv;
if (horizontally) {
mv = db::vector<coord_type> (pnew - p, 0);
} else {
mv = db::vector<coord_type> (0, pnew - p);
}
i->first.move (mv);
current = i->first;
}
// final adjustment
Box new_all = m_objects.front ().first + m_objects.back ().first;
db::vector<coord_type> mv;
coord_type d = box_position<Box, horizontally> (all, ref) - box_position<Box, horizontally> (new_all, ref);
if (horizontally) {
mv = db::vector<coord_type> (d, 0);
} else {
mv = db::vector<coord_type> (0, d);
}
for (typename objects::iterator i = m_objects.begin (); i != m_objects.end (); ++i) {
i->first.move (mv);
if (refp >= -1 && refp <= 1) {
coord_type dp = box_position<Box, ! horizontally> (all, refp) - box_position<Box, ! horizontally> (i->first, refp);
db::vector<coord_type> mvp;
if (horizontally) {
mvp = db::vector<coord_type> (0, dp);
} else {
mvp = db::vector<coord_type> (dp, 0);
}
i->first.move (mvp);
}
}
}
};
}
#endif

View File

@ -25,209 +25,67 @@
#include "dbLibrary.h"
#include "dbLibraryManager.h"
#include "dbPCellHeader.h"
#include "edtEditorOptionsPages.h"
#include "edtPCellParametersPage.h"
#include "edtConfig.h"
#include "edtService.h"
#include "edtEditorOptionsPages.h"
#include "edtPropertiesPageUtils.h"
#include "tlExceptions.h"
#include "layPlugin.h"
#include "layLayoutView.h"
#include "layCellSelectionForm.h"
#include "ui_EditorOptionsDialog.h"
#include "layQtTools.h"
#include "ui_EditorOptionsGeneric.h"
#include "ui_EditorOptionsPath.h"
#include "ui_EditorOptionsText.h"
#include "ui_EditorOptionsInst.h"
#include "ui_EditorOptionsInstPCellParam.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QTabWidget>
#include <QToolButton>
#include <QCompleter>
namespace edt
{
// ------------------------------------------------------------------
// EditorOptionsPage implementation
// Configures a value from a line edit
EditorOptionsPage::EditorOptionsPage ()
: mp_owner (0), m_active (true), mp_plugin_declaration (0)
{
// nothing yet ..
}
EditorOptionsPage::~EditorOptionsPage ()
{
set_owner (0);
}
void
EditorOptionsPage::set_owner (EditorOptionsPages *owner)
{
if (mp_owner) {
mp_owner->unregister_page (this);
}
mp_owner = owner;
}
void
EditorOptionsPage::activate (bool active)
{
if (m_active != active) {
m_active = active;
if (mp_owner) {
mp_owner->activate_page (this);
}
}
}
// ------------------------------------------------------------------
// EditorOptionsPages implementation
struct EOPCompareOp
{
bool operator() (edt::EditorOptionsPage *a, edt::EditorOptionsPage *b) const
{
return a->order () < b->order ();
}
};
EditorOptionsPages::EditorOptionsPages (const std::vector<edt::EditorOptionsPage *> &pages, lay::Dispatcher *root)
: mp_root (root)
{
mp_ui = new Ui::EditorOptionsDialog ();
mp_ui->setupUi (this);
connect (mp_ui->apply_pb, SIGNAL (clicked ()), this, SLOT (apply ()));
m_pages = pages;
for (std::vector <edt::EditorOptionsPage *>::const_iterator p = m_pages.begin (); p != m_pages.end (); ++p) {
(*p)->set_owner (this);
}
update (0);
setup ();
}
EditorOptionsPages::~EditorOptionsPages ()
{
while (m_pages.size () > 0) {
delete m_pages [0];
}
delete mp_ui;
mp_ui = 0;
}
void
EditorOptionsPages::unregister_page (edt::EditorOptionsPage *page)
{
std::vector <edt::EditorOptionsPage *> pages;
for (std::vector <edt::EditorOptionsPage *>::const_iterator p = m_pages.begin (); p != m_pages.end (); ++p) {
if (*p != page) {
pages.push_back (*p);
}
}
m_pages = pages;
update (0);
}
void
EditorOptionsPages::activate_page (edt::EditorOptionsPage *page)
template <class Value>
static void configure_from_line_edit (lay::Dispatcher *dispatcher, QLineEdit *le, const std::string &cfg_name)
{
try {
page->setup (mp_root);
} catch (...) {
// catch any errors related to configuration file errors etc.
Value value = Value (0);
tl::from_string (tl::to_string (le->text ()), value);
dispatcher->config_set (cfg_name, tl::to_string (value));
lay::indicate_error (le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (le, &ex);
}
update (page);
if (isVisible ()) {
activateWindow ();
raise ();
}
}
void
EditorOptionsPages::update (edt::EditorOptionsPage *page)
{
std::sort (m_pages.begin (), m_pages.end (), EOPCompareOp ());
while (mp_ui->pages->count () > 0) {
mp_ui->pages->removeTab (0);
}
int index = -1;
for (std::vector <edt::EditorOptionsPage *>::iterator p = m_pages.begin (); p != m_pages.end (); ++p) {
if ((*p)->active ()) {
mp_ui->pages->addTab ((*p)->q_frame (), tl::to_qstring ((*p)->title ()));
if ((*p) == page) {
index = int (std::distance (m_pages.begin (), p));
}
} else {
(*p)->q_frame ()->setParent (0);
}
}
if (index < 0) {
index = mp_ui->pages->currentIndex ();
}
if (index >= int (mp_ui->pages->count ())) {
index = mp_ui->pages->count () - 1;
}
mp_ui->pages->setCurrentIndex (index);
}
void
EditorOptionsPages::setup ()
{
try {
for (std::vector <edt::EditorOptionsPage *>::iterator p = m_pages.begin (); p != m_pages.end (); ++p) {
if ((*p)->active ()) {
(*p)->setup (mp_root);
}
}
// make the display consistent with the status (this is important for
// PCell parameters where the PCell may be asked to modify the parameters)
do_apply ();
} catch (...) {
// catch any errors related to configuration file errors etc.
}
}
void
EditorOptionsPages::do_apply ()
{
for (std::vector <edt::EditorOptionsPage *>::iterator p = m_pages.begin (); p != m_pages.end (); ++p) {
if ((*p)->active ()) {
(*p)->apply (mp_root);
}
}
}
void
EditorOptionsPages::apply ()
{
BEGIN_PROTECTED
do_apply ();
END_PROTECTED_W (this)
}
void
EditorOptionsPages::accept ()
{
BEGIN_PROTECTED
do_apply ();
QDialog::accept ();
END_PROTECTED_W (this)
}
// ------------------------------------------------------------------
// EditorOptionsGeneric implementation
EditorOptionsGeneric::EditorOptionsGeneric ()
: QWidget (), EditorOptionsPage ()
EditorOptionsGeneric::EditorOptionsGeneric (lay::Dispatcher *dispatcher)
: EditorOptionsPage (dispatcher)
{
mp_ui = new Ui::EditorOptionsGeneric ();
mp_ui->setupUi (this);
connect (mp_ui->grid_cb, SIGNAL (activated (int)), this, SLOT (grid_changed (int)));
connect (mp_ui->edit_grid_le, SIGNAL (editingFinished ()), this, SLOT (edited ()));
connect (mp_ui->grid_cb, SIGNAL (activated (int)), this, SLOT (edited ()));
connect (mp_ui->move_angle_cb, SIGNAL (activated (int)), this, SLOT (edited ()));
connect (mp_ui->conn_angle_cb, SIGNAL (activated (int)), this, SLOT (edited ()));
connect (mp_ui->hier_sel_cbx, SIGNAL (clicked ()), this, SLOT (edited ()));
connect (mp_ui->hier_copy_mode_cbx, SIGNAL (activated (int)), this, SLOT (edited ()));
connect (mp_ui->snap_objects_cbx, SIGNAL (clicked ()), this, SLOT (edited ()));
connect (mp_ui->max_shapes_le, SIGNAL (editingFinished ()), this, SLOT (edited ()));
connect (mp_ui->show_shapes_cbx, SIGNAL (clicked ()), this, SLOT (edited ()));
}
EditorOptionsGeneric::~EditorOptionsGeneric ()
@ -236,27 +94,32 @@ EditorOptionsGeneric::~EditorOptionsGeneric ()
mp_ui = 0;
}
std::string
std::string
EditorOptionsGeneric::title () const
{
return tl::to_string (QObject::tr ("Basic Editing"));
}
void
EditorOptionsGeneric::apply (lay::Plugin *root)
void
EditorOptionsGeneric::apply (lay::Dispatcher *root)
{
// Edit grid
db::DVector eg;
EditGridConverter egc;
if (mp_ui->grid_cb->currentIndex () == 0) {
eg = db::DVector (-1.0, -1.0);
root->config_set (cfg_edit_grid, egc.to_string (db::DVector (-1.0, -1.0)));
} else if (mp_ui->grid_cb->currentIndex () == 1) {
eg = db::DVector ();
root->config_set (cfg_edit_grid, egc.to_string (db::DVector ()));
} else {
egc.from_string_picky (tl::to_string (mp_ui->edit_grid_le->text ()), eg);
try {
db::DVector eg;
egc.from_string_picky (tl::to_string (mp_ui->edit_grid_le->text ()), eg);
lay::indicate_error (mp_ui->edit_grid_le, 0);
root->config_set (cfg_edit_grid, egc.to_string (eg));
} catch (tl::Exception &ex) {
lay::indicate_error (mp_ui->edit_grid_le, &ex);
}
}
root->config_set (cfg_edit_grid, egc.to_string (eg));
// Edit & move angle
@ -269,9 +132,7 @@ EditorOptionsGeneric::apply (lay::Plugin *root)
root->config_set (cfg_edit_hier_copy_mode, tl::to_string ((cpm < 0 || cpm > 1) ? -1 : cpm));
root->config_set (cfg_edit_snap_to_objects, tl::to_string (mp_ui->snap_objects_cbx->isChecked ()));
unsigned int max_shapes = 1000;
tl::from_string (tl::to_string (mp_ui->max_shapes_le->text ()), max_shapes);
root->config_set (cfg_edit_max_shapes_of_instances, tl::to_string (max_shapes));
configure_from_line_edit<unsigned int> (root, mp_ui->max_shapes_le, cfg_edit_max_shapes_of_instances);
root->config_set (cfg_edit_show_shapes_of_instances, tl::to_string (mp_ui->show_shapes_cbx->isChecked ()));
}
@ -287,8 +148,8 @@ EditorOptionsGeneric::show_shapes_changed ()
mp_ui->max_shapes_le->setEnabled (mp_ui->show_shapes_cbx->isChecked ());
}
void
EditorOptionsGeneric::setup (lay::Plugin *root)
void
EditorOptionsGeneric::setup (lay::Dispatcher *root)
{
// Edit grid
@ -305,12 +166,13 @@ EditorOptionsGeneric::setup (lay::Plugin *root)
mp_ui->edit_grid_le->setText (tl::to_qstring (egc.to_string (eg)));
}
grid_changed (mp_ui->grid_cb->currentIndex ());
lay::indicate_error (mp_ui->edit_grid_le, 0);
// edit & move angle
ACConverter acc;
lay::angle_constraint_type ac;
ac = lay::AC_Any;
root->config_get (cfg_edit_move_angle_mode, ac, acc);
mp_ui->move_angle_cb->setCurrentIndex (int (ac));
@ -334,6 +196,7 @@ EditorOptionsGeneric::setup (lay::Plugin *root)
unsigned int max_shapes = 1000;
root->config_get (cfg_edit_max_shapes_of_instances, max_shapes);
mp_ui->max_shapes_le->setText (tl::to_qstring (tl::to_string (max_shapes)));
lay::indicate_error (mp_ui->max_shapes_le, 0);
bool show_shapes = true;
root->config_get (cfg_edit_show_shapes_of_instances, show_shapes);
@ -343,11 +206,16 @@ EditorOptionsGeneric::setup (lay::Plugin *root)
// ------------------------------------------------------------------
// EditorOptionsText implementation
EditorOptionsText::EditorOptionsText ()
: QWidget (), EditorOptionsPage ()
EditorOptionsText::EditorOptionsText (lay::Dispatcher *dispatcher)
: lay::EditorOptionsPage (dispatcher)
{
mp_ui = new Ui::EditorOptionsText ();
mp_ui->setupUi (this);
connect (mp_ui->text_le, SIGNAL (editingFinished ()), this, SLOT (edited ()));
connect (mp_ui->halign_cbx, SIGNAL (activated (int)), this, SLOT (edited ()));
connect (mp_ui->valign_cbx, SIGNAL (activated (int)), this, SLOT (edited ()));
connect (mp_ui->size_le, SIGNAL (editingFinished ()), this, SLOT (edited ()));
}
EditorOptionsText::~EditorOptionsText ()
@ -363,7 +231,7 @@ EditorOptionsText::title () const
}
void
EditorOptionsText::apply (lay::Plugin *root)
EditorOptionsText::apply (lay::Dispatcher *root)
{
// Text string
root->config_set (cfg_edit_text_string, tl::unescape_string (tl::to_string (mp_ui->text_le->text ())));
@ -387,7 +255,7 @@ EditorOptionsText::apply (lay::Plugin *root)
}
void
EditorOptionsText::setup (lay::Plugin *root)
EditorOptionsText::setup (lay::Dispatcher *root)
{
// Text string
std::string s;
@ -416,13 +284,18 @@ EditorOptionsText::setup (lay::Plugin *root)
// ------------------------------------------------------------------
// EditorOptionsPath implementation
EditorOptionsPath::EditorOptionsPath ()
: QWidget (), EditorOptionsPage ()
EditorOptionsPath::EditorOptionsPath (lay::Dispatcher *dispatcher)
: lay::EditorOptionsPage (dispatcher)
{
mp_ui = new Ui::EditorOptionsPath ();
mp_ui->setupUi (this);
connect (mp_ui->type_cb, SIGNAL (currentIndexChanged (int)), this, SLOT (type_changed (int)));
connect (mp_ui->width_le, SIGNAL (editingFinished ()), this, SLOT (edited ()));
connect (mp_ui->type_cb, SIGNAL (activated (int)), this, SLOT (edited ()));
connect (mp_ui->start_ext_le, SIGNAL (editingFinished ()), this, SLOT (edited ()));
connect (mp_ui->end_ext_le, SIGNAL (editingFinished ()), this, SLOT (edited ()));
}
EditorOptionsPath::~EditorOptionsPath ()
@ -445,13 +318,11 @@ EditorOptionsPath::type_changed (int type)
}
void
EditorOptionsPath::apply (lay::Plugin *root)
EditorOptionsPath::apply (lay::Dispatcher *root)
{
// width
double w = 0.0;
tl::from_string (tl::to_string (mp_ui->width_le->text ()), w);
root->config_set (cfg_edit_path_width, tl::to_string (w));
configure_from_line_edit<double> (root, mp_ui->width_le, cfg_edit_path_width);
// path type and extensions
@ -465,14 +336,10 @@ EditorOptionsPath::apply (lay::Plugin *root)
} else if (mp_ui->type_cb->currentIndex () == 2) {
double bgnext = 0.0, endext = 0.0;
root->config_set (cfg_edit_path_ext_type, "variable");
tl::from_string (tl::to_string (mp_ui->start_ext_le->text ()), bgnext);
root->config_set (cfg_edit_path_ext_var_begin, tl::to_string (bgnext));
tl::from_string (tl::to_string (mp_ui->end_ext_le->text ()), endext);
root->config_set (cfg_edit_path_ext_var_end, tl::to_string (endext));
configure_from_line_edit<double> (root, mp_ui->start_ext_le, cfg_edit_path_ext_var_begin);
configure_from_line_edit<double> (root, mp_ui->end_ext_le, cfg_edit_path_ext_var_end);
} else if (mp_ui->type_cb->currentIndex () == 3) {
@ -482,13 +349,14 @@ EditorOptionsPath::apply (lay::Plugin *root)
}
void
EditorOptionsPath::setup (lay::Plugin *root)
EditorOptionsPath::setup (lay::Dispatcher *root)
{
// width
double w = 0.0;
root->config_get (cfg_edit_path_width, w);
mp_ui->width_le->setText (tl::to_qstring (tl::to_string (w)));
lay::indicate_error (mp_ui->width_le, 0);
// path type and extensions
@ -509,26 +377,35 @@ EditorOptionsPath::setup (lay::Plugin *root)
root->config_get (cfg_edit_path_ext_var_begin, bgnext);
root->config_get (cfg_edit_path_ext_var_end, endext);
mp_ui->start_ext_le->setText (tl::to_qstring (tl::to_string (bgnext)));
lay::indicate_error (mp_ui->start_ext_le, 0);
mp_ui->end_ext_le->setText (tl::to_qstring (tl::to_string (endext)));
lay::indicate_error (mp_ui->end_ext_le, 0);
}
// ------------------------------------------------------------------
// EditorOptionsInst implementation
EditorOptionsInst::EditorOptionsInst (lay::Dispatcher *root)
: QWidget (), EditorOptionsPage (), mp_root (root), mp_pcell_parameters (0)
EditorOptionsInst::EditorOptionsInst (lay::Dispatcher *dispatcher)
: lay::EditorOptionsPage (dispatcher)
{
mp_ui = new Ui::EditorOptionsInst ();
mp_ui->setupUi (this);
connect (mp_ui->array_grp, SIGNAL (clicked ()), this, SLOT (array_changed ()));
connect (mp_ui->browse_pb, SIGNAL (clicked ()), this, SLOT (browse_cell ()));
connect (mp_ui->lib_cbx, SIGNAL (currentIndexChanged (int)), this, SLOT (library_changed (int)));
connect (mp_ui->cell_le, SIGNAL (textChanged (const QString &)), this, SLOT (cell_name_changed (const QString &)));
QHBoxLayout *layout = new QHBoxLayout (mp_ui->pcell_tab);
layout->setMargin (0);
mp_ui->pcell_tab->setLayout (layout);
connect (mp_ui->lib_cbx, SIGNAL (currentIndexChanged (int)), this, SLOT (library_changed ()));
connect (mp_ui->cell_le, SIGNAL (editingFinished ()), this, SLOT (edited ()));
connect (mp_ui->angle_le, SIGNAL (editingFinished ()), this, SLOT (edited ()));
connect (mp_ui->scale_le, SIGNAL (editingFinished ()), this, SLOT (edited ()));
connect (mp_ui->rows_le, SIGNAL (editingFinished ()), this, SLOT (edited ()));
connect (mp_ui->row_x_le, SIGNAL (editingFinished ()), this, SLOT (edited ()));
connect (mp_ui->row_y_le, SIGNAL (editingFinished ()), this, SLOT (edited ()));
connect (mp_ui->columns_le, SIGNAL (editingFinished ()), this, SLOT (edited ()));
connect (mp_ui->column_x_le, SIGNAL (editingFinished ()), this, SLOT (edited ()));
connect (mp_ui->column_y_le, SIGNAL (editingFinished ()), this, SLOT (edited ()));
connect (mp_ui->mirror_cbx, SIGNAL (clicked ()), this, SLOT (edited ()));
connect (mp_ui->array_grp, SIGNAL (clicked ()), this, SLOT (edited ()));
connect (mp_ui->place_origin_cb, SIGNAL (clicked ()), this, SLOT (edited ()));
m_cv_index = -1;
}
@ -546,19 +423,61 @@ EditorOptionsInst::title () const
}
void
EditorOptionsInst::library_changed (int)
EditorOptionsInst::library_changed ()
{
BEGIN_PROTECTED
update_pcell_parameters ();
END_PROTECTED
update_cell_edits ();
edited ();
}
// Maximum number of cells for which to offer a cell name completer
const static size_t max_cells = 10000;
void
EditorOptionsInst::cell_name_changed (const QString &)
EditorOptionsInst::update_cell_edits ()
{
BEGIN_PROTECTED
update_pcell_parameters ();
END_PROTECTED
if (mp_ui->cell_le->completer ()) {
mp_ui->cell_le->completer ()->deleteLater ();
}
db::Layout *layout = 0;
lay::LayoutView *view = lay::LayoutView::current ();
// find the layout the cell has to be looked up: that is either the layout of the current instance or
// the library selected
if (mp_ui->lib_cbx->current_library ()) {
layout = &mp_ui->lib_cbx->current_library ()->layout ();
} else if (view && view->cellview (m_cv_index).is_valid ()) {
layout = &view->cellview (m_cv_index)->layout ();
}
if (! layout) {
return;
}
QStringList cellnames;
if (layout->cells () < max_cells) {
for (db::Layout::iterator c = layout->begin (); c != layout->end (); ++c) {
cellnames.push_back (tl::to_qstring (layout->cell_name (c->cell_index ())));
}
for (db::Layout::pcell_iterator pc = layout->begin_pcells (); pc != layout->end_pcells () && size_t (cellnames.size ()) < max_cells; ++pc) {
cellnames.push_back (tl::to_qstring (pc->first));
}
}
if (size_t (cellnames.size ()) < max_cells) {
QCompleter *completer = new QCompleter (cellnames, this);
completer->setCaseSensitivity (Qt::CaseSensitive);
mp_ui->cell_le->setCompleter (completer);
} else {
mp_ui->cell_le->setCompleter (0);
}
std::pair<bool, db::pcell_id_type> pc = layout->pcell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ());
std::pair<bool, db::cell_index_type> cc = layout->cell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ());
// by the way, update the foreground color of the cell edit box as well (red, if not valid)
tl::Exception ex ("No cell or PCell with this name");
lay::indicate_error (mp_ui->cell_le, (! pc.first && ! cc.first) ? &ex : 0);
}
void
@ -602,7 +521,7 @@ BEGIN_PROTECTED
} else if (layout->is_valid_cell_index (form.selected_cell_index ())) {
mp_ui->cell_le->setText (tl::to_qstring (layout->cell_name (form.selected_cell_index ())));
}
update_pcell_parameters ();
edited ();
}
}
@ -620,74 +539,40 @@ EditorOptionsInst::array_changed ()
mp_ui->columns_le->setEnabled (array);
mp_ui->column_x_le->setEnabled (array);
mp_ui->column_y_le->setEnabled (array);
edited ();
}
void
EditorOptionsInst::apply (lay::Plugin *root)
EditorOptionsInst::apply (lay::Dispatcher *root)
{
// cell name
root->config_set (cfg_edit_inst_cell_name, tl::to_string (mp_ui->cell_le->text ()));
// cell name
// lib name
if (mp_ui->lib_cbx->current_library ()) {
root->config_set (cfg_edit_inst_lib_name, mp_ui->lib_cbx->current_library ()->get_name ());
} else {
root->config_set (cfg_edit_inst_lib_name, std::string ());
}
// pcell parameters
std::string param;
db::Layout *layout = 0;
if (mp_ui->lib_cbx->current_library ()) {
layout = &mp_ui->lib_cbx->current_library ()->layout ();
} else if (m_cv_index >= 0 && lay::LayoutView::current () && lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) {
layout = &lay::LayoutView::current ()->cellview (m_cv_index)->layout ();
}
if (layout && mp_pcell_parameters) {
std::pair<bool, db::pcell_id_type> pc = layout->pcell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ());
if (pc.first) {
const db::PCellDeclaration *pc_decl = layout->pcell_declaration (pc.second);
if (pc_decl) {
param = pcell_parameters_to_string (pc_decl->named_parameters (mp_pcell_parameters->get_parameters ()));
}
}
}
root->config_set (cfg_edit_inst_pcell_parameters, param);
// rotation, scaling
double angle = 0.0;
tl::from_string (tl::to_string (mp_ui->angle_le->text ()), angle);
root->config_set (cfg_edit_inst_angle, tl::to_string (angle));
configure_from_line_edit<double> (root, mp_ui->angle_le, cfg_edit_inst_angle);
bool mirror = mp_ui->mirror_cbx->isChecked ();
root->config_set (cfg_edit_inst_mirror, tl::to_string (mirror));
double scale = 1.0;
tl::from_string (tl::to_string (mp_ui->scale_le->text ()), scale);
root->config_set (cfg_edit_inst_scale, tl::to_string (scale));
configure_from_line_edit<double> (root, mp_ui->scale_le, cfg_edit_inst_scale);
// array
bool array = mp_ui->array_grp->isChecked ();
root->config_set (cfg_edit_inst_array, tl::to_string (array));
int rows = 1, columns = 1;
double row_x = 0.0, row_y = 0.0, column_x = 0.0, column_y = 0.0;
tl::from_string (tl::to_string (mp_ui->rows_le->text ()), rows);
tl::from_string (tl::to_string (mp_ui->row_x_le->text ()), row_x);
tl::from_string (tl::to_string (mp_ui->row_y_le->text ()), row_y);
tl::from_string (tl::to_string (mp_ui->columns_le->text ()), columns);
tl::from_string (tl::to_string (mp_ui->column_x_le->text ()), column_x);
tl::from_string (tl::to_string (mp_ui->column_y_le->text ()), column_y);
root->config_set (cfg_edit_inst_rows, tl::to_string (rows));
root->config_set (cfg_edit_inst_row_x, tl::to_string (row_x));
root->config_set (cfg_edit_inst_row_y, tl::to_string (row_y));
root->config_set (cfg_edit_inst_columns, tl::to_string (columns));
root->config_set (cfg_edit_inst_column_x, tl::to_string (column_x));
root->config_set (cfg_edit_inst_column_y, tl::to_string (column_y));
configure_from_line_edit<int> (root, mp_ui->rows_le, cfg_edit_inst_rows);
configure_from_line_edit<double> (root, mp_ui->row_x_le, cfg_edit_inst_row_x);
configure_from_line_edit<double> (root, mp_ui->row_y_le, cfg_edit_inst_row_y);
configure_from_line_edit<int> (root, mp_ui->columns_le, cfg_edit_inst_columns);
configure_from_line_edit<double> (root, mp_ui->column_x_le, cfg_edit_inst_column_x);
configure_from_line_edit<double> (root, mp_ui->column_y_le, cfg_edit_inst_column_y);
// place origin of cell flag
bool place_origin = mp_ui->place_origin_cb->isChecked ();
@ -695,45 +580,187 @@ EditorOptionsInst::apply (lay::Plugin *root)
}
void
EditorOptionsInst::setup (lay::Plugin *root)
EditorOptionsInst::setup (lay::Dispatcher *root)
{
m_cv_index = -1;
if (lay::LayoutView::current ()) {
m_cv_index = lay::LayoutView::current ()->active_cellview_index ();
}
mp_ui->lib_cbx->update_list ();
if (m_cv_index >= 0 && lay::LayoutView::current () && lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) {
mp_ui->lib_cbx->set_technology_filter (lay::LayoutView::current ()->cellview (m_cv_index)->tech_name (), true);
} else {
mp_ui->lib_cbx->set_technology_filter (std::string (), false);
try {
mp_ui->lib_cbx->blockSignals (true);
mp_ui->lib_cbx->update_list ();
if (m_cv_index >= 0 && lay::LayoutView::current () && lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) {
mp_ui->lib_cbx->set_technology_filter (lay::LayoutView::current ()->cellview (m_cv_index)->tech_name (), true);
} else {
mp_ui->lib_cbx->set_technology_filter (std::string (), false);
}
// cell name
std::string s;
root->config_get (cfg_edit_inst_cell_name, s);
mp_ui->cell_le->setText (tl::to_qstring (s));
// library
std::string l;
root->config_get (cfg_edit_inst_lib_name, l);
mp_ui->lib_cbx->set_current_library (db::LibraryManager::instance ().lib_ptr_by_name (l));
mp_ui->lib_cbx->blockSignals (false);
update_cell_edits ();
} catch (...) {
mp_ui->lib_cbx->blockSignals (false);
throw;
}
// rotation, scaling
double angle = 0.0;
root->config_get (cfg_edit_inst_angle, angle);
mp_ui->angle_le->setText (tl::to_qstring (tl::to_string (angle)));
lay::indicate_error (mp_ui->angle_le, 0);
bool mirror = false;
root->config_get (cfg_edit_inst_mirror, mirror);
mp_ui->mirror_cbx->setChecked (mirror);
double scale = 1.0;
root->config_get (cfg_edit_inst_scale, scale);
mp_ui->scale_le->setText (tl::to_qstring (tl::to_string (scale)));
lay::indicate_error (mp_ui->scale_le, 0);
// array
bool array = false;
root->config_get (cfg_edit_inst_array, array);
mp_ui->array_grp->setChecked (array);
int rows = 1, columns = 1;
double row_x = 0.0, row_y = 0.0, column_x = 0.0, column_y = 0.0;
root->config_get (cfg_edit_inst_rows, rows);
root->config_get (cfg_edit_inst_row_x, row_x);
root->config_get (cfg_edit_inst_row_y, row_y);
root->config_get (cfg_edit_inst_columns, columns);
root->config_get (cfg_edit_inst_column_x, column_x);
root->config_get (cfg_edit_inst_column_y, column_y);
mp_ui->rows_le->setText (tl::to_qstring (tl::to_string (rows)));
lay::indicate_error (mp_ui->rows_le, 0);
mp_ui->row_x_le->setText (tl::to_qstring (tl::to_string (row_x)));
lay::indicate_error (mp_ui->row_x_le, 0);
mp_ui->row_y_le->setText (tl::to_qstring (tl::to_string (row_y)));
lay::indicate_error (mp_ui->row_y_le, 0);
mp_ui->columns_le->setText (tl::to_qstring (tl::to_string (columns)));
lay::indicate_error (mp_ui->columns_le, 0);
mp_ui->column_x_le->setText (tl::to_qstring (tl::to_string (column_x)));
lay::indicate_error (mp_ui->column_x_le, 0);
mp_ui->column_y_le->setText (tl::to_qstring (tl::to_string (column_y)));
lay::indicate_error (mp_ui->column_y_le, 0);
// place origin of cell flag
bool place_origin = false;
root->config_get (cfg_edit_inst_place_origin, place_origin);
mp_ui->place_origin_cb->setChecked (place_origin);
}
// ------------------------------------------------------------------
// EditorOptionsInstPCellParam implementation
EditorOptionsInstPCellParam::EditorOptionsInstPCellParam (lay::Dispatcher *dispatcher)
: lay::EditorOptionsPage (dispatcher), mp_pcell_parameters (0), mp_placeholder_label (0)
{
mp_ui = new Ui::EditorOptionsInstPCellParam ();
mp_ui->setupUi (this);
}
EditorOptionsInstPCellParam::~EditorOptionsInstPCellParam ()
{
delete mp_ui;
mp_ui = 0;
}
std::string
EditorOptionsInstPCellParam::title () const
{
return tl::to_string (QObject::tr ("PCell"));
}
void
EditorOptionsInstPCellParam::apply (lay::Dispatcher *root)
{
// pcell parameters
std::string param;
db::Layout *layout = 0;
db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name);
if (lib) {
layout = &lib->layout ();
} else if (m_cv_index >= 0 && lay::LayoutView::current () && lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) {
layout = &lay::LayoutView::current ()->cellview (m_cv_index)->layout ();
}
bool ok = true;
if (layout && mp_pcell_parameters) {
std::pair<bool, db::pcell_id_type> pc = layout->pcell_by_name (tl::to_string (m_cell_name).c_str ());
if (pc.first) {
const db::PCellDeclaration *pc_decl = layout->pcell_declaration (pc.second);
if (pc_decl) {
param = pcell_parameters_to_string (pc_decl->named_parameters (mp_pcell_parameters->get_parameters (&ok)));
}
}
}
if (ok) {
root->config_set (cfg_edit_inst_pcell_parameters, param);
}
}
void
EditorOptionsInstPCellParam::setup (lay::Dispatcher *root)
{
m_cv_index = -1;
if (lay::LayoutView::current ()) {
m_cv_index = lay::LayoutView::current ()->active_cellview_index ();
}
bool needs_update = (mp_pcell_parameters == 0);
// cell name
std::string s;
root->config_get (cfg_edit_inst_cell_name, s);
mp_ui->cell_le->setText (tl::to_qstring (s));
std::string cn;
root->config_get (cfg_edit_inst_cell_name, cn);
if (cn != m_cell_name) {
m_cell_name = cn;
needs_update = true;
}
// library
std::string l;
root->config_get (cfg_edit_inst_lib_name, l);
mp_ui->lib_cbx->set_current_library (db::LibraryManager::instance ().lib_ptr_by_name (l));
std::string ln;
root->config_get (cfg_edit_inst_lib_name, ln);
if (ln != m_lib_name) {
m_lib_name = ln;
needs_update = true;
}
db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name);
// pcell parameters
std::string param;
root->config_get (cfg_edit_inst_pcell_parameters, param);
db::Layout *layout = 0;
if (mp_ui->lib_cbx->current_library ()) {
layout = &mp_ui->lib_cbx->current_library ()->layout ();
if (lib) {
layout = &lib->layout ();
} else if (m_cv_index >= 0 && lay::LayoutView::current () && lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) {
layout = &lay::LayoutView::current ()->cellview (m_cv_index)->layout ();
}
std::vector<tl::Variant> pv;
if (layout && mp_pcell_parameters) {
if (layout) {
std::pair<bool, db::pcell_id_type> pc = layout->pcell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ());
std::pair<bool, db::pcell_id_type> pc = layout->pcell_by_name (tl::to_string (m_cell_name).c_str ());
if (pc.first) {
@ -768,88 +795,49 @@ EditorOptionsInst::setup (lay::Plugin *root)
}
}
if (! needs_update) {
bool ok = false;
if (mp_pcell_parameters->get_parameters (&ok) != pv || ! ok) {
needs_update = true;
}
}
try {
update_pcell_parameters (pv);
if (needs_update) {
update_pcell_parameters (pv);
}
} catch (...) { }
// rotation, scaling
double angle = 0.0;
root->config_get (cfg_edit_inst_angle, angle);
mp_ui->angle_le->setText (tl::to_qstring (tl::to_string (angle)));
bool mirror = false;
root->config_get (cfg_edit_inst_mirror, mirror);
mp_ui->mirror_cbx->setChecked (mirror);
double scale = 1.0;
root->config_get (cfg_edit_inst_scale, scale);
mp_ui->scale_le->setText (tl::to_qstring (tl::to_string (scale)));
// array
bool array = false;
root->config_get (cfg_edit_inst_array, array);
mp_ui->array_grp->setChecked (array);
int rows = 1, columns = 1;
double row_x = 0.0, row_y = 0.0, column_x = 0.0, column_y = 0.0;
root->config_get (cfg_edit_inst_rows, rows);
root->config_get (cfg_edit_inst_row_x, row_x);
root->config_get (cfg_edit_inst_row_y, row_y);
root->config_get (cfg_edit_inst_columns, columns);
root->config_get (cfg_edit_inst_column_x, column_x);
root->config_get (cfg_edit_inst_column_y, column_y);
mp_ui->rows_le->setText (tl::to_qstring (tl::to_string (rows)));
mp_ui->row_x_le->setText (tl::to_qstring (tl::to_string (row_x)));
mp_ui->row_y_le->setText (tl::to_qstring (tl::to_string (row_y)));
mp_ui->columns_le->setText (tl::to_qstring (tl::to_string (columns)));
mp_ui->column_x_le->setText (tl::to_qstring (tl::to_string (column_x)));
mp_ui->column_y_le->setText (tl::to_qstring (tl::to_string (column_y)));
// place origin of cell flag
bool place_origin = false;
root->config_get (cfg_edit_inst_place_origin, place_origin);
mp_ui->place_origin_cb->setChecked (place_origin);
}
void
EditorOptionsInst::update_pcell_parameters ()
void
EditorOptionsInstPCellParam::update_pcell_parameters ()
{
update_pcell_parameters (std::vector <tl::Variant> ());
}
void
EditorOptionsInst::update_pcell_parameters (const std::vector <tl::Variant> &parameters)
void
EditorOptionsInstPCellParam::update_pcell_parameters (const std::vector <tl::Variant> &parameters)
{
db::Layout *layout = 0;
lay::LayoutView *view = lay::LayoutView::current ();
if (m_cv_index < 0 || !lay::LayoutView::current () || !lay::LayoutView::current ()->cellview (m_cv_index).is_valid ()) {
mp_ui->param_tab_widget->setTabEnabled (1, false);
return;
}
// find the layout the cell has to be looked up: that is either the layout of the current instance or
// find the layout the cell has to be looked up: that is either the layout of the current instance or
// the library selected
if (mp_ui->lib_cbx->current_library ()) {
layout = &mp_ui->lib_cbx->current_library ()->layout ();
} else {
layout = &lay::LayoutView::current ()->cellview (m_cv_index)->layout ();
db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name);
if (lib) {
layout = &lib->layout ();
} else if (view) {
const lay::CellView &cv = view->cellview (m_cv_index);
if (cv.is_valid ()) {
layout = &cv->layout ();
}
}
std::pair<bool, db::pcell_id_type> pc = layout->pcell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ());
std::pair<bool, db::cell_index_type> cc = layout->cell_by_name (tl::to_string (mp_ui->cell_le->text ()).c_str ());
// by the way, update the foreground color of the cell edit box as well (red, if not valid)
QPalette pl = mp_ui->cell_le->palette ();
if (! pc.first && ! cc.first) {
pl.setColor (QPalette::Text, Qt::red);
pl.setColor (QPalette::Base, QColor (Qt::red).lighter (180));
} else {
pl.setColor (QPalette::Text, palette ().color (QPalette::Text));
pl.setColor (QPalette::Base, palette ().color (QPalette::Base));
std::pair<bool, db::pcell_id_type> pc (false, 0);
if (layout) {
pc = layout->pcell_by_name (tl::to_string (m_cell_name).c_str ());
}
mp_ui->cell_le->setPalette (pl);
PCellParametersPage::State pcp_state;
@ -860,19 +848,30 @@ EditorOptionsInst::update_pcell_parameters (const std::vector <tl::Variant> &par
mp_pcell_parameters->deleteLater ();
}
if (mp_placeholder_label) {
mp_placeholder_label->hide ();
mp_placeholder_label->deleteLater ();
}
mp_pcell_parameters = 0;
mp_placeholder_label = 0;
if (pc.first && layout->pcell_declaration (pc.second)) {
if (pc.first && layout->pcell_declaration (pc.second) && view && view->cellview (m_cv_index).is_valid ()) {
mp_ui->param_tab_widget->setTabEnabled (1, true);
lay::LayoutView *view = lay::LayoutView::current ();
mp_pcell_parameters = new PCellParametersPage (mp_ui->pcell_tab, &view->cellview (m_cv_index)->layout (), view, m_cv_index, layout->pcell_declaration (pc.second), parameters);
mp_ui->pcell_tab->layout ()->addWidget (mp_pcell_parameters);
mp_pcell_parameters = new PCellParametersPage (this, true /*dense*/);
mp_pcell_parameters->setup (&view->cellview (m_cv_index)->layout (), view, m_cv_index, layout->pcell_declaration (pc.second), parameters);
this->layout ()->addWidget (mp_pcell_parameters);
mp_pcell_parameters->set_state (pcp_state);
connect (mp_pcell_parameters, SIGNAL (edited ()), this, SLOT (edited ()));
} else {
mp_ui->param_tab_widget->setTabEnabled (1, false);
mp_placeholder_label = new QLabel (this);
mp_placeholder_label->setText (tr ("Not a PCell"));
mp_placeholder_label->setAlignment (Qt::AlignHCenter | Qt::AlignVCenter);
this->layout ()->addWidget (mp_placeholder_label);
}
}

View File

@ -24,12 +24,17 @@
#ifndef HDR_edtEditorOptionsPages
#define HDR_edtEditorOptionsPages
#include "layEditorOptionsPage.h"
#include <tlVariant.h>
#include <QDialog>
#include <QFrame>
#include <vector>
#include <string>
class QTabWidget;
class QLabel;
namespace Ui
{
class EditorOptionsDialog;
@ -39,6 +44,7 @@ namespace Ui
class EditorOptionsPath;
class EditorOptionsText;
class EditorOptionsInst;
class EditorOptionsInstPCellParam;
}
namespace lay
@ -53,83 +59,22 @@ namespace edt
class PCellParametersPage;
class EditorOptionsPages;
/**
* @brief The base class for a object properties page
*/
class EditorOptionsPage
{
public:
EditorOptionsPage ();
virtual ~EditorOptionsPage ();
virtual QWidget *q_frame () = 0;
virtual std::string title () const = 0;
virtual int order () const = 0;
virtual void apply (lay::Plugin *root) = 0;
virtual void setup (lay::Plugin *root) = 0;
bool active () const { return m_active; }
void activate (bool active);
void set_owner (EditorOptionsPages *owner);
const lay::PluginDeclaration *plugin_declaration () const { return mp_plugin_declaration; }
void set_plugin_declaration (const lay::PluginDeclaration *pd) { mp_plugin_declaration = pd; }
private:
EditorOptionsPages *mp_owner;
bool m_active;
const lay::PluginDeclaration *mp_plugin_declaration;
};
/**
* @brief The object properties dialog
*/
class EditorOptionsPages
: public QDialog
{
Q_OBJECT
public:
EditorOptionsPages (const std::vector<edt::EditorOptionsPage *> &pages, lay::Dispatcher *root);
~EditorOptionsPages ();
void unregister_page (edt::EditorOptionsPage *page);
void activate_page (edt::EditorOptionsPage *page);
public slots:
void apply ();
void setup ();
void accept ();
private:
std::vector <edt::EditorOptionsPage *> m_pages;
Ui::EditorOptionsDialog *mp_ui;
lay::Dispatcher *mp_root;
void update (edt::EditorOptionsPage *page);
void do_apply ();
};
/**
* @brief The generic properties page
*/
class EditorOptionsGeneric
: public QWidget, public EditorOptionsPage
: public lay::EditorOptionsPage
{
Q_OBJECT
public:
EditorOptionsGeneric ();
EditorOptionsGeneric (lay::Dispatcher *dispatcher);
~EditorOptionsGeneric ();
virtual QWidget *q_frame () { return this; }
virtual std::string title () const;
virtual int order () const { return 0; }
void apply (lay::Plugin *root);
void setup (lay::Plugin *root);
void apply (lay::Dispatcher *root);
void setup (lay::Dispatcher *root);
public slots:
void grid_changed (int);
@ -143,18 +88,16 @@ private:
* @brief The text properties page
*/
class EditorOptionsText
: public QWidget, public EditorOptionsPage
: public lay::EditorOptionsPage
{
public:
EditorOptionsText ();
EditorOptionsText (lay::Dispatcher *dispatcher);
~EditorOptionsText ();
virtual QWidget *q_frame () { return this; }
virtual std::string title () const;
virtual int order () const { return 10; }
void apply (lay::Plugin *root);
void setup (lay::Plugin *root);
void apply (lay::Dispatcher *root);
void setup (lay::Dispatcher *root);
private:
Ui::EditorOptionsText *mp_ui;
@ -164,20 +107,18 @@ private:
* @brief The path properties page
*/
class EditorOptionsPath
: public QWidget, public EditorOptionsPage
: public lay::EditorOptionsPage
{
Q_OBJECT
public:
EditorOptionsPath ();
EditorOptionsPath (lay::Dispatcher *dispatcher);
~EditorOptionsPath ();
virtual QWidget *q_frame () { return this; }
virtual std::string title () const;
virtual int order () const { return 20; }
void apply (lay::Plugin *root);
void setup (lay::Plugin *root);
virtual int order () const { return 30; }
void apply (lay::Dispatcher *root);
void setup (lay::Dispatcher *root);
public slots:
void type_changed (int);
@ -190,7 +131,7 @@ private:
* @brief The instance properties page
*/
class EditorOptionsInst
: public QWidget, public EditorOptionsPage
: public lay::EditorOptionsPage
{
Q_OBJECT
@ -198,25 +139,49 @@ public:
EditorOptionsInst (lay::Dispatcher *root);
~EditorOptionsInst ();
virtual QWidget *q_frame () { return this; }
virtual std::string title () const;
virtual int order () const { return 20; }
void apply (lay::Plugin *root);
void setup (lay::Plugin *root);
void apply (lay::Dispatcher *root);
void setup (lay::Dispatcher *root);
public slots:
private slots:
void array_changed ();
void browse_cell ();
void update_pcell_parameters ();
void library_changed (int index);
void cell_name_changed (const QString &s);
void library_changed ();
void update_cell_edits ();
private:
Ui::EditorOptionsInst *mp_ui;
lay::Dispatcher *mp_root;
edt::PCellParametersPage *mp_pcell_parameters;
int m_cv_index;
};
/**
* @brief The instance properties page (PCell parameters)
*/
class EditorOptionsInstPCellParam
: public lay::EditorOptionsPage
{
Q_OBJECT
public:
EditorOptionsInstPCellParam (lay::Dispatcher *root);
~EditorOptionsInstPCellParam ();
virtual std::string title () const;
virtual int order () const { return 21; }
void apply (lay::Dispatcher *root);
void setup (lay::Dispatcher *root);
private slots:
void update_pcell_parameters ();
private:
Ui::EditorOptionsInstPCellParam *mp_ui;
edt::PCellParametersPage *mp_pcell_parameters;
QLabel *mp_placeholder_label;
int m_cv_index;
std::string m_lib_name, m_cell_name;
void update_pcell_parameters (const std::vector <tl::Variant> &parameters);
};

View File

@ -31,6 +31,7 @@
#include "layObjectInstPath.h"
#include "layLayoutView.h"
#include "layCellSelectionForm.h"
#include "layQtTools.h"
#include "tlExceptions.h"
#include "tlString.h"
@ -65,6 +66,43 @@ InstPropertiesPage::InstPropertiesPage (edt::Service *service, db::Manager *mana
connect (lib_cbx, SIGNAL (currentIndexChanged (int)), this, SLOT (library_changed (int)));
connect (cell_name_le, SIGNAL (textChanged (const QString &)), this, SLOT (cell_name_changed (const QString &)));
if (! readonly ()) {
connect (lib_cbx, SIGNAL (activated (int)), this, SIGNAL (edited ()));
connect (cell_name_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (array_grp, SIGNAL (clicked ()), this, SIGNAL (edited ()));
connect (rows_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (columns_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (row_x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (row_y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (column_x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (column_y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (pos_x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (pos_y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (angle_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (mag_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (mirror_cbx, SIGNAL (clicked ()), this, SIGNAL (edited ()));
} else {
browse_pb->setEnabled (false);
cell_name_le->setReadOnly (true);
rows_le->setReadOnly (true);
columns_le->setReadOnly (true);
row_x_le->setReadOnly (true);
row_y_le->setReadOnly (true);
column_x_le->setReadOnly (true);
column_y_le->setReadOnly (true);
pos_x_le->setReadOnly (true);
pos_y_le->setReadOnly (true);
angle_le->setReadOnly (true);
mag_le->setReadOnly (true);
lib_cbx->setEnabled (false);
array_grp->setEnabled (false);
mirror_cbx->setEnabled (false);
}
QHBoxLayout *layout = new QHBoxLayout (pcell_tab);
layout->setMargin (0);
pcell_tab->setLayout (layout);
@ -125,12 +163,19 @@ BEGIN_PROTECTED
}
if (form.exec ()) {
cell_name_le->blockSignals (true);
if (form.selected_cell_is_pcell ()) {
cell_name_le->setText (tl::to_qstring (layout->pcell_header (form.selected_pcell_id ())->get_name ()));
} else if (layout->is_valid_cell_index (form.selected_cell_index ())) {
cell_name_le->setText (tl::to_qstring (layout->cell_name (form.selected_cell_index ())));
}
cell_name_le->blockSignals (false);
update_pcell_parameters ();
emit edited ();
}
END_PROTECTED
@ -140,7 +185,9 @@ void
InstPropertiesPage::show_props ()
{
lay::UserPropertiesForm props_form (this);
props_form.show (mp_service->view (), m_selection_ptrs [m_index]->cv_index (), m_prop_id);
if (props_form.show (mp_service->view (), m_selection_ptrs [m_index]->cv_index (), m_prop_id)) {
emit edited ();
}
}
void
@ -222,6 +269,7 @@ InstPropertiesPage::update ()
db::cell_index_type def_cell_index = pos->back ().inst_ptr.cell_index ();
const db::Cell &def_cell = def_layout->cell (def_cell_index);
lib_cbx->blockSignals (true);
std::pair<db::Library *, db::cell_index_type> dl = def_layout->defining_library (def_cell_index);
lib_cbx->set_technology_filter (cv->tech_name (), true);
lib_cbx->set_current_library (dl.first);
@ -229,13 +277,16 @@ InstPropertiesPage::update ()
def_layout = &dl.first->layout ();
def_cell_index = dl.second;
}
lib_cbx->blockSignals (false);
std::pair<bool, db::pcell_id_type> pci = def_layout->is_pcell_instance (def_cell_index);
cell_name_le->blockSignals (true);
if (pci.first && def_layout->pcell_declaration (pci.second)) {
cell_name_le->setText (tl::to_qstring (def_layout->pcell_header (pci.second)->get_name ()));
} else {
cell_name_le->setText (tl::to_qstring (def_layout->cell_name (def_cell_index)));
}
cell_name_le->blockSignals (false);
db::Vector rowv, columnv;
unsigned long rows, columns;
@ -325,6 +376,9 @@ InstPropertiesPage::readonly ()
ChangeApplicator *
InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance & /*inst*/, double dbu)
{
bool has_error = false;
bool has_pcell_error = false;
std::auto_ptr<CombinedChangeApplicator> appl (new CombinedChangeApplicator ());
edt::Service::obj_iterator pos = m_selection_ptrs [m_index];
@ -343,34 +397,69 @@ InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance &
layout = &cv->layout ();
}
std::pair<bool, db::cell_index_type> ci = layout->cell_by_name (tl::to_string (cell_name_le->text ()).c_str ());
std::pair<bool, db::pcell_id_type> pci = layout->pcell_by_name (tl::to_string (cell_name_le->text ()).c_str ());
if (! ci.first && ! pci.first) {
throw tl::Exception (tl::to_string (QObject::tr ("Not a valid cell name: %s")).c_str (), tl::to_string (cell_name_le->text ()).c_str ());
try {
std::pair<bool, db::cell_index_type> ci = layout->cell_by_name (tl::to_string (cell_name_le->text ()).c_str ());
std::pair<bool, db::pcell_id_type> pci = layout->pcell_by_name (tl::to_string (cell_name_le->text ()).c_str ());
if (! ci.first && ! pci.first) {
throw tl::Exception (tl::to_string (QObject::tr ("Not a valid cell or PCell name: %s")).c_str (), tl::to_string (cell_name_le->text ()).c_str ());
}
lay::indicate_error (cell_name_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (cell_name_le, &ex);
has_error = true;
}
db::cell_index_type inst_cell_index = ci.second;
try {
// instantiate the PCell
if (pci.first) {
tl_assert (mp_pcell_parameters != 0);
tl_assert (layout->pcell_declaration (pci.second) == mp_pcell_parameters->pcell_decl ());
inst_cell_index = layout->get_pcell_variant (pci.second, mp_pcell_parameters->get_parameters ());
}
std::pair<bool, db::cell_index_type> ci = layout->cell_by_name (tl::to_string (cell_name_le->text ()).c_str ());
std::pair<bool, db::pcell_id_type> pci = layout->pcell_by_name (tl::to_string (cell_name_le->text ()).c_str ());
tl_assert (ci.first || pci.first);
// reference the library
if (lib) {
layout = & cv->layout ();
inst_cell_index = layout->get_lib_proxy (lib, inst_cell_index);
}
db::cell_index_type inst_cell_index = 0;
if (inst_cell_index != pos->back ().inst_ptr.cell_index ()) {
appl->add (new ChangeTargetCellApplicator (inst_cell_index));
// instantiate the PCell
if (pci.first) {
tl_assert (mp_pcell_parameters != 0);
tl_assert (layout->pcell_declaration (pci.second) == mp_pcell_parameters->pcell_decl ());
inst_cell_index = layout->get_pcell_variant (pci.second, mp_pcell_parameters->get_parameters ());
} else {
inst_cell_index = ci.second;
}
// reference the library
if (lib) {
layout = & cv->layout ();
inst_cell_index = layout->get_lib_proxy (lib, inst_cell_index);
}
if (inst_cell_index != pos->back ().inst_ptr.cell_index ()) {
appl->add (new ChangeTargetCellApplicator (inst_cell_index));
}
} catch (tl::Exception &ex) {
has_pcell_error = true;
}
double x = 0.0, y = 0.0;
tl::from_string (tl::to_string (pos_x_le->text ()), x);
tl::from_string (tl::to_string (pos_y_le->text ()), y);
try {
tl::from_string (tl::to_string (pos_x_le->text ()), x);
lay::indicate_error (pos_x_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (pos_x_le, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (pos_y_le->text ()), y);
lay::indicate_error (pos_y_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (pos_y_le, &ex);
has_error = true;
}
db::DCplxTrans t;
if (abs_cb->isChecked ()) {
@ -381,20 +470,32 @@ InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance &
bool mirror = mirror_cbx->isChecked ();
double angle = 0.0;
tl::from_string (tl::to_string (angle_le->text ()), angle);
try {
tl::from_string (tl::to_string (angle_le->text ()), angle);
lay::indicate_error (angle_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (angle_le, &ex);
has_error = true;
}
double mag = 0.0;
tl::from_string (tl::to_string (mag_le->text ()), mag);
try {
tl::from_string (tl::to_string (mag_le->text ()), mag);
lay::indicate_error (mag_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (mag_le, &ex);
has_error = true;
}
angle -= (floor (angle / 360.0) + 1.0) * 360.0;
while (angle < -1e-6) {
angle += 360.0;
}
db::CellInstArray::complex_trans_type tr = pos->back ().inst_ptr.complex_trans ();
db::CellInstArray::complex_trans_type trans = pos->back ().inst_ptr.complex_trans ();
if (fabs (angle - tr.angle ()) > 1e-6 || mirror != tr.is_mirror () || fabs (mag - tr.mag ()) > 1e-6 || ! disp.equal (tr.disp () * dbu)) {
appl->add (new ChangeInstanceTransApplicator (angle, tr.angle (), mirror, tr.is_mirror (), mag, tr.mag (), disp, tr.disp () * dbu));
if (fabs (angle - trans.angle ()) > 1e-6 || mirror != trans.is_mirror () || fabs (mag - trans.mag ()) > 1e-6 || ! disp.equal (trans.disp () * dbu)) {
appl->add (new ChangeInstanceTransApplicator (angle, trans.angle (), mirror, trans.is_mirror (), mag, trans.mag (), disp, trans.disp () * dbu));
}
db::CellInstArray::vector_type a_org, b_org;
@ -407,12 +508,53 @@ InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance &
double rx = 0.0, ry = 0.0;
unsigned long rows = 0, cols = 0;
tl::from_string (tl::to_string (column_x_le->text ()), cx);
tl::from_string (tl::to_string (column_y_le->text ()), cy);
tl::from_string (tl::to_string (row_x_le->text ()), rx);
tl::from_string (tl::to_string (row_y_le->text ()), ry);
tl::from_string (tl::to_string (rows_le->text ()), rows);
tl::from_string (tl::to_string (columns_le->text ()), cols);
try {
tl::from_string (tl::to_string (column_x_le->text ()), cx);
lay::indicate_error (column_x_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (column_x_le, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (column_y_le->text ()), cy);
lay::indicate_error (column_y_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (column_y_le, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (row_x_le->text ()), rx);
lay::indicate_error (row_x_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (row_x_le, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (row_y_le->text ()), ry);
lay::indicate_error (row_y_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (row_y_le, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (rows_le->text ()), rows);
lay::indicate_error (rows_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (rows_le, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (columns_le->text ()), cols);
lay::indicate_error (columns_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (columns_le, &ex);
has_error = true;
}
db::DVector rv = db::DVector (dpoint_from_dpoint (db::DPoint (rx, ry), dbu, du, t));
db::DVector cv = db::DVector (dpoint_from_dpoint (db::DPoint (cx, cy), dbu, du, t));
@ -432,6 +574,14 @@ InstPropertiesPage::create_applicator (db::Cell & /*cell*/, const db::Instance &
}
if (has_error || has_pcell_error) {
throw tl::Exception (tl::to_string (tr ("At least one value and PCell parameter is not correct - see hightlighted entry fields or the PCell error indicator")));
} else if (has_error) {
throw tl::Exception (tl::to_string (tr ("At least one value is not correct - see hightlighted entry fields")));
} else if (has_pcell_error) {
throw tl::Exception (tl::to_string (tr ("At least one PCell parameter is not correct - see hightlighted entry fields or the PCell error indicator")));
}
return appl.release ();
}
@ -453,7 +603,7 @@ InstPropertiesPage::recompute_selection_ptrs (const std::vector<lay::ObjectInstP
}
void
InstPropertiesPage::do_apply (bool current_only)
InstPropertiesPage::do_apply (bool current_only, bool relative)
{
lay::LayerState layer_state = mp_service->view ()->layer_snapshot ();
unsigned int cv_index = m_selection_ptrs [m_index]->cv_index ();
@ -482,32 +632,7 @@ InstPropertiesPage::do_apply (bool current_only)
bool relative_mode = false;
if (! current_only && applicator->supports_relative_mode ()) {
static bool s_relative_mode = true;
QMessageBox mb (QMessageBox::Question,
tr ("Apply Changes To All"),
tr ("For this operation absolute or relative mode is available which affects the way parameters of the selected objects are changed:\n\n"
"In absolute mode, they will be set to the given value. In relative mode, they will be adjusted by the same amount.\n"),
QMessageBox::NoButton, this);
mb.addButton (tr ("Cancel"), QMessageBox::RejectRole);
QPushButton *absolute = mb.addButton (tr ("Absolute"), QMessageBox::NoRole);
QPushButton *relative = mb.addButton (tr ("Relative"), QMessageBox::YesRole);
mb.setDefaultButton (s_relative_mode ? relative : absolute);
mb.exec ();
if (mb.clickedButton () == absolute) {
s_relative_mode = relative_mode = false;
} else if (mb.clickedButton () == relative) {
s_relative_mode = relative_mode = true;
} else {
// Cancel pressed
return;
}
relative_mode = relative;
}
// Note: using the apply-all scheme for applying a single change may look like overhead.
@ -599,7 +724,7 @@ InstPropertiesPage::do_apply (bool current_only)
void
InstPropertiesPage::apply ()
{
do_apply (true);
do_apply (true, false);
}
bool
@ -609,9 +734,9 @@ InstPropertiesPage::can_apply_to_all () const
}
void
InstPropertiesPage::apply_to_all ()
InstPropertiesPage::apply_to_all (bool relative)
{
do_apply (false);
do_apply (false, relative);
}
void
@ -636,16 +761,13 @@ InstPropertiesPage::update_pcell_parameters ()
std::pair<bool, db::pcell_id_type> pc = layout->pcell_by_name (tl::to_string (cell_name_le->text ()).c_str ());
std::pair<bool, db::cell_index_type> cc = layout->cell_by_name (tl::to_string (cell_name_le->text ()).c_str ());
// by the way, update the foreground color of the cell edit box as well (red, if not valid)
QPalette pl = cell_name_le->palette ();
// indicate an invalid cell name
if (! pc.first && ! cc.first) {
pl.setColor (QPalette::Text, Qt::red);
pl.setColor (QPalette::Base, QColor (Qt::red).lighter (180));
tl::Exception ex (tl::to_string (QObject::tr ("Not a valid cell or PCell name: %s")).c_str (), tl::to_string (cell_name_le->text ()).c_str ());
lay::indicate_error (cell_name_le, &ex);
} else {
pl.setColor (QPalette::Text, palette ().color (QPalette::Text));
pl.setColor (QPalette::Base, palette ().color (QPalette::Base));
lay::indicate_error (cell_name_le, 0);
}
cell_name_le->setPalette (pl);
if (pc.first && layout->pcell_declaration (pc.second)) {
@ -686,13 +808,14 @@ InstPropertiesPage::update_pcell_parameters ()
mp_pcell_parameters->deleteLater ();
}
mp_pcell_parameters = new PCellParametersPage (pcell_tab, &cv->layout (), mp_service->view (), pos->cv_index (), layout->pcell_declaration (pc.second), parameters);
mp_pcell_parameters = new PCellParametersPage (pcell_tab);
connect (mp_pcell_parameters, SIGNAL (edited ()), this, SIGNAL (edited ()));
mp_pcell_parameters->setup (&cv->layout (), mp_service->view (), pos->cv_index (), layout->pcell_declaration (pc.second), parameters);
pcell_tab->layout ()->addWidget (mp_pcell_parameters);
}
param_tab_widget->setTabEnabled (1, true);
param_tab_widget->setCurrentIndex (1);
} else {
@ -704,8 +827,11 @@ InstPropertiesPage::update_pcell_parameters ()
mp_pcell_parameters = 0;
param_tab_widget->setCurrentIndex (0);
if (param_tab_widget->currentIndex () == 1) {
param_tab_widget->setCurrentIndex (0);
}
param_tab_widget->setTabEnabled (1, false);
}
}

View File

@ -68,9 +68,9 @@ protected:
virtual bool readonly ();
virtual void apply ();
virtual void apply_to_all ();
virtual void apply_to_all (bool relative);
virtual bool can_apply_to_all () const;
void do_apply (bool current_only);
void do_apply (bool current_only, bool relative);
virtual ChangeApplicator *create_applicator (db::Cell &cell, const db::Instance &inst, double dbu);
protected slots:

View File

@ -30,7 +30,11 @@
#include "tlExceptions.h"
#include "layLayoutView.h"
#include "layDialogs.h"
#include "laySelector.h"
#include "layCellSelectionForm.h"
#include "layFinder.h"
#include "layLayerProperties.h"
#include "layLayerTreeModel.h"
#include "tlProgress.h"
#include "edtPlugin.h"
#include "edtMainService.h"
@ -39,8 +43,11 @@
#include "edtConfig.h"
#include "edtDialogs.h"
#include "edtEditorOptionsPages.h"
#include "edtDistribute.h"
#include <QMessageBox>
#include <QFontInfo>
#include <QWidgetAction>
namespace edt
{
@ -58,12 +65,18 @@ MainService::MainService (db::Manager *manager, lay::LayoutView *view, lay::Disp
m_flatten_insts_levels (std::numeric_limits<int>::max ()),
m_flatten_prune (false),
m_align_hmode (0), m_align_vmode (0), m_align_visible_layers (false),
m_hdistribute (true),
m_distribute_hmode (1), m_distribute_hpitch (0.0), m_distribute_hspace (0.0),
m_vdistribute (true),
m_distribute_vmode (1), m_distribute_vpitch (0.0), m_distribute_vspace (0.0),
m_distribute_visible_layers (false),
m_origin_mode_x (-1), m_origin_mode_y (-1), m_origin_visible_layers_for_bbox (false),
m_array_a (0.0, 1.0), m_array_b (1.0, 0.0),
m_array_na (1), m_array_nb (1),
m_router (0.0), m_rinner (0.0), m_npoints (64), m_undo_before_apply (true),
mp_round_corners_dialog (0),
mp_align_options_dialog (0),
mp_distribute_options_dialog (0),
mp_flatten_inst_options_dialog (0),
mp_make_cell_options_dialog (0),
mp_make_array_options_dialog (0)
@ -94,6 +107,15 @@ MainService::align_options_dialog ()
return mp_align_options_dialog;
}
edt::DistributeOptionsDialog *
MainService::distribute_options_dialog ()
{
if (! mp_distribute_options_dialog) {
mp_distribute_options_dialog = new edt::DistributeOptionsDialog (view ());
}
return mp_distribute_options_dialog;
}
lay::FlattenInstOptionsDialog *
MainService::flatten_inst_options_dialog ()
{
@ -128,10 +150,10 @@ MainService::menu_activated (const std::string &symbol)
cm_descend ();
} else if (symbol == "edt::ascend") {
cm_ascend ();
} else if (symbol == "edt::edit_options") {
cm_edit_options ();
} else if (symbol == "edt::sel_align") {
cm_align ();
} else if (symbol == "edt::sel_distribute") {
cm_distribute ();
} else if (symbol == "edt::sel_tap") {
cm_tap ();
} else if (symbol == "edt::sel_round_corners") {
@ -1835,7 +1857,115 @@ MainService::cm_align ()
}
}
void
void
MainService::cm_distribute ()
{
tl_assert (view ()->is_editable ());
check_no_guiding_shapes ();
std::vector<edt::Service *> edt_services = view ()->get_plugins <edt::Service> ();
if (! distribute_options_dialog ()->exec_dialog (view (), m_hdistribute, m_distribute_hmode, m_distribute_hpitch, m_distribute_hspace,
m_vdistribute, m_distribute_vmode, m_distribute_vpitch, m_distribute_vspace,
m_distribute_visible_layers)) {
return;
}
if (! m_hdistribute && ! m_vdistribute) {
return;
}
// count the items
size_t n = 0;
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
for (edt::Service::obj_iterator s = (*es)->selection ().begin (); s != (*es)->selection ().end (); ++s) {
++n;
}
}
std::vector<std::pair<size_t, size_t> > objects_for_service;
std::vector<db::DCplxTrans> transformations;
{
std::vector<db::DBox> org_boxes;
org_boxes.reserve (n);
edt::distributed_placer<db::DBox, size_t> placer;
placer.reserve (n);
size_t i = 0;
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
objects_for_service.push_back (std::make_pair (i, i));
for (edt::Service::obj_iterator s = (*es)->selection ().begin (); s != (*es)->selection ().end (); ++s) {
const db::Layout &layout = view ()->cellview (s->cv_index ())->layout ();
db::CplxTrans tr = db::CplxTrans (layout.dbu ()) * s->trans ();
db::DBox box;
if (! s->is_cell_inst ()) {
box = tr * s->shape ().bbox ();
} else {
box = inst_bbox (tr, view (), s->cv_index (), s->back (), m_distribute_visible_layers);
}
org_boxes.push_back (box);
placer.insert (box, i);
++i;
}
objects_for_service.back ().second = i;
}
int href = int (m_distribute_hmode - 2);
int vref = 2 - int (m_distribute_vmode);
if (m_hdistribute && m_vdistribute) {
placer.distribute_matrix (href, m_distribute_hpitch, m_distribute_hspace,
vref, m_distribute_vpitch, m_distribute_vspace);
} else if (m_hdistribute) {
placer.distribute_h (href, vref, m_distribute_hpitch, m_distribute_hspace);
} else if (m_vdistribute) {
placer.distribute_v (vref, href, m_distribute_vpitch, m_distribute_vspace);
}
transformations.resize (org_boxes.size ());
for (edt::distributed_placer<db::DBox, size_t>::iterator i = placer.begin (); i != placer.end (); ++i) {
transformations[i->second] = db::DCplxTrans (i->first.p1 () - org_boxes[i->second].p1 ());
}
}
{
view ()->cancel_edits ();
manager ()->transaction (tl::to_string (QObject::tr ("Distribution")));
// do the distribution
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
size_t ie = es - edt_services.begin ();
// create a transformation vector that describes each shape's transformation
std::vector <db::DCplxTrans> tv (transformations.begin () + objects_for_service [ie].first, transformations.begin () + objects_for_service [ie].second);
// use the "transform" method to transform the shapes and instances (with individual transformations)
(*es)->transform (db::DCplxTrans () /*dummy*/, &tv);
}
manager ()->commit ();
}
}
void
MainService::cm_make_array ()
{
size_t n = 0;
@ -1941,21 +2071,67 @@ MainService::cm_make_array ()
void
MainService::cm_tap ()
{
tl_assert (view ()->is_editable ());
check_no_guiding_shapes ();
if (! view ()->view_object_widget ()->mouse_in_window ()) {
return;
}
std::vector<edt::Service *> edt_services = view ()->get_plugins <edt::Service> ();
lay::ShapeFinder finder (true /*point mode*/, false /*all hierarchy levels*/, db::ShapeIterator::All, 0);
// get (common) cellview index of the selected shapes
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
for (edt::Service::obj_iterator s = (*es)->selection ().begin (); s != (*es)->selection ().end (); ++s) {
const lay::CellView &cv = view ()->cellview (s->cv_index ());
if (cv.is_valid () && ! s->is_cell_inst ()) {
view ()->set_current_layer (s->cv_index (), cv->layout ().get_properties (s->layer ()));
return;
}
// capture all objects in point mode (default: capture one only)
finder.set_catch_all (true);
// go through all visible layers of all cellviews
db::DPoint pt = view ()->view_object_widget ()->mouse_position_um ();
finder.find (view (), db::DBox (pt, pt));
std::set<std::pair<unsigned int, unsigned int> > layers_in_selection;
for (lay::ShapeFinder::iterator f = finder.begin (); f != finder.end (); ++f) {
// ignore guiding shapes
if (f->layer () != view ()->cellview (f->cv_index ())->layout ().guiding_shape_layer ()) {
layers_in_selection.insert (std::make_pair (f->cv_index (), f->layer ()));
}
}
std::vector<lay::LayerPropertiesConstIterator> tapped_layers;
for (lay::LayerPropertiesConstIterator lp = view ()->begin_layers (view ()->current_layer_list ()); ! lp.at_end (); ++lp) {
const lay::LayerPropertiesNode *ln = lp.operator-> ();
if (layers_in_selection.find (std::make_pair ((unsigned int) ln->cellview_index (), (unsigned int) ln->layer_index ())) != layers_in_selection.end ()) {
tapped_layers.push_back (lp);
}
}
if (tapped_layers.empty ()) {
return;
}
// List the layers under the cursor as pop up a menu
std::auto_ptr<QMenu> menu (new QMenu (view ()));
menu->show ();
int icon_size = menu->style ()->pixelMetric (QStyle::PM_ButtonIconSize);
QPoint mp = view ()->view_object_widget ()->mapToGlobal (view ()->view_object_widget ()->mouse_position ());
for (std::vector<lay::LayerPropertiesConstIterator>::const_iterator l = tapped_layers.begin (); l != tapped_layers.end (); ++l) {
QAction *a = menu->addAction (lay::LayerTreeModel::icon_for_layer (*l, view (), icon_size, icon_size, 0, true), tl::to_qstring ((*l)->source (true).to_string ()));
a->setData (int (l - tapped_layers.begin ()));
}
QAction *action = menu->exec (mp);
if (action) {
int index = action->data ().toInt ();
lay::LayerPropertiesConstIterator iter = tapped_layers [index];
view ()->set_current_layer (iter);
edt::Service *es = dynamic_cast<edt::Service *> (view ()->view_object_widget ()->active_service ());
if (es) {
es->tap (pt);
}
}
}
void
@ -1966,16 +2142,12 @@ MainService::cm_change_layer ()
int cv_index = -1;
std::vector<edt::Service *> edt_services = view ()->get_plugins <edt::Service> ();
// get (common) cellview index of the selected shapes
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
for (edt::Service::obj_iterator s = (*es)->selection ().begin (); s != (*es)->selection ().end (); ++s) {
if (cv_index >= 0 && cv_index != int (s->cv_index ())) {
throw tl::Exception (tl::to_string (QObject::tr ("Selections originate from different layouts - cannot switch layer in this case.")));
}
cv_index = int (s->cv_index ());
for (SelectionIterator s (view ()); ! s.at_end (); ++s) {
if (cv_index >= 0 && cv_index != int (s->cv_index ())) {
throw tl::Exception (tl::to_string (QObject::tr ("Selections originate from different layouts - cannot switch layer in this case.")));
}
cv_index = int (s->cv_index ());
}
if (cv_index >= 0) {
@ -2037,46 +2209,42 @@ MainService::cm_change_layer ()
// Insert and delete the shape. This exploits the fact, that a shape can be erased multiple times -
// this is important since the selection potentially contains the same shape multiple times.
for (std::vector<edt::Service *>::const_iterator es = edt_services.begin (); es != edt_services.end (); ++es) {
for (SelectionIterator s (view ()); ! s.at_end (); ++s) {
for (edt::Service::obj_iterator s = (*es)->selection ().begin (); s != (*es)->selection ().end (); ++s) {
if (!s->is_cell_inst () && int (s->layer ()) != layer) {
if (!s->is_cell_inst () && int (s->layer ()) != layer) {
db::Cell &cell = layout.cell (s->cell_index ());
if (cell.shapes (s->layer ()).is_valid (s->shape ())) {
cell.shapes (layer).insert (s->shape ());
cell.shapes (s->layer ()).erase_shape (s->shape ());
}
db::Cell &cell = layout.cell (s->cell_index ());
if (cell.shapes (s->layer ()).is_valid (s->shape ())) {
cell.shapes (layer).insert (s->shape ());
cell.shapes (s->layer ()).erase_shape (s->shape ());
}
} else if (s->is_cell_inst ()) {
} else if (s->is_cell_inst ()) {
// If the selected object is a PCell instance, and there is exactly one visible, writable layer type parameter, change this one
// If the selected object is a PCell instance, and there is exactly one visible, writable layer type parameter, change this one
db::Instance inst = s->back ().inst_ptr;
db::Cell &cell = layout.cell (s->cell_index ());
db::Instance inst = s->back ().inst_ptr;
db::Cell &cell = layout.cell (s->cell_index ());
if (cell.is_valid (inst)) {
if (cell.is_valid (inst)) {
const db::PCellDeclaration *pcell_decl = layout.pcell_declaration_for_pcell_variant (inst.cell_index ());
if (pcell_decl) {
const db::PCellDeclaration *pcell_decl = layout.pcell_declaration_for_pcell_variant (inst.cell_index ());
if (pcell_decl) {
size_t layer_par_index = 0;
int n_layer_par = 0;
for (std::vector<db::PCellParameterDeclaration>::const_iterator d = pcell_decl->parameter_declarations ().begin (); d != pcell_decl->parameter_declarations ().end () && n_layer_par < 2; ++d) {
if (d->get_type () == db::PCellParameterDeclaration::t_layer && !d->is_hidden () && !d->is_readonly ()) {
++n_layer_par;
layer_par_index = size_t (d - pcell_decl->parameter_declarations ().begin ());
}
}
if (n_layer_par == 1) {
std::vector<tl::Variant> parameters = cell.get_pcell_parameters (inst);
tl_assert (layer_par_index < parameters.size ());
parameters [layer_par_index] = layout.get_properties (layer);
cell.change_pcell_parameters (inst, parameters);
size_t layer_par_index = 0;
int n_layer_par = 0;
for (std::vector<db::PCellParameterDeclaration>::const_iterator d = pcell_decl->parameter_declarations ().begin (); d != pcell_decl->parameter_declarations ().end () && n_layer_par < 2; ++d) {
if (d->get_type () == db::PCellParameterDeclaration::t_layer && !d->is_hidden () && !d->is_readonly ()) {
++n_layer_par;
layer_par_index = size_t (d - pcell_decl->parameter_declarations ().begin ());
}
}
if (n_layer_par == 1) {
std::vector<tl::Variant> parameters = cell.get_pcell_parameters (inst);
tl_assert (layer_par_index < parameters.size ());
parameters [layer_par_index] = layout.get_properties (layer);
cell.change_pcell_parameters (inst, parameters);
}
}
@ -2101,12 +2269,6 @@ MainService::cm_change_layer ()
}
void
MainService::cm_edit_options ()
{
show_editor_options_dialog ();
}
void
MainService::check_no_guiding_shapes ()
{

View File

@ -51,6 +51,7 @@ class RoundCornerOptionsDialog;
class MakeCellOptionsDialog;
class MakeArrayOptionsDialog;
class AlignOptionsDialog;
class DistributeOptionsDialog;
// -------------------------------------------------------------
@ -93,11 +94,6 @@ public:
*/
void cm_ascend ();
/**
* @brief Edit object options
*/
void cm_edit_options ();
/**
* @brief Change the layer of the shapes in the selection
*/
@ -153,6 +149,11 @@ public:
*/
void cm_align ();
/**
* @brief Distribute the selected shapes and instances
*/
void cm_distribute ();
/**
* @brief Flatten instances
*/
@ -205,6 +206,13 @@ private:
int m_align_hmode;
int m_align_vmode;
bool m_align_visible_layers;
bool m_hdistribute;
int m_distribute_hmode;
double m_distribute_hpitch, m_distribute_hspace;
bool m_vdistribute;
int m_distribute_vmode;
double m_distribute_vpitch, m_distribute_vspace;
bool m_distribute_visible_layers;
std::string m_make_cell_name;
int m_origin_mode_x, m_origin_mode_y;
bool m_origin_visible_layers_for_bbox;
@ -215,6 +223,7 @@ private:
bool m_undo_before_apply;
edt::RoundCornerOptionsDialog *mp_round_corners_dialog;
edt::AlignOptionsDialog *mp_align_options_dialog;
edt::DistributeOptionsDialog *mp_distribute_options_dialog;
lay::FlattenInstOptionsDialog *mp_flatten_inst_options_dialog;
edt::MakeCellOptionsDialog *mp_make_cell_options_dialog;
edt::MakeArrayOptionsDialog *mp_make_array_options_dialog;
@ -223,6 +232,7 @@ private:
void check_no_guiding_shapes ();
edt::RoundCornerOptionsDialog *round_corners_dialog ();
edt::AlignOptionsDialog *align_options_dialog ();
edt::DistributeOptionsDialog *distribute_options_dialog ();
lay::FlattenInstOptionsDialog *flatten_inst_options_dialog ();
edt::MakeCellOptionsDialog *make_cell_options_dialog ();
edt::MakeArrayOptionsDialog *make_array_options_dialog ();

View File

@ -1,65 +0,0 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "edtPCellParametersDialog.h"
#include <QPushButton>
namespace edt
{
PCellParametersDialog::PCellParametersDialog (QWidget *parent)
: QDialog (parent)
{
setupUi (this);
connect (buttons->button (QDialogButtonBox::Apply), SIGNAL (clicked ()), this, SLOT (apply_pressed ()));
}
void
PCellParametersDialog::apply_pressed ()
{
emit parameters_changed ();
parameters_changed_event ();
}
std::vector<tl::Variant>
PCellParametersDialog::get_parameters ()
{
return parameters->get_parameters ();
}
void
PCellParametersDialog::set_parameters (const std::vector<tl::Variant> &p)
{
parameters->set_parameters (p);
}
int
PCellParametersDialog::exec (const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type &p)
{
parameters->setup (layout, view, cv_index, pcell_decl, p);
return QDialog::exec ();
}
}

View File

@ -1,86 +0,0 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_edtPCellParametersDialog
#define HDR_edtPCellParametersDialog
#include "dbPCellDeclaration.h"
#include "ui_PCellParametersDialog.h"
#include <QDialog>
namespace lay
{
class LayoutView;
}
namespace edt
{
/**
* @brief A QScrollArea that displays and allows editing PCell parameters
*/
class PCellParametersDialog
: public QDialog, private Ui::PCellParametersDialog
{
Q_OBJECT
public:
/**
* @brief Constructor: create a dialog showing the given parameters
* @param parent The parent widget
*/
PCellParametersDialog (QWidget *parent);
/**
* @brief Executes the parameter dialog
* @param layout The layout in which the PCell instance resides
* @param view The layout view from which to take layers for example
* @param cv_index The index of the cellview in "view"
* @param pcell_decl The PCell declaration
* @param parameters The parameter values to show (if empty, the default values are used)
*/
int exec (const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type &p);
/**
* @brief Get the current parameters
*/
std::vector<tl::Variant> get_parameters ();
/**
* @brief Sets the given parameters as values
*/
void set_parameters (const std::vector<tl::Variant> &values);
tl::Event parameters_changed_event;
signals:
void parameters_changed ();
private slots:
void apply_pressed ();
};
}
#endif

View File

@ -22,7 +22,9 @@
#include "edtPCellParametersPage.h"
#include "edtPropertiesPageUtils.h"
#include "layWidgets.h"
#include "layQtTools.h"
#include "tlScriptError.h"
#include <QFrame>
@ -142,15 +144,8 @@ static void set_value (const db::PCellParameterDeclaration &p, const db::Layout
}
}
PCellParametersPage::PCellParametersPage (QWidget *parent, const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type &parameters)
: QFrame (parent)
{
init ();
setup (layout, view, cv_index, pcell_decl, parameters);
}
PCellParametersPage::PCellParametersPage (QWidget *parent)
: QFrame (parent)
PCellParametersPage::PCellParametersPage (QWidget *parent, bool dense)
: QFrame (parent), m_dense (dense), dm_parameter_changed (this, &PCellParametersPage::do_parameter_changed)
{
init ();
}
@ -165,6 +160,8 @@ PCellParametersPage::init ()
mp_parameters_area = 0;
QGridLayout *frame_layout = new QGridLayout (this);
// spacing and margin for tool windows
frame_layout->setMargin (0);
setLayout (frame_layout);
mp_error_icon = new QLabel (this);
@ -173,6 +170,7 @@ PCellParametersPage::init ()
frame_layout->addWidget (mp_error_icon, 1, 0, 1, 1);
mp_error_label = new QLabel (this);
mp_error_label->setWordWrap (true);
QPalette palette = mp_error_label->palette ();
palette.setColor (QPalette::Foreground, Qt::red);
mp_error_label->setPalette (palette);
@ -200,6 +198,7 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int
m_widgets.clear ();
mp_parameters_area = new QScrollArea (this);
mp_parameters_area->setFrameShape (QFrame::NoFrame);
QGridLayout *frame_layout = dynamic_cast<QGridLayout *> (QFrame::layout ());
frame_layout->addWidget (mp_parameters_area, 0, 0, 1, 2);
frame_layout->setRowStretch (0, 1);
@ -211,6 +210,11 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int
QGridLayout *inner_grid = new QGridLayout (inner_frame);
inner_frame->setLayout (inner_grid);
if (m_dense) {
inner_grid->setMargin (4);
inner_grid->setHorizontalSpacing (6);
inner_grid->setVerticalSpacing (2);
}
QWidget *main_frame = inner_frame;
QGridLayout *main_grid = inner_grid;
@ -247,6 +251,11 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int
main_grid->addWidget (gb, main_row, 0, 1, 2);
inner_grid = new QGridLayout (gb);
if (m_dense) {
inner_grid->setMargin (4);
inner_grid->setHorizontalSpacing (6);
inner_grid->setVerticalSpacing (2);
}
gb->setLayout (inner_grid);
inner_frame = gb;
@ -286,11 +295,13 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int
QHBoxLayout *hb = new QHBoxLayout (f);
hb->setMargin (0);
f->setLayout (hb);
f->setFrameShape (QFrame::NoFrame);
QLineEdit *le = new QLineEdit (f);
le->setEnabled (! p->is_readonly ());
hb->addWidget (le);
le->setMaximumWidth (150);
le->setObjectName (tl::to_qstring (p->get_name ()));
m_widgets.push_back (le);
QLabel *ul = new QLabel (f);
@ -299,7 +310,7 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int
inner_grid->addWidget (f, row, 1);
connect (le, SIGNAL (editingFinished ()), this, SLOT (activated ()));
connect (le, SIGNAL (editingFinished ()), this, SLOT (parameter_changed ()));
}
break;
@ -309,10 +320,11 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int
{
QLineEdit *le = new QLineEdit (inner_frame);
le->setEnabled (! p->is_readonly ());
le->setObjectName (tl::to_qstring (p->get_name ()));
m_widgets.push_back (le);
inner_grid->addWidget (le, row, 1);
connect (le, SIGNAL (editingFinished ()), this, SLOT (activated ()));
connect (le, SIGNAL (editingFinished ()), this, SLOT (parameter_changed ()));
}
break;
@ -322,19 +334,25 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int
ly->setEnabled (! p->is_readonly ());
ly->set_no_layer_available (true);
ly->set_view (mp_view, m_cv_index, true /*all layers*/);
ly->setObjectName (tl::to_qstring (p->get_name ()));
m_widgets.push_back (ly);
inner_grid->addWidget (ly, row, 1);
connect (ly, SIGNAL (activated (int)), this, SLOT (parameter_changed ()));
}
break;
case db::PCellParameterDeclaration::t_boolean:
{
QCheckBox *cbx = new QCheckBox (inner_frame);
// this makes the checkbox not stretch over the full width - better when navigating with tab
cbx->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Preferred));
cbx->setEnabled (! p->is_readonly ());
cbx->setObjectName (tl::to_qstring (p->get_name ()));
m_widgets.push_back (cbx);
inner_grid->addWidget (cbx, row, 1);
connect (cbx, SIGNAL (stateChanged (int)), this, SLOT (activated ()));
connect (cbx, SIGNAL (stateChanged (int)), this, SLOT (parameter_changed ()));
}
break;
@ -346,6 +364,7 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int
} else {
QComboBox *cb = new QComboBox (inner_frame);
cb->setObjectName (tl::to_qstring (p->get_name ()));
int i = 0;
for (std::vector<tl::Variant>::const_iterator c = p->get_choices ().begin (); c != p->get_choices ().end (); ++c, ++i) {
@ -356,7 +375,8 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int
}
}
connect (cb, SIGNAL (activated (int)), this, SLOT (activated ()));
connect (cb, SIGNAL (activated (int)), this, SLOT (parameter_changed ()));
cb->setEnabled (! p->is_readonly ());
cb->setMinimumContentsLength (30);
cb->setSizeAdjustPolicy (QComboBox::AdjustToMinimumContentsLengthWithIcon);
@ -377,17 +397,24 @@ PCellParametersPage::setup (const db::Layout *layout, lay::LayoutView *view, int
mp_parameters_area->setWidget (main_frame);
main_frame->show ();
// does a first coerce and update
get_parameters ();
// does a first coerce and update. Ignore errors for now.
bool ok = false;
get_parameters (&ok);
}
PCellParametersPage::State
PCellParametersPage::get_state ()
{
State s;
s.valid = true;
s.vScrollPosition = mp_parameters_area->verticalScrollBar ()->value ();
s.hScrollPosition = mp_parameters_area->horizontalScrollBar ()->value ();
if (focusWidget ()) {
s.focusWidget = focusWidget ()->objectName ();
}
return s;
}
@ -395,29 +422,42 @@ void
PCellParametersPage::set_state (const State &s)
{
if (s.valid) {
mp_parameters_area->verticalScrollBar ()->setValue (s.vScrollPosition);
mp_parameters_area->horizontalScrollBar ()->setValue (s.hScrollPosition);
if (! s.focusWidget.isEmpty ()) {
QWidget *c = findChild<QWidget *> (s.focusWidget);
if (c) {
c->setFocus ();
}
}
}
}
void
PCellParametersPage::activated ()
PCellParametersPage::parameter_changed ()
{
// does a coerce and update
get_parameters ();
dm_parameter_changed ();
}
void
PCellParametersPage::clicked ()
void
PCellParametersPage::do_parameter_changed ()
{
// does a coerce and update
get_parameters ();
bool ok = false;
get_parameters (&ok);
if (ok) {
emit edited ();
}
}
std::vector<tl::Variant>
PCellParametersPage::get_parameters ()
PCellParametersPage::get_parameters (bool *ok)
{
std::vector<tl::Variant> parameters;
bool edit_error = true;
int r = 0;
const std::vector<db::PCellParameterDeclaration> &pcp = mp_pcell_decl->parameter_declarations ();
@ -443,9 +483,22 @@ PCellParametersPage::get_parameters ()
{
QLineEdit *le = dynamic_cast<QLineEdit *> (m_widgets [r]);
if (le) {
int v = 0;
tl::from_string (tl::to_string (le->text ()), v);
parameters.back () = tl::Variant (v);
try {
int v = 0;
tl::from_string (tl::to_string (le->text ()), v);
parameters.back () = tl::Variant (v);
lay::indicate_error (le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (le, &ex);
edit_error = false;
}
}
}
break;
@ -454,9 +507,22 @@ PCellParametersPage::get_parameters ()
{
QLineEdit *le = dynamic_cast<QLineEdit *> (m_widgets [r]);
if (le) {
double v = 0;
tl::from_string (tl::to_string (le->text ()), v);
parameters.back () = tl::Variant (v);
try {
double v = 0;
tl::from_string (tl::to_string (le->text ()), v);
parameters.back () = tl::Variant (v);
lay::indicate_error (le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (le, &ex);
edit_error = false;
}
}
}
break;
@ -516,22 +582,43 @@ PCellParametersPage::get_parameters ()
try {
if (! edit_error) {
throw tl::Exception (tl::to_string (tr ("There are errors. See the highlighted edit fields for details.")));
}
// coerce the parameters
mp_pcell_decl->coerce_parameters (*mp_layout, parameters);
set_parameters (parameters);
mp_error_label->hide ();
mp_error_icon->hide ();
if (ok) {
*ok = true;
}
} catch (tl::ScriptError &ex) {
mp_error_label->setText (tl::to_qstring (ex.basic_msg ()));
mp_error_label->setToolTip (tl::to_qstring (ex.msg ()));
mp_error_icon->show ();
mp_error_label->show ();
if (ok) {
mp_error_label->setText (tl::to_qstring (ex.basic_msg ()));
mp_error_label->setToolTip (tl::to_qstring (ex.msg ()));
mp_error_icon->show ();
mp_error_label->show ();
*ok = false;
} else {
throw;
}
} catch (tl::Exception &ex) {
mp_error_label->setText (tl::to_qstring (ex.msg ()));
mp_error_icon->show ();
mp_error_label->show ();
if (ok) {
mp_error_label->setText (tl::to_qstring (ex.msg ()));
mp_error_icon->show ();
mp_error_label->show ();
*ok = false;
} else {
throw;
}
}
@ -539,7 +626,7 @@ PCellParametersPage::get_parameters ()
}
void
PCellParametersPage::set_parameters (const std::vector<tl::Variant> &parameters)
PCellParametersPage::set_parameters (const std::vector<tl::Variant> &parameters)
{
// write the changed value back
size_t r = 0;

View File

@ -25,6 +25,7 @@
#define HDR_edtPCellParametersPage
#include "dbPCellDeclaration.h"
#include "tlDeferredExecution.h"
#include <QFrame>
#include <QScrollArea>
@ -42,7 +43,7 @@ namespace edt
* @brief A QScrollArea that displays and allows editing PCell parameters
*/
class PCellParametersPage
: public QFrame
: public QFrame, public tl::Object
{
Q_OBJECT
@ -54,31 +55,28 @@ public:
bool valid;
int hScrollPosition;
int vScrollPosition;
QString focusWidget;
};
/**
* @brief Constructor: creates a page showing the given parameters
*
* @param parent The parent widget
* @param layout The layout in which the PCell instance resides
* @param view The layout view from which to take layers for example
* @param cv_index The index of the cellview in "view"
* @param pcell_decl The PCell declaration
* @param parameters The parameter values to show (if empty, the default values are used)
*/
PCellParametersPage (QWidget *parent, const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type &parameters);
/**
* @brief Default constructor
*
* Use "setup" to configure the page.
*
* @param dense Use a dense layout if true
*/
PCellParametersPage (QWidget *parent);
PCellParametersPage (QWidget *parent, bool dense = false);
/**
* @brief Delayed initialization
* @brief initialization
*
* Use this method to setup when the arguments are not available in the constructor
*
* @param layout The layout in which the PCell instance resides
* @param view The layout view from which to take layers for example
* @param cv_index The index of the cellview in "view"
* @param pcell_decl The PCell declaration
* @param parameters The parameter values to show (if empty, the default values are used)
*/
void setup (const db::Layout *layout, lay::LayoutView *view, int cv_index, const db::PCellDeclaration *pcell_decl, const db::pcell_parameters_type &parameters);
@ -94,8 +92,12 @@ public:
/**
* @brief Get the current parameters
*
* *ok is set to true, if there is no error. In case of an error it's set to false.
* The error is indicated in the error label in the editor page.
* If ok is null, an exception is thrown.
*/
std::vector<tl::Variant> get_parameters ();
std::vector<tl::Variant> get_parameters (bool *ok = 0);
/**
* @brief Get the PCell declaration pointer
@ -110,10 +112,12 @@ public:
*/
void set_parameters (const std::vector<tl::Variant> &values);
public slots:
void activated ();
void clicked ();
signals:
void edited ();
private slots:
void parameter_changed ();
private:
QScrollArea *mp_parameters_area;
QLabel *mp_error_label;
@ -124,8 +128,11 @@ private:
lay::LayoutView *mp_view;
int m_cv_index;
db::pcell_parameters_type m_parameters;
bool m_dense;
tl::DeferredMethod<PCellParametersPage> dm_parameter_changed;
void init ();
void do_parameter_changed ();
};
}

View File

@ -32,6 +32,7 @@
#include "edtService.h"
#include "edtConfig.h"
#include "edtDialogs.h"
#include "edtPlugin.h"
#include "edtEditorOptionsPages.h"
#include <cmath>
@ -1009,9 +1010,7 @@ PartialShapeFinder::visit_cell (const db::Cell &cell, const db::Box &search_box,
PartialService::PartialService (db::Manager *manager, lay::LayoutView *view, lay::Dispatcher *root)
: QObject (),
lay::ViewService (view->view_object_widget ()),
lay::Editable (view),
lay::Plugin (view),
lay::EditorServiceBase (view),
db::Object (manager),
mp_view (view),
mp_root (root),
@ -1069,7 +1068,7 @@ PartialService::deactivated ()
void
PartialService::activated ()
{
// ...
// .. nothing yet ..
}
void
@ -1294,11 +1293,11 @@ PartialService::snap (const db::DVector &v) const
const int sr_pixels = 8; // TODO: make variable
db::DPoint
lay::PointSnapToObjectResult
PartialService::snap2 (const db::DPoint &p) const
{
double snap_range = widget ()->mouse_event_trans ().inverted ().ctrans (sr_pixels);
return lay::obj_snap (m_snap_to_objects ? view () : 0, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, snap_range).second;
return lay::obj_snap (m_snap_to_objects ? view () : 0, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, snap_range);
}
void
@ -1530,20 +1529,27 @@ PartialService::wheel_event (int /*delta*/, bool /*horizonal*/, const db::DPoint
bool
PartialService::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio)
{
clear_mouse_cursors ();
if (m_dragging) {
set_cursor (lay::Cursor::size_all);
m_alt_ac = ac_from_buttons (buttons);
// drag the vertex or edge/segment
lay::PointSnapToObjectResult snap_details;
// drag the vertex or edge/segment
if (is_single_point_selection ()) {
// for a single selected point, m_start is the original position and we snap the target -
// thus, we can bring the point on grid or to an object's edge or vertex
m_current = snap2 (p);
// for a single selected point, m_start is the original position and we snap the target -
// thus, we can bring the point on grid or to an object's edge or vertex
snap_details = snap2 (p);
m_current = snap_details.snapped_point;
} else {
m_current = m_start + snap (p - m_start);
}
mouse_cursor_from_snap_details (snap_details);
selection_to_view ();
m_alt_ac = lay::AC_Global;

View File

@ -25,10 +25,8 @@
#ifndef HDR_edtPartialService
#define HDR_edtPartialService
#include "layEditable.h"
#include "layEditorServiceBase.h"
#include "layObjectInstPath.h"
#include "layLayoutView.h"
#include "layPlugin.h"
#include "layViewObject.h"
#include "layRubberBox.h"
#include "laySnap.h"
@ -140,9 +138,7 @@ struct EdgeWithIndex
*/
class PartialService
: public QObject,
public lay::ViewService,
public lay::Editable,
public lay::Plugin,
public lay::EditorServiceBase,
public db::Object
{
Q_OBJECT
@ -326,7 +322,7 @@ private:
db::DPoint snap (const db::DPoint &p) const;
db::DVector snap (const db::DVector &p) const;
db::DPoint snap2 (const db::DPoint &p) const;
lay::PointSnapToObjectResult snap2 (const db::DPoint &p) const;
void enter_edge (const EdgeWithIndex &e, size_t &nmarker, partial_objects::const_iterator sel, const std::map <PointWithIndex, db::Point> &new_points, const std::map <EdgeWithIndex, db::Edge> &new_edges, const db::ICplxTrans &gt, const std::vector<db::DCplxTrans> &tv, bool transient);
void enter_vertices (size_t &nmarker, partial_objects::const_iterator sel, const std::map <PointWithIndex, db::Point> &new_points, const std::map <EdgeWithIndex, db::Edge> &new_edges, const db::ICplxTrans &gt, const std::vector<db::DCplxTrans> &tv, bool transient);

View File

@ -22,6 +22,9 @@
#include "layTipDialog.h"
#include "layEditorOptionsPages.h"
#include "layDispatcher.h"
#include "layLayoutView.h"
#include "edtPlugin.h"
#include "edtConfig.h"
#include "edtService.h"
@ -29,13 +32,27 @@
#include "edtMainService.h"
#include "edtPartialService.h"
#include "edtEditorOptionsPages.h"
#include "edtRecentConfigurationPage.h"
#include <QApplication>
#include <QLayout>
namespace edt
{
static
edt::RecentConfigurationPage::ConfigurationDescriptor shape_cfg_descriptors[] =
{
edt::RecentConfigurationPage::ConfigurationDescriptor ("", tl::to_string (tr ("Layer")), edt::RecentConfigurationPage::Layer),
};
static
void get_shape_editor_options_pages (std::vector<lay::EditorOptionsPage *> &ret, lay::LayoutView *view, lay::Dispatcher *dispatcher)
{
ret.push_back (new RecentConfigurationPage (view, dispatcher, "edit-recent-shape-param",
&shape_cfg_descriptors[0], &shape_cfg_descriptors[sizeof (shape_cfg_descriptors) / sizeof (shape_cfg_descriptors[0])]));
}
static
void get_text_options (std::vector < std::pair<std::string, std::string> > &options)
{
options.push_back (std::pair<std::string, std::string> (cfg_edit_text_string, "ABC"));
@ -44,10 +61,21 @@ void get_text_options (std::vector < std::pair<std::string, std::string> > &opti
options.push_back (std::pair<std::string, std::string> (cfg_edit_text_valign, "bottom"));
}
static
void get_text_editor_options_pages (std::vector<edt::EditorOptionsPage *> &ret, lay::Dispatcher *)
edt::RecentConfigurationPage::ConfigurationDescriptor text_cfg_descriptors[] =
{
ret.push_back (new edt::EditorOptionsText ());
edt::RecentConfigurationPage::ConfigurationDescriptor ("", tl::to_string (tr ("Layer")), edt::RecentConfigurationPage::Layer),
edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_text_string, tl::to_string (tr ("Text")), edt::RecentConfigurationPage::Text),
edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_text_size, tl::to_string (tr ("Size")), edt::RecentConfigurationPage::Double),
edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_text_halign, tl::to_string (tr ("Hor. align")), edt::RecentConfigurationPage::Text),
edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_text_valign, tl::to_string (tr ("Vert. align")), edt::RecentConfigurationPage::Text)
};
static
void get_text_editor_options_pages (std::vector<lay::EditorOptionsPage *> &ret, lay::LayoutView *view, lay::Dispatcher *dispatcher)
{
ret.push_back (new RecentConfigurationPage (view, dispatcher, "edit-recent-text-param",
&text_cfg_descriptors[0], &text_cfg_descriptors[sizeof (text_cfg_descriptors) / sizeof (text_cfg_descriptors[0])]));
ret.push_back (new edt::EditorOptionsText (dispatcher));
}
static
@ -59,10 +87,21 @@ void get_path_options (std::vector < std::pair<std::string, std::string> > &opti
options.push_back (std::pair<std::string, std::string> (cfg_edit_path_ext_var_end, "0.0"));
}
static
void get_path_editor_options_pages (std::vector<edt::EditorOptionsPage *> &ret, lay::Dispatcher *)
edt::RecentConfigurationPage::ConfigurationDescriptor path_cfg_descriptors[] =
{
ret.push_back (new EditorOptionsPath ());
edt::RecentConfigurationPage::ConfigurationDescriptor ("", tl::to_string (tr ("Layer")), edt::RecentConfigurationPage::Layer),
edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_path_width, tl::to_string (tr ("Width")), edt::RecentConfigurationPage::Double),
edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_path_ext_type, tl::to_string (tr ("Ends")), edt::RecentConfigurationPage::Int),
edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_path_ext_var_begin, tl::to_string (tr ("Begin ext.")), edt::RecentConfigurationPage::Double),
edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_path_ext_var_end, tl::to_string (tr ("End ext.")), edt::RecentConfigurationPage::Double)
};
static
void get_path_editor_options_pages (std::vector<lay::EditorOptionsPage *> &ret, lay::LayoutView *view, lay::Dispatcher *dispatcher)
{
ret.push_back (new RecentConfigurationPage (view, dispatcher, "edit-recent-path-param",
&path_cfg_descriptors[0], &path_cfg_descriptors[sizeof (path_cfg_descriptors) / sizeof (path_cfg_descriptors[0])]));
ret.push_back (new EditorOptionsPath (dispatcher));
}
static
@ -84,10 +123,30 @@ void get_inst_options (std::vector < std::pair<std::string, std::string> > &opti
options.push_back (std::pair<std::string, std::string> (cfg_edit_show_shapes_of_instances, "true"));
}
static
void get_inst_editor_options_pages (std::vector<edt::EditorOptionsPage *> &ret, lay::Dispatcher *root)
edt::RecentConfigurationPage::ConfigurationDescriptor inst_cfg_descriptors[] =
{
ret.push_back (new EditorOptionsInst (root));
edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_lib_name, tl::to_string (tr ("Library")), edt::RecentConfigurationPage::CellLibraryName),
edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_cell_name, tl::to_string (tr ("Cell")), edt::RecentConfigurationPage::CellDisplayName),
edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_angle, tl::to_string (tr ("Angle")), edt::RecentConfigurationPage::Double),
edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_mirror, tl::to_string (tr ("Mirror")), edt::RecentConfigurationPage::Bool),
edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_scale, tl::to_string (tr ("Scale")), edt::RecentConfigurationPage::Double),
edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_array, tl::to_string (tr ("Array")), edt::RecentConfigurationPage::ArrayFlag),
edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_rows, tl::to_string (tr ("Rows")), edt::RecentConfigurationPage::IntIfArray),
edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_row_x, tl::to_string (tr ("Row step (x)")), edt::RecentConfigurationPage::DoubleIfArray),
edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_row_y, tl::to_string (tr ("Row step (y)")), edt::RecentConfigurationPage::DoubleIfArray),
edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_columns, tl::to_string (tr ("Columns")), edt::RecentConfigurationPage::IntIfArray),
edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_column_x, tl::to_string (tr ("Column step (x)")), edt::RecentConfigurationPage::DoubleIfArray),
edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_column_y, tl::to_string (tr ("Column step (y)")), edt::RecentConfigurationPage::DoubleIfArray),
edt::RecentConfigurationPage::ConfigurationDescriptor (cfg_edit_inst_pcell_parameters, tl::to_string (tr ("PCell parameters")), edt::RecentConfigurationPage::PCellParameters)
};
static
void get_inst_editor_options_pages (std::vector<lay::EditorOptionsPage *> &ret, lay::LayoutView *view, lay::Dispatcher *dispatcher)
{
ret.push_back (new RecentConfigurationPage (view, dispatcher, "edit-recent-inst-param",
&inst_cfg_descriptors[0], &inst_cfg_descriptors[sizeof (inst_cfg_descriptors) / sizeof (inst_cfg_descriptors[0])]));
ret.push_back (new EditorOptionsInstPCellParam (dispatcher));
ret.push_back (new EditorOptionsInst (dispatcher));
}
template <class Svc>
@ -97,7 +156,7 @@ class PluginDeclaration
public:
PluginDeclaration (const std::string &title, const std::string &mouse_mode,
void (*option_get_f) (std::vector < std::pair<std::string, std::string> > &) = 0,
void (*pages_f) (std::vector <edt::EditorOptionsPage *> &, lay::Dispatcher *) = 0)
void (*pages_f) (std::vector <lay::EditorOptionsPage *> &, lay::LayoutView *, lay::Dispatcher *) = 0)
: m_title (title), m_mouse_mode (mouse_mode), mp_option_get_f (option_get_f), mp_pages_f (pages_f)
{
// .. nothing yet ..
@ -120,11 +179,11 @@ public:
// .. nothing yet ..
}
virtual void get_editor_options_pages (std::vector<edt::EditorOptionsPage *> &pages, lay::Dispatcher *root) const
virtual void get_editor_options_pages (std::vector<lay::EditorOptionsPage *> &pages, lay::LayoutView *view, lay::Dispatcher *root) const
{
if (mp_pages_f != 0) {
size_t nstart = pages.size ();
(*mp_pages_f) (pages, root);
(*mp_pages_f) (pages, view, root);
while (nstart < pages.size ()) {
pages [nstart++]->set_plugin_declaration (this);
}
@ -155,16 +214,16 @@ private:
std::string m_mouse_mode;
void (*mp_option_get_f) (std::vector < std::pair<std::string, std::string> > &options);
void (*mp_pages_f) (std::vector <edt::EditorOptionsPage *> &, lay::Dispatcher *);
void (*mp_pages_f) (std::vector <lay::EditorOptionsPage *> &, lay::LayoutView *, lay::Dispatcher *);
};
static tl::RegisteredClass<lay::PluginDeclaration> config_decl1 (
new edt::PluginDeclaration<edt::PolygonService> (tl::to_string (QObject::tr ("Polygons")), "polygon:edit_mode\t" + tl::to_string (QObject::tr ("Polygon")) + "<:polygon.png>" + tl::to_string (QObject::tr ("{Create a polygon}"))),
new edt::PluginDeclaration<edt::PolygonService> (tl::to_string (QObject::tr ("Polygons")), "polygon:edit_mode\t" + tl::to_string (QObject::tr ("Polygon")) + "<:polygon.png>" + tl::to_string (QObject::tr ("{Create a polygon}")), 0, &get_shape_editor_options_pages),
4010,
"edt::Service(Polygons)"
);
static tl::RegisteredClass<lay::PluginDeclaration> config_decl2 (
new edt::PluginDeclaration<edt::BoxService> (tl::to_string (QObject::tr ("Boxes")), "box:edit_mode\t" + tl::to_string (QObject::tr ("Box")) + "\t<:box.png>" + tl::to_string (QObject::tr ("{Create a box}"))),
new edt::PluginDeclaration<edt::BoxService> (tl::to_string (QObject::tr ("Boxes")), "box:edit_mode\t" + tl::to_string (QObject::tr ("Box")) + "\t<:box.png>" + tl::to_string (QObject::tr ("{Create a box}")), 0, &get_shape_editor_options_pages),
4011,
"edt::Service(Boxes)"
);
@ -189,7 +248,7 @@ class MainPluginDeclaration
{
public:
MainPluginDeclaration (const std::string &title)
: mp_root (0), m_title (title), mp_obj_prop_dialog (0)
: mp_root (0), m_title (title)
{
// .. nothing yet ..
}
@ -218,13 +277,12 @@ public:
menu_entries.push_back (lay::menu_item ("edt::descend", "descend", "zoom_menu.end", tl::to_string (QObject::tr ("Descend")) + "(Ctrl+D)"));
menu_entries.push_back (lay::menu_item ("edt::ascend", "ascend", "zoom_menu.end", tl::to_string (QObject::tr ("Ascend")) + "(Ctrl+A)"));
menu_entries.push_back (lay::separator ("edit_options_group:edit_mode", "edit_menu.end"));
menu_entries.push_back (lay::menu_item ("edt::edit_options", "edit_options:edit_mode", "edit_menu.end", tl::to_string (QObject::tr ("Editor Options")) + "(F3)"));
menu_entries.push_back (lay::menu_item ("edt::sel_make_array", "make_array:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Make Array"))));
menu_entries.push_back (lay::separator ("selection_group:edit_mode", "edit_menu.selection_menu.end"));
menu_entries.push_back (lay::menu_item ("edt::sel_change_layer", "change_layer:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Change Layer"))));
menu_entries.push_back (lay::menu_item ("edt::sel_tap", "tap:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Tap")) + "(T)"));
menu_entries.push_back (lay::menu_item ("edt::sel_align", "align:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Align"))));
menu_entries.push_back (lay::menu_item ("edt::sel_distribute", "distribute:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Distribute"))));
menu_entries.push_back (lay::menu_item ("edt::sel_round_corners", "round_corners:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Round Corners"))));
menu_entries.push_back (lay::menu_item ("edt::sel_size", "size:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Size Shapes"))));
menu_entries.push_back (lay::menu_item ("edt::sel_union", "union:edit_mode", "edit_menu.selection_menu.end", tl::to_string (QObject::tr ("Merge Shapes"))));
@ -263,12 +321,19 @@ public:
{
return false;
}
virtual bool implements_mouse_mode (std::string & /*title*/) const
{
return false;
}
virtual void get_editor_options_pages (std::vector<lay::EditorOptionsPage *> &pages, lay::LayoutView * /*view*/, lay::Dispatcher *dispatcher) const
{
// NOTE: we do not set plugin_declaration which makes the page unspecific
EditorOptionsGeneric *generic_opt = new EditorOptionsGeneric (dispatcher);
pages.push_back (generic_opt);
}
virtual void initialize (lay::Dispatcher *root)
{
lay::Dispatcher *mp = lay::Dispatcher::instance ();
@ -278,24 +343,6 @@ public:
mp_root = root;
// create the editor options dialog
m_prop_dialog_pages.push_back (new edt::EditorOptionsGeneric ());
for (tl::Registrar<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); cls != tl::Registrar<lay::PluginDeclaration>::end (); ++cls) {
const PluginDeclarationBase *pd_base = dynamic_cast<const PluginDeclarationBase *> (&*cls);
if (pd_base) {
pd_base->get_editor_options_pages (m_prop_dialog_pages, root);
}
}
mp_obj_prop_dialog = new edt::EditorOptionsPages (m_prop_dialog_pages, root);
for (std::vector<edt::EditorOptionsPage *>::const_iterator op = m_prop_dialog_pages.begin (); op != m_prop_dialog_pages.end (); ++op) {
if ((*op)->plugin_declaration () != 0) {
(*op)->activate (false);
}
}
// add entries to the combine mode dialog
mp->menu ()->insert_item ("@toolbar.combine_mode.end", "combine_mode_add", new lay::ConfigureAction (tl::to_string (QObject::tr ("Add<:/cm_add.png>{Add shapes}")), cfg_edit_combine_mode, CMConverter ().to_string (CM_Add)));
mp->menu ()->insert_item ("@toolbar.combine_mode.end", "combine_mode_merge", new lay::ConfigureAction (tl::to_string (QObject::tr ("Merge<:/cm_merge.png>{Merge shapes with background}")), cfg_edit_combine_mode, CMConverter ().to_string (CM_Merge)));
@ -333,42 +380,6 @@ public:
}
}
virtual void uninitialize (lay::Dispatcher *)
{
if (mp_obj_prop_dialog) {
delete mp_obj_prop_dialog;
mp_obj_prop_dialog = 0;
}
}
virtual void config_finalize ()
{
if (mp_obj_prop_dialog && mp_obj_prop_dialog->isVisible ()) {
mp_obj_prop_dialog->setup ();
}
}
void show_dialog () const
{
if (mp_obj_prop_dialog) {
if (! mp_obj_prop_dialog->isVisible ()) {
mp_obj_prop_dialog->setup ();
mp_obj_prop_dialog->show ();
}
mp_obj_prop_dialog->activateWindow ();
mp_obj_prop_dialog->raise ();
}
}
void activate (const lay::PluginDeclaration *pd, bool active) const
{
for (std::vector<edt::EditorOptionsPage *>::const_iterator op = m_prop_dialog_pages.begin (); op != m_prop_dialog_pages.end (); ++op) {
if ((*op)->plugin_declaration () == pd) {
(*op)->activate (active);
}
}
}
void initialized (lay::Dispatcher *root)
{
lay::Dispatcher *mp = lay::Dispatcher::instance ();
@ -397,40 +408,27 @@ public:
private:
lay::Dispatcher *mp_root;
std::string m_title;
edt::EditorOptionsPages *mp_obj_prop_dialog;
std::vector<edt::EditorOptionsPage *> m_prop_dialog_pages;
};
static tl::RegisteredClass<lay::PluginDeclaration> config_decl_main (new edt::MainPluginDeclaration (tl::to_string (QObject::tr ("Instances and shapes"))), 4000, "edt::MainService");
void
show_editor_options_dialog ()
commit_recent (lay::LayoutView *view)
{
// look for the plugin declaration and show the dialog
for (tl::Registrar<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); cls != tl::Registrar<lay::PluginDeclaration>::end (); ++cls) {
const MainPluginDeclaration *main_pd = dynamic_cast<const MainPluginDeclaration *> (&*cls);
if (main_pd) {
main_pd->show_dialog ();
break;
lay::EditorOptionsPages *eo_pages = view->editor_options_pages ();;
if (!eo_pages) {
return;
}
for (std::vector<lay::EditorOptionsPage *>::const_iterator op = eo_pages->pages ().begin (); op != eo_pages->pages ().end (); ++op) {
if ((*op)->active ()) {
(*op)->commit_recent (view);
}
}
}
void
activate_service (const lay::PluginDeclaration *pd, bool active)
{
// look for the plugin declaration and show the dialog
for (tl::Registrar<lay::PluginDeclaration>::iterator cls = tl::Registrar<lay::PluginDeclaration>::begin (); cls != tl::Registrar<lay::PluginDeclaration>::end (); ++cls) {
const MainPluginDeclaration *main_pd = dynamic_cast<const MainPluginDeclaration *> (&*cls);
if (main_pd) {
main_pd->activate (pd, active);
break;
}
}
}
static tl::RegisteredClass<lay::PluginDeclaration> config_decl20 (new edt::MainPluginDeclaration (tl::to_string (QObject::tr ("Instances and shapes"))), 4000, "edt::MainService");
class PartialPluginDeclaration
: public lay::PluginDeclaration
: public PluginDeclarationBase
{
public:
PartialPluginDeclaration (const std::string &title, const std::string &mouse_mode)
@ -444,6 +442,11 @@ public:
// .. nothing yet ..
}
virtual void get_editor_options_pages (std::vector<lay::EditorOptionsPage *> & /*pages*/, lay::LayoutView * /*view*/, lay::Dispatcher * /*root*/) const
{
// .. no specific ones ..
}
virtual lay::Plugin *create_plugin (db::Manager *manager, lay::Dispatcher *root, lay::LayoutView *view) const
{
return new edt::PartialService (manager, view, root);

View File

@ -31,35 +31,24 @@
namespace lay
{
class Dispatcher;
class EditorOptionsPage;
}
namespace edt
{
class EditorOptionsPage;
/**
* @brief A helper class for plugin declarations for editor services
*/
class PluginDeclarationBase
: public lay::PluginDeclaration
{
public:
virtual void get_editor_options_pages (std::vector<edt::EditorOptionsPage *> &, lay::Dispatcher *) const = 0;
// .. nothing yet ..
};
/**
* @brief Show the editor options dialog
*
* This dialog is a global resource which is managed by the main plugin declaration
* @brief Commits the current configuration for the recently used configuration list
*/
void show_editor_options_dialog ();
/**
* @brief Activate or deactivate a certain service
*
* This will show or hide the editor properties pages for the respective service.
*/
void activate_service (const lay::PluginDeclaration *pd, bool active);
void commit_recent (lay::LayoutView *view);
}
#endif

View File

@ -26,6 +26,8 @@
#include "dbShapes.h"
#include "dbLayout.h"
#include <QLineEdit>
namespace edt
{

View File

@ -33,6 +33,8 @@
#include <vector>
class QLineEdit;
namespace edt
{

View File

@ -24,9 +24,11 @@
#include "edtPropertiesPages.h"
#include "edtPropertiesPageUtils.h"
#include "edtDialogs.h"
#include "edtPropertiesPageUtils.h"
#include "layDialogs.h"
#include "layObjectInstPath.h"
#include "layLayoutView.h"
#include "layQtTools.h"
#include "tlExceptions.h"
#include "tlString.h"
@ -174,7 +176,7 @@ ShapePropertiesPage::recompute_selection_ptrs (const std::vector<lay::ObjectInst
}
void
ShapePropertiesPage::do_apply (bool current_only)
ShapePropertiesPage::do_apply (bool current_only, bool relative)
{
std::auto_ptr<ChangeApplicator> applicator;
@ -203,32 +205,7 @@ ShapePropertiesPage::do_apply (bool current_only)
// Ask whether to use relative or absolute mode
bool relative_mode = false;
if (! current_only && applicator->supports_relative_mode ()) {
static bool s_relative_mode = true;
QMessageBox mb (QMessageBox::Question,
tr ("Apply Changes To All"),
tr ("For this operation absolute or relative mode is available which affects the way parameters of the selected objects are changed:\n\n"
"In absolute mode, they will be set to the given value. In relative mode, they will be adjusted by the same amount.\n"),
QMessageBox::NoButton, this);
mb.addButton (tr ("Cancel"), QMessageBox::RejectRole);
QPushButton *absolute = mb.addButton (tr ("Absolute"), QMessageBox::NoRole);
QPushButton *relative = mb.addButton (tr ("Relative"), QMessageBox::YesRole);
mb.setDefaultButton (s_relative_mode ? relative : absolute);
mb.exec ();
if (mb.clickedButton () == absolute) {
s_relative_mode = relative_mode = false;
} else if (mb.clickedButton () == relative) {
s_relative_mode = relative_mode = true;
} else {
// Cancel pressed
return;
}
relative_mode = relative;
}
// Note: using the apply-all scheme for applying a single change may look like overhead.
@ -333,7 +310,7 @@ ShapePropertiesPage::do_apply (bool current_only)
void
ShapePropertiesPage::apply ()
{
do_apply (true);
do_apply (true, false);
}
bool
@ -343,9 +320,9 @@ ShapePropertiesPage::can_apply_to_all () const
}
void
ShapePropertiesPage::apply_to_all ()
ShapePropertiesPage::apply_to_all (bool relative)
{
do_apply (false);
do_apply (false, relative);
}
void
@ -391,7 +368,9 @@ void
ShapePropertiesPage::show_props ()
{
lay::UserPropertiesForm props_form (this);
props_form.show (mp_service->view (), m_selection_ptrs [m_index]->cv_index (), m_prop_id);
if (props_form.show (mp_service->view (), m_selection_ptrs [m_index]->cv_index (), m_prop_id)) {
emit edited ();
}
}
bool
@ -404,13 +383,19 @@ ShapePropertiesPage::readonly ()
// PolygonPropertiesPage implementation
PolygonPropertiesPage::PolygonPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent)
: ShapePropertiesPage (service, manager, parent)
: ShapePropertiesPage (service, manager, parent), m_in_text_changed (false)
{
setupUi (this);
setup ();
connect (inst_pb, SIGNAL (clicked ()), this, SLOT (show_inst ()));
connect (prop_pb, SIGNAL (clicked ()), this, SLOT (show_props ()));
if (! readonly ()) {
connect (pointListEdit, SIGNAL (textChanged ()), this, SLOT (text_changed ()));
} else {
pointListEdit->setReadOnly (true);
}
}
void
@ -448,61 +433,87 @@ PolygonPropertiesPage::do_update (const db::Shape &shape, double dbu, const std:
}
pointListEdit->setText (tl::to_qstring (ptlist));
if (! m_in_text_changed) {
pointListEdit->blockSignals (true);
pointListEdit->setText (tl::to_qstring (ptlist));
pointListEdit->blockSignals (false);
}
pointCountLabel->setText (tl::to_qstring (tl::sprintf (tl::to_string (QObject::tr ("(%lu points)")), poly.vertices ())));
}
void
PolygonPropertiesPage::text_changed ()
{
m_in_text_changed = true;
try {
emit edited ();
} catch (tl::Exception &) {
// ignore exceptions
}
m_in_text_changed = false;
}
ChangeApplicator *
PolygonPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape &shape, double dbu)
{
std::string text (tl::to_string (pointListEdit->toPlainText ()));
tl::Extractor ex (text.c_str ());
db::VCplxTrans t = db::CplxTrans (trans ()).inverted ();
bool du = dbu_units ();
db::Polygon poly;
if (*ex.skip () == '(') {
try {
db::DPolygon dp;
ex.read (dp);
std::string text (tl::to_string (pointListEdit->toPlainText ()));
tl::Extractor ex (text.c_str ());
poly = db::Polygon (dp.transformed (db::DCplxTrans (t) * db::DCplxTrans (du ? 1.0 : 1.0 / dbu)));
db::VCplxTrans t = db::CplxTrans (trans ()).inverted ();
bool du = dbu_units ();
} else {
if (*ex.skip () == '(') {
unsigned int h = 0;
while (! ex.at_end ()) {
db::DPolygon dp;
ex.read (dp);
std::vector <db::Point> points;
poly = db::Polygon (dp.transformed (db::DCplxTrans (t) * db::DCplxTrans (du ? 1.0 : 1.0 / dbu)));
while (! ex.at_end () && ! ex.test ("/")) {
} else {
double dx = 0.0, dy = 0.0;
ex.read (dx);
ex.test (",");
ex.read (dy);
ex.test (";");
unsigned int h = 0;
while (! ex.at_end ()) {
points.push_back (point_from_dpoint (db::DPoint (dx, dy), dbu, du, t));
std::vector <db::Point> points;
while (! ex.at_end () && ! ex.test ("/")) {
double dx = 0.0, dy = 0.0;
ex.read (dx);
ex.test (",");
ex.read (dy);
ex.test (";");
points.push_back (point_from_dpoint (db::DPoint (dx, dy), dbu, du, t));
}
if (points.size () < 3) {
throw tl::Exception (tl::to_string (QObject::tr ("Polygon must have at least three points")));
}
if (h == 0) {
poly.assign_hull (points.begin (), points.end (), false /*not compressed*/);
} else {
poly.insert_hole (points.begin (), points.end (), false /*not compressed*/);
}
++h;
}
if (points.size () < 3) {
throw tl::Exception (tl::to_string (QObject::tr ("Polygon must have at least three points")));
}
if (h == 0) {
poly.assign_hull (points.begin (), points.end (), false /*not compressed*/);
} else {
poly.insert_hole (points.begin (), points.end (), false /*not compressed*/);
}
++h;
}
lay::indicate_error (pointListEdit, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (pointListEdit, &ex);
throw;
}
db::Polygon org_poly;
@ -525,15 +536,32 @@ BoxPropertiesPage::BoxPropertiesPage (edt::Service *service, db::Manager *manage
setup ();
mode_tab->setCurrentIndex (s_coordinateMode ? 0 : 1);
connect (mode_tab, SIGNAL (currentChanged (int)), this, SLOT (changed ()));
connect (x1_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ()));
connect (y1_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ()));
connect (x2_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ()));
connect (y2_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ()));
connect (w_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ()));
connect (h_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ()));
connect (cx_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ()));
connect (cy_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ()));
if (! readonly ()) {
connect (mode_tab, SIGNAL (currentChanged (int)), this, SLOT (changed ()));
connect (x1_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ()));
connect (y1_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ()));
connect (x2_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ()));
connect (y2_le_1, SIGNAL (editingFinished ()), this, SLOT (changed ()));
connect (w_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ()));
connect (h_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ()));
connect (cx_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ()));
connect (cy_le_2, SIGNAL (editingFinished ()), this, SLOT (changed ()));
} else {
x1_le_1->setReadOnly (true);
y1_le_1->setReadOnly (true);
x2_le_1->setReadOnly (true);
y2_le_1->setReadOnly (true);
w_le_2->setReadOnly (true);
h_le_2->setReadOnly (true);
cx_le_2->setReadOnly (true);
cy_le_2->setReadOnly (true);
}
connect (inst_pb, SIGNAL (clicked ()), this, SLOT (show_inst ()));
connect (prop_pb, SIGNAL (clicked ()), this, SLOT (show_props ()));
}
@ -576,11 +604,44 @@ BoxPropertiesPage::get_box (int mode) const
{
if (mode == 0) {
bool has_error = false;
double x1 = 0.0, y1 = 0.0, x2 = 0.0, y2 = 0.0;
tl::from_string (tl::to_string (x1_le_1->text ()), x1);
tl::from_string (tl::to_string (y1_le_1->text ()), y1);
tl::from_string (tl::to_string (x2_le_1->text ()), x2);
tl::from_string (tl::to_string (y2_le_1->text ()), y2);
try {
tl::from_string (tl::to_string (x1_le_1->text ()), x1);
lay::indicate_error (x1_le_1, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (x1_le_1, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (y1_le_1->text ()), y1);
lay::indicate_error (y1_le_1, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (y1_le_1, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (x2_le_1->text ()), x2);
lay::indicate_error (x2_le_1, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (x2_le_1, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (y2_le_1->text ()), y2);
lay::indicate_error (y2_le_1, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (y2_le_1, &ex);
has_error = true;
}
if (has_error) {
throw tl::Exception (tl::to_string (tr ("Invalid values - see highlighted entry boxes")));
}
if (m_lr_swapped) {
std::swap (x1, x2);
@ -603,11 +664,44 @@ BoxPropertiesPage::get_box (int mode) const
} else {
bool has_error = false;
double cx = 0.0, cy = 0.0, w = 0.0, h = 0.0;
tl::from_string (tl::to_string (cx_le_2->text ()), cx);
tl::from_string (tl::to_string (cy_le_2->text ()), cy);
tl::from_string (tl::to_string (w_le_2->text ()), w);
tl::from_string (tl::to_string (h_le_2->text ()), h);
try {
tl::from_string (tl::to_string (cx_le_2->text ()), cx);
lay::indicate_error (cx_le_2, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (cx_le_2, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (cy_le_2->text ()), cy);
lay::indicate_error (cy_le_2, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (cy_le_2, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (w_le_2->text ()), w);
lay::indicate_error (w_le_2, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (w_le_2, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (h_le_2->text ()), h);
lay::indicate_error (h_le_2, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (h_le_2, &ex);
has_error = true;
}
if (has_error) {
throw tl::Exception (tl::to_string (tr ("Invalid values - see highlighted entry boxes")));
}
db::VCplxTrans t = db::VCplxTrans (trans ().inverted ());
bool du = dbu_units ();
@ -664,6 +758,8 @@ BoxPropertiesPage::changed ()
set_box (get_box (m_tab_index));
} catch (...) {
}
emit edited ();
}
// -------------------------------------------------------------------------
@ -677,6 +773,28 @@ TextPropertiesPage::TextPropertiesPage (edt::Service *service, db::Manager *mana
connect (inst_pb, SIGNAL (clicked ()), this, SLOT (show_inst ()));
connect (prop_pb, SIGNAL (clicked ()), this, SLOT (show_props ()));
if (! readonly ()) {
connect (text_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (x_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (y_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (size_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (orient_cbx, SIGNAL (activated (int)), this, SIGNAL (edited ()));
connect (halign_cbx, SIGNAL (activated (int)), this, SIGNAL (edited ()));
connect (valign_cbx, SIGNAL (activated (int)), this, SIGNAL (edited ()));
} else {
text_le->setReadOnly (true);
x_le->setReadOnly (true);
y_le->setReadOnly (true);
size_le->setReadOnly (true);
orient_cbx->setEnabled (false);
halign_cbx->setEnabled (false);
valign_cbx->setEnabled (false);
}
}
void
@ -707,12 +825,28 @@ TextPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::st
ChangeApplicator *
TextPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape &shape, double dbu)
{
bool has_error = false;
db::VCplxTrans t = db::CplxTrans (trans ()).inverted ();
bool du = dbu_units ();
double x = 0.0, y = 0.0;
tl::from_string (tl::to_string (x_le->text ()), x);
tl::from_string (tl::to_string (y_le->text ()), y);
try {
tl::from_string (tl::to_string (x_le->text ()), x);
lay::indicate_error (x_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (x_le, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (y_le->text ()), y);
lay::indicate_error (y_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (y_le, &ex);
has_error = true;
}
db::Vector tp = db::Vector (point_from_dpoint (db::DPoint (x, y), dbu, du, t));
db::Trans tt (orient_cbx->currentIndex (), tp);
@ -739,7 +873,13 @@ TextPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape
db::Coord size = 0;
if (! size_le->text ().isEmpty ()) {
size = coord_from_string (tl::to_string (size_le->text ()).c_str (), dbu, du, t);
try {
size = coord_from_string (tl::to_string (size_le->text ()).c_str (), dbu, du, t);
lay::indicate_error (size_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (size_le, &ex);
has_error = true;
}
}
if (size != org_text.size ()) {
appl->add (new TextSizeChangeApplicator (size));
@ -749,6 +889,10 @@ TextPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape
appl->add (new TextStringChangeApplicator (str));
}
if (has_error) {
throw tl::Exception (tl::to_string (tr ("Invalid values - see highlighted entry boxes")));
}
return appl.release ();
}
@ -756,16 +900,22 @@ TextPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape
// PathPropertiesPage implementation
PathPropertiesPage::PathPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent)
: ShapePropertiesPage (service, manager, parent)
: ShapePropertiesPage (service, manager, parent), m_in_text_changed (false)
{
setupUi (this);
setup ();
connect (inst_pb, SIGNAL (clicked ()), this, SLOT (show_inst ()));
connect (prop_pb, SIGNAL (clicked ()), this, SLOT (show_props ()));
ptlist_le->setReadOnly (true);
width_le->setReadOnly (true);
start_ext_le->setReadOnly (true);
end_ext_le->setReadOnly (true);
round_cb->setEnabled (false);
}
void
void
PathPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::string &lname)
{
layer_lbl->setText (tl::to_qstring (lname));
@ -788,7 +938,12 @@ PathPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::st
}
ptlist += coords_to_string (t * *pt, dbu, du);
}
ptlist_le->setText (tl::to_qstring (ptlist));
if (! m_in_text_changed) {
ptlist_le->blockSignals (true);
ptlist_le->setText (tl::to_qstring (ptlist));
ptlist_le->blockSignals (false);
}
width_le->setText (tl::to_qstring (coord_to_string (t.ctrans (path.width ()), dbu, du)));
start_ext_le->setText (tl::to_qstring (coord_to_string (t.ctrans (path.extensions ().first), dbu, du)));
@ -797,68 +952,16 @@ PathPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::st
}
ChangeApplicator *
PathPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape &shape, double dbu)
PathPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape & /*shape*/, double /*dbu*/)
{
db::VCplxTrans t = db::CplxTrans (trans ()).inverted ();
bool du = dbu_units ();
std::string text (tl::to_string (ptlist_le->toPlainText ()));
tl::Extractor ex (text.c_str ());
std::vector <db::Point> points;
while (! ex.at_end ()) {
double dx = 0.0, dy = 0.0;
ex.read (dx);
ex.read (dy);
points.push_back (point_from_dpoint (db::DPoint (dx, dy), dbu, du, t));
}
if (points.size () < 1) {
throw tl::Exception (tl::to_string (QObject::tr ("The path must have at least one point")));
}
db::Coord w = coord_from_string (tl::to_string (width_le->text ()).c_str (), dbu, du, t);
db::Coord se = coord_from_string (tl::to_string (start_ext_le->text ()).c_str (), dbu, du, t);
db::Coord ee = coord_from_string (tl::to_string (end_ext_le->text ()).c_str (), dbu, du, t);
bool round = round_cb->isChecked ();
std::auto_ptr<CombinedChangeApplicator> appl;
db::Path org_path;
shape.path (org_path);
std::vector <db::Point> org_points;
for (db::Path::iterator p = org_path.begin (); p != org_path.end (); ++p) {
org_points.push_back (*p);
}
if (org_points != points) {
appl->add (new PathPointsChangeApplicator (points, org_points));
}
if (w != org_path.width ()) {
appl->add (new PathWidthChangeApplicator (w, org_path.width ()));
}
if (se != org_path.extensions ().first) {
appl->add (new PathStartExtensionChangeApplicator (se));
}
if (ee != org_path.extensions ().second) {
appl->add (new PathEndExtensionChangeApplicator (ee));
}
if (round != org_path.round ()) {
appl->add (new PathRoundEndChangeApplicator (round));
}
return appl.release ();
return 0;
}
// -------------------------------------------------------------------------
// EditablePathPropertiesPage implementation
EditablePathPropertiesPage::EditablePathPropertiesPage (edt::Service *service, db::Manager *manager, QWidget *parent)
: ShapePropertiesPage (service, manager, parent)
: ShapePropertiesPage (service, manager, parent), m_in_text_changed (false)
{
setupUi (this);
setup ();
@ -866,6 +969,12 @@ EditablePathPropertiesPage::EditablePathPropertiesPage (edt::Service *service, d
connect (inst_pb, SIGNAL (clicked ()), this, SLOT (show_inst ()));
connect (prop_pb, SIGNAL (clicked ()), this, SLOT (show_props ()));
connect (type_cb, SIGNAL (currentIndexChanged (int)), this, SLOT (type_selected (int)));
connect (ptlist_le, SIGNAL (textChanged ()), this, SLOT (text_changed ()));
connect (width_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (start_ext_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (end_ext_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (type_cb, SIGNAL (activated (int)), this, SIGNAL (edited ()));
}
static int
@ -886,7 +995,19 @@ path_type_choice (const db::Path &path)
}
}
void
void
EditablePathPropertiesPage::text_changed ()
{
m_in_text_changed = true;
try {
emit edited ();
} catch (tl::Exception &) {
// ignore exceptions
}
m_in_text_changed = false;
}
void
EditablePathPropertiesPage::do_update (const db::Shape &shape, double dbu, const std::string &lname)
{
layer_lbl->setText (tl::to_qstring (lname));
@ -909,7 +1030,12 @@ EditablePathPropertiesPage::do_update (const db::Shape &shape, double dbu, const
}
ptlist += coords_to_string (t * *pt, dbu, du);
}
ptlist_le->setText (tl::to_qstring (ptlist));
if (! m_in_text_changed) {
ptlist_le->blockSignals (true);
ptlist_le->setText (tl::to_qstring (ptlist));
ptlist_le->blockSignals (false);
}
db::Coord w = path.width ();
db::Coord se = path.extensions ().first;
@ -921,6 +1047,10 @@ EditablePathPropertiesPage::do_update (const db::Shape &shape, double dbu, const
end_ext_le->setText (tl::to_qstring (coord_to_string (t.ctrans (ee), dbu, du)));
int type_choice = path_type_choice (path);
if (type_cb->currentIndex () == 2) {
// keep "variable" mode, otherwise if's difficult to switch to it
type_choice = 2;
}
type_cb->setCurrentIndex (type_choice);
type_selected (type_choice);
}
@ -928,6 +1058,8 @@ EditablePathPropertiesPage::do_update (const db::Shape &shape, double dbu, const
ChangeApplicator *
EditablePathPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db::Shape &shape, double dbu)
{
bool has_error = false;
db::VCplxTrans t = db::CplxTrans (trans ()).inverted ();
bool du = dbu_units ();
@ -936,22 +1068,38 @@ EditablePathPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db
std::vector <db::Point> points;
while (! ex.at_end ()) {
try {
double dx = 0.0, dy = 0.0;
ex.read (dx);
ex.read (dy);
while (! ex.at_end ()) {
points.push_back (point_from_dpoint (db::DPoint (dx, dy), dbu, du, t));
double dx = 0.0, dy = 0.0;
ex.read (dx);
ex.read (dy);
points.push_back (point_from_dpoint (db::DPoint (dx, dy), dbu, du, t));
}
if (points.size () < 1) {
throw tl::Exception (tl::to_string (QObject::tr ("The path must have at least one point")));
}
lay::indicate_error (ptlist_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (ptlist_le, &ex);
has_error = true;
}
if (points.size () < 1) {
throw tl::Exception (tl::to_string (QObject::tr ("The path must have at least one point")));
db::Coord w = 0;
try {
w = coord_from_string (tl::to_string (width_le->text ()).c_str (), dbu, du, t);
lay::indicate_error (width_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (width_le, &ex);
has_error = true;
}
db::Coord w = coord_from_string (tl::to_string (width_le->text ()).c_str (), dbu, du, t);
db::Coord se = 0, ee = 0;
switch (type_cb->currentIndex ()) {
case 0: // flush
@ -961,8 +1109,20 @@ EditablePathPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db
se = ee = std::numeric_limits <db::Coord>::min (); // force to half width
break;
case 2: // variable
se = coord_from_string (tl::to_string (start_ext_le->text ()).c_str (), dbu, du, t);
ee = coord_from_string (tl::to_string (end_ext_le->text ()).c_str (), dbu, du, t);
try {
se = coord_from_string (tl::to_string (start_ext_le->text ()).c_str (), dbu, du, t);
lay::indicate_error (start_ext_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (start_ext_le, &ex);
has_error = true;
}
try {
ee = coord_from_string (tl::to_string (end_ext_le->text ()).c_str (), dbu, du, t);
lay::indicate_error (end_ext_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (end_ext_le, &ex);
has_error = true;
}
break;
}
@ -989,6 +1149,10 @@ EditablePathPropertiesPage::create_applicator (db::Shapes & /*shapes*/, const db
appl->add (new PathRoundEndChangeApplicator (type_cb->currentIndex () == 3));
}
if (has_error) {
throw tl::Exception (tl::to_string (tr ("Invalid values - see highlighted entry boxes")));
}
return appl.release ();
}

View File

@ -56,13 +56,15 @@ public:
virtual void operator++ ();
virtual void leave ();
protected:
virtual bool readonly ();
private:
virtual void update ();
virtual void apply ();
virtual void apply_to_all ();
virtual void apply_to_all (bool relative);
virtual bool can_apply_to_all () const;
virtual void do_apply (bool current_only);
virtual bool readonly ();
virtual void do_apply (bool current_only, bool relative);
void recompute_selection_ptrs (const std::vector<lay::ObjectInstPath> &new_sel);
protected:
@ -104,6 +106,12 @@ public:
protected:
virtual QCheckBox *dbu_checkbox () const { return dbu_cb; }
virtual QCheckBox *abs_checkbox () const { return abs_cb; }
public slots:
void text_changed ();
private:
bool m_in_text_changed;
};
class BoxPropertiesPage
@ -167,6 +175,9 @@ public:
protected:
virtual QCheckBox *dbu_checkbox () const { return dbu_cb; }
virtual QCheckBox *abs_checkbox () const { return abs_cb; }
private:
bool m_in_text_changed;
};
class EditablePathPropertiesPage
@ -187,6 +198,10 @@ protected:
public slots:
void type_selected (int);
void text_changed ();
private:
bool m_in_text_changed;
};
}

View File

@ -0,0 +1,394 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "edtRecentConfigurationPage.h"
#include "edtUtils.h"
#include "layDispatcher.h"
#include "layLayoutView.h"
#include "layLayerTreeModel.h"
#include "dbLibraryManager.h"
#include "dbLibrary.h"
#include <QVBoxLayout>
#include <QHeaderView>
#include <QLabel>
namespace edt
{
static const size_t max_entries = 100;
void
RecentConfigurationPage::init ()
{
QVBoxLayout *ly = new QVBoxLayout (this);
ly->setMargin (0);
QLabel *label = new QLabel (this);
label->setText (tr ("Click to select a recent configuration"));
ly->addWidget (label);
mp_tree_widget = new QTreeWidget (this);
mp_tree_widget->setRootIsDecorated (false);
mp_tree_widget->setUniformRowHeights (true);
mp_tree_widget->setSelectionMode (QAbstractItemView::NoSelection);
mp_tree_widget->setAllColumnsShowFocus (true);
ly->addWidget (mp_tree_widget);
connect (mp_tree_widget, SIGNAL (itemClicked (QTreeWidgetItem *, int)), this, SLOT (item_clicked (QTreeWidgetItem *)));
mp_view->layer_list_changed_event.add (this, &RecentConfigurationPage::layers_changed);
mp_tree_widget->setColumnCount (int (m_cfg.size ()));
QStringList column_labels;
for (std::list<ConfigurationDescriptor>::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c) {
column_labels << tl::to_qstring (c->title);
}
mp_tree_widget->setHeaderLabels (column_labels);
update_list (get_stored_values ());
}
RecentConfigurationPage::~RecentConfigurationPage ()
{
// .. nothing yet ..
}
std::string RecentConfigurationPage::title () const
{
return tl::to_string (tr ("Recent"));
}
int RecentConfigurationPage::order () const
{
return 100;
}
std::list<std::vector<std::string> >
RecentConfigurationPage::get_stored_values () const
{
std::string serialized_list = dispatcher ()->config_get (m_recent_cfg_name);
std::list<std::vector<std::string> > values;
tl::Extractor ex (serialized_list.c_str ());
while (! ex.at_end ()) {
values.push_back (std::vector<std::string> ());
while (! ex.at_end () && ! ex.test (";")) {
values.back ().push_back (std::string ());
ex.read_word_or_quoted (values.back ().back ());
ex.test (",");
}
}
return values;
}
void
RecentConfigurationPage::set_stored_values (const std::list<std::vector<std::string> > &values) const
{
std::string serialized_list;
for (std::list<std::vector<std::string> >::const_iterator v = values.begin (); v != values.end (); ++v) {
if (v != values.begin ()) {
serialized_list += ";";
}
for (std::vector<std::string>::const_iterator s = v->begin (); s != v->end (); ++s) {
serialized_list += tl::to_word_or_quoted_string (*s);
serialized_list += ",";
}
}
dispatcher ()->config_set (m_recent_cfg_name, serialized_list);
}
static lay::LayerPropertiesConstIterator
lp_iter_from_string (lay::LayoutView *view, const std::string &s)
{
// parse the layer spec (<layer-props>[@<cv-index>])
db::LayerProperties lp;
tl::Extractor ex (s.c_str ());
lp.read (ex);
int cv_index = 0;
if (ex.test ("@")) {
ex.read (cv_index);
}
// rename the ones that got shifted.
lay::LayerPropertiesConstIterator l = view->begin_layers ();
while (! l.at_end ()) {
if (l->source (true).cv_index () == int (cv_index) && l->source (true).layer_props ().log_equal (lp)) {
return l;
}
++l;
}
return l;
}
void
RecentConfigurationPage::render_to (QTreeWidgetItem *item, int column, const std::vector<std::string> &values, RecentConfigurationPage::ConfigurationRendering rendering)
{
// store original value
item->setData (column, Qt::UserRole, tl::to_qstring (values [column]));
switch (rendering) {
case RecentConfigurationPage::ArrayFlag:
case RecentConfigurationPage::Bool:
{
bool f = false;
tl::from_string (values [column], f);
static QString checkmark = QString::fromUtf8 ("\xe2\x9c\x93");
item->setText (column, f ? checkmark : QString ()); // "checkmark"
}
break;
case RecentConfigurationPage::Layer:
{
int icon_size = mp_view->style ()->pixelMetric (QStyle::PM_ButtonIconSize);
lay::LayerPropertiesConstIterator l = lp_iter_from_string (mp_view, values [column]);
if (! l.is_null () && ! l.at_end ()) {
item->setIcon (column, lay::LayerTreeModel::icon_for_layer (l, mp_view, icon_size, icon_size, 0, true));
item->setText (column, tl::to_qstring (values [column]));
} else {
item->setIcon (column, QIcon ());
item->setText (column, tl::to_qstring ("(" + values [column] + ")"));
}
}
break;
case RecentConfigurationPage::Int:
case RecentConfigurationPage::Double:
case RecentConfigurationPage::Text:
item->setText (column, tl::to_qstring (values [column]));
break;
case RecentConfigurationPage::CellLibraryName:
if (values [column].empty ()) {
item->setText (column, tr ("(local)"));
} else {
item->setText (column, tl::to_qstring (values [column]));
}
break;
case RecentConfigurationPage::IntIfArray:
case RecentConfigurationPage::DoubleIfArray:
{
bool is_array = false;
int flag_column = 0;
for (std::list<ConfigurationDescriptor>::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c, ++flag_column) {
if (c->rendering == RecentConfigurationPage::ArrayFlag) {
tl::from_string (values [flag_column], is_array);
break;
}
}
if (is_array) {
item->setText (column, tl::to_qstring (values [column]));
} else {
item->setText (column, QString ());
}
}
break;
case RecentConfigurationPage::CellDisplayName:
{
// search for a libname
int libname_column = 0;
const db::Library *lib = 0;
for (std::list<ConfigurationDescriptor>::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c, ++libname_column) {
if (c->rendering == RecentConfigurationPage::CellLibraryName) {
lib = db::LibraryManager::instance ().lib_ptr_by_name (values [libname_column]);
break;
}
}
if (lib) {
// search for a PCell parameters
int pcp_column = 0;
std::map<std::string, tl::Variant> pcp;
for (std::list<ConfigurationDescriptor>::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c, ++pcp_column) {
if (c->rendering == RecentConfigurationPage::PCellParameters) {
pcp = pcell_parameters_from_string (values [pcp_column]);
break;
}
}
std::pair<bool, db::Layout::pcell_id_type> pcid = lib->layout ().pcell_by_name (values [column].c_str ());
if (pcid.first) {
const db::PCellDeclaration *pc_decl = lib->layout ().pcell_declaration (pcid.second);
if (pc_decl) {
item->setText (column, tl::to_qstring (pc_decl->get_display_name (pc_decl->map_parameters (pcp))));
break;
}
}
}
item->setText (column, tl::to_qstring (values [column]));
}
break;
case RecentConfigurationPage::PCellParameters:
{
std::map<std::string, tl::Variant> pcp;
pcp = pcell_parameters_from_string (values [column]);
std::string r;
for (std::map<std::string, tl::Variant>::const_iterator p = pcp.begin (); p != pcp.end (); ++p) {
if (p != pcp.begin ()) {
r += ",";
}
r += p->first;
r += "=";
r += p->second.to_string ();
}
item->setText (column, tl::to_qstring (r));
}
break;
}
}
void
RecentConfigurationPage::layers_changed (int)
{
update_list (get_stored_values ());
}
void
RecentConfigurationPage::update_list (const std::list<std::vector<std::string> > &stored_values)
{
int row = 0;
for (std::list<std::vector<std::string> >::const_iterator v = stored_values.begin (); v != stored_values.end (); ++v, ++row) {
QTreeWidgetItem *item = 0;
if (row < mp_tree_widget->topLevelItemCount ()) {
item = mp_tree_widget->topLevelItem (row);
} else {
item = new QTreeWidgetItem (mp_tree_widget);
mp_tree_widget->addTopLevelItem (item);
}
int column = 0;
for (std::list<ConfigurationDescriptor>::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c, ++column) {
if (column < int (v->size ())) {
render_to (item, column, *v, c->rendering);
}
}
}
while (mp_tree_widget->topLevelItemCount () > row) {
delete mp_tree_widget->takeTopLevelItem (row);
}
mp_tree_widget->header ()->resizeSections (QHeaderView::ResizeToContents);
}
void
RecentConfigurationPage::item_clicked (QTreeWidgetItem *item)
{
int column = 0;
for (std::list<ConfigurationDescriptor>::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c, ++column) {
std::string v = tl::to_string (item->data (column, Qt::UserRole).toString ());
if (c->rendering == Layer) {
// "getting" a layer means making it current
db::LayerProperties lp;
tl::Extractor ex (v.c_str ());
lp.read (ex);
int cv_index = 0;
if (ex.test ("@")) {
ex.read (cv_index);
}
mp_view->set_or_request_current_layer (cv_index, lp);
} else {
dispatcher ()->config_set (c->cfg_name, v);
}
}
dispatcher ()->config_end ();
}
void
RecentConfigurationPage::commit_recent (lay::Dispatcher *root)
{
std::vector<std::string> values;
values.reserve (m_cfg.size ());
for (std::list<ConfigurationDescriptor>::const_iterator c = m_cfg.begin (); c != m_cfg.end (); ++c) {
if (c->rendering == Layer) {
std::string s;
if (!(mp_view->current_layer ().is_null () || mp_view->current_layer ().at_end ()) && mp_view->current_layer ()->is_visual ()) {
int cv_index = mp_view->current_layer ()->cellview_index ();
const lay::CellView &cv = mp_view->cellview (cv_index);
int li = mp_view->current_layer ()->layer_index ();
if (cv.is_valid () && cv->layout ().is_valid_layer (li)) {
s = cv->layout ().get_properties (li).to_string ();
if (cv_index > 0) {
s += "@" + tl::to_string (cv_index);
}
}
}
values.push_back (s);
} else {
values.push_back (root->config_get (c->cfg_name));
}
}
std::list<std::vector<std::string> > stored_values = get_stored_values ();
for (std::list<std::vector<std::string> >::iterator v = stored_values.begin (); v != stored_values.end (); ++v) {
if (*v == values) {
stored_values.erase (v);
break;
}
}
stored_values.push_front (values);
while (stored_values.size () > max_entries) {
stored_values.erase (--stored_values.end ());
}
set_stored_values (stored_values);
update_list (stored_values);
}
}

View File

@ -0,0 +1,115 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef HDR_edtRecentConfigurationPage
#define HDR_edtRecentConfigurationPage
#include "layEditorOptionsPage.h"
#include "tlObject.h"
#include <list>
#include <QTreeWidget>
namespace lay
{
class LayoutView;
}
namespace edt
{
class PCellParametersPage;
class EditorOptionsPages;
/**
* @brief The base class for a object properties page
*/
class RecentConfigurationPage
: public lay::EditorOptionsPage,
public tl::Object
{
Q_OBJECT
public:
enum ConfigurationRendering
{
Text = 0,
Bool = 1,
Double = 2,
Int = 3,
Layer = 4,
PCellParameters = 5,
CellLibraryName = 6,
CellDisplayName = 7,
ArrayFlag = 8,
DoubleIfArray = 9,
IntIfArray = 10
};
struct ConfigurationDescriptor
{
ConfigurationDescriptor (const std::string &_cfg_name, const std::string &_title, ConfigurationRendering _rendering)
: cfg_name (_cfg_name), title (_title), rendering (_rendering)
{ }
std::string cfg_name, title;
ConfigurationRendering rendering;
};
template <class Iter>
RecentConfigurationPage (lay::LayoutView *view, lay::Dispatcher *dispatcher, const std::string &recent_cfg_name, Iter begin_cfg, Iter end_cfg)
: EditorOptionsPage (dispatcher), mp_view (view), m_recent_cfg_name (recent_cfg_name), m_cfg (begin_cfg, end_cfg)
{
init ();
}
virtual ~RecentConfigurationPage ();
virtual std::string title () const;
virtual int order () const;
virtual void apply (lay::Dispatcher * /*root*/) { }
virtual void setup (lay::Dispatcher * /*root*/) { }
virtual void commit_recent (lay::Dispatcher *root);
private slots:
void item_clicked (QTreeWidgetItem *item);
private:
lay::LayoutView *mp_view;
std::string m_recent_cfg_name;
std::list<ConfigurationDescriptor> m_cfg;
QTreeWidget *mp_tree_widget;
void init ();
void update_list (const std::list<std::vector<std::string> > &stored_values);
std::list<std::vector<std::string> > get_stored_values () const;
void set_stored_values (const std::list<std::vector<std::string> > &values) const;
void render_to (QTreeWidgetItem *item, int column, const std::vector<std::string> &values, RecentConfigurationPage::ConfigurationRendering rendering);
void layers_changed (int);
};
}
#endif

View File

@ -60,49 +60,8 @@ ac_from_buttons (unsigned int buttons)
// -------------------------------------------------------------
std::string pcell_parameters_to_string (const std::map<std::string, tl::Variant> &parameters)
{
std::string param;
param = "!"; // flags PCells
for (std::map<std::string, tl::Variant>::const_iterator p = parameters.begin (); p != parameters.end (); ++p) {
param += tl::to_word_or_quoted_string (p->first);
param += ":";
param += p->second.to_parsable_string ();
param += ";";
}
return param;
}
std::map<std::string, tl::Variant> pcell_parameters_from_string (const std::string &s)
{
tl::Extractor ex (s.c_str ());
std::map<std::string, tl::Variant> pm;
ex.test ("!");
try {
while (! ex.at_end ()) {
std::string n;
ex.read_word_or_quoted (n);
ex.test (":");
ex.read (pm.insert (std::make_pair (n, tl::Variant ())).first->second);
ex.test (";");
}
} catch (...) {
// ignore errors
}
return pm;
}
// -------------------------------------------------------------
Service::Service (db::Manager *manager, lay::LayoutView *view, db::ShapeIterator::flags_type flags)
: lay::ViewService (view->view_object_widget ()),
lay::Editable (view),
lay::Plugin (view),
: lay::EditorServiceBase (view),
db::Object (manager),
mp_view (view),
mp_transient_marker (0),
@ -122,9 +81,7 @@ Service::Service (db::Manager *manager, lay::LayoutView *view, db::ShapeIterator
}
Service::Service (db::Manager *manager, lay::LayoutView *view)
: lay::ViewService (view->view_object_widget ()),
lay::Editable (view),
lay::Plugin (view),
: lay::EditorServiceBase (view),
db::Object (manager),
mp_view (view),
mp_transient_marker (0),
@ -217,18 +174,30 @@ Service::snap (const db::DPoint &p, const db::DPoint &plast, bool connect) const
const int sr_pixels = 8; // TODO: make variable
db::DPoint
Service::snap2 (const db::DPoint &p) const
lay::PointSnapToObjectResult
Service::snap2_details (const db::DPoint &p) const
{
double snap_range = widget ()->mouse_event_trans ().inverted ().ctrans (sr_pixels);
return lay::obj_snap (m_snap_to_objects ? view () : 0, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, snap_range).second;
return lay::obj_snap (m_snap_to_objects ? view () : 0, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, snap_range);
}
db::DPoint
Service::snap2 (const db::DPoint &p) const
{
return snap2_details (p).snapped_point;
}
db::DPoint
Service::snap2 (const db::DPoint &p, const db::DPoint &plast, bool connect) const
{
double snap_range = widget ()->mouse_event_trans ().inverted ().ctrans (sr_pixels);
return lay::obj_snap (m_snap_to_objects ? view () : 0, plast, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, connect ? connect_ac () : move_ac (), snap_range).second;
return lay::obj_snap (m_snap_to_objects ? view () : 0, plast, p, m_edit_grid == db::DVector () ? m_global_grid : m_edit_grid, connect ? connect_ac () : move_ac (), snap_range).snapped_point;
}
void
Service::service_configuration_changed ()
{
// The base class implementation does nothing
}
bool
@ -238,27 +207,60 @@ Service::configure (const std::string &name, const std::string &value)
edt::ACConverter acc;
if (name == cfg_edit_global_grid) {
egc.from_string (value, m_global_grid);
service_configuration_changed ();
} else if (name == cfg_edit_show_shapes_of_instances) {
tl::from_string (value, m_show_shapes_of_instances);
service_configuration_changed ();
} else if (name == cfg_edit_max_shapes_of_instances) {
tl::from_string (value, m_max_shapes_of_instances);
service_configuration_changed ();
} else if (name == cfg_edit_grid) {
egc.from_string (value, m_edit_grid);
service_configuration_changed ();
return true; // taken
} else if (name == cfg_edit_snap_to_objects) {
tl::from_string (value, m_snap_to_objects);
service_configuration_changed ();
return true; // taken
} else if (name == cfg_edit_move_angle_mode) {
acc.from_string (value, m_move_ac);
service_configuration_changed ();
return true; // taken
} else if (name == cfg_edit_connect_angle_mode) {
acc.from_string (value, m_connect_ac);
service_configuration_changed ();
return true; // taken
} else if (name == cfg_edit_top_level_selection) {
tl::from_string (value, m_top_level_sel);
service_configuration_changed ();
} else if (name == cfg_edit_hier_copy_mode) {
tl::from_string (value, m_hier_copy_mode);
service_configuration_changed ();
} else {
lay::EditorServiceBase::configure (name, value);
}
return false; // not taken
@ -710,14 +712,15 @@ Service::mouse_move_event (const db::DPoint &p, unsigned int buttons, bool prio)
// in this mode, ignore exceptions here since it is rather annoying to have messages popping
// up then.
try {
do_begin_edit (p);
m_editing = true;
begin_edit (p);
} catch (...) {
set_edit_marker (0);
}
}
if (m_editing) {
do_mouse_move (p);
} else {
do_mouse_move_inactive (p);
}
m_alt_ac = lay::AC_Global;
@ -744,8 +747,7 @@ Service::mouse_press_event (const db::DPoint &p, unsigned int buttons, bool prio
view ()->cancel (); // cancel any pending edit operations and clear the selection
set_edit_marker (0);
do_begin_edit (p);
m_editing = true;
begin_edit (p);
} else {
if (do_mouse_click (p)) {
@ -797,22 +799,21 @@ Service::mouse_click_event (const db::DPoint &p, unsigned int buttons, bool prio
void
Service::activated ()
{
// make all editor option pages visible
activate_service (plugin_declaration (), true);
if (view ()->is_editable ()) {
view ()->cancel (); // cancel any pending edit operations and clear the selection
set_edit_marker (0);
m_immediate = do_activated ();
m_editing = false;
}
}
void
Service::deactivated ()
{
// make all editor option pages visible
activate_service (plugin_declaration (), false);
lay::EditorServiceBase::deactivated ();
edit_cancel ();
@ -1432,6 +1433,19 @@ Service::move_markers (const db::DTrans &t)
}
}
void
Service::begin_edit (const db::DPoint &p)
{
do_begin_edit (p);
m_editing = true;
}
void
Service::tap (const db::DPoint & /*initial*/)
{
// .. nothing here ..
}
void
Service::selection_to_view ()
{

View File

@ -27,9 +27,8 @@
#include "edtCommon.h"
#include "layEditable.h"
#include "layEditorServiceBase.h"
#include "layPlugin.h"
#include "layViewObject.h"
#include "layMarker.h"
#include "laySnap.h"
#include "layObjectInstPath.h"
@ -44,12 +43,14 @@
#include <vector>
#include <QColor>
namespace lay {
class LayerPropertiesConstIterator;
}
namespace edt {
class Service;
class PluginDeclarationBase;
class EditorOptionsPages;
class EditorOptionsPage;
// -------------------------------------------------------------
@ -70,9 +71,7 @@ std::map<std::string, tl::Variant> pcell_parameters_from_string (const std::stri
// -------------------------------------------------------------
class EDT_PUBLIC Service
: public lay::ViewService,
public lay::Editable,
public lay::Plugin,
: public lay::EditorServiceBase,
public db::Object
{
public:
@ -216,22 +215,6 @@ public:
return m_color;
}
/**
* @brief Obtain the lay::ViewService interface
*/
lay::ViewService *view_service_interface ()
{
return this;
}
/**
* @brief Obtain the lay::Editable interface
*/
lay::Editable *editable_interface ()
{
return this;
}
/**
* @brief Get the selection container
*/
@ -344,6 +327,11 @@ public:
*/
virtual void edit_cancel ();
/**
* @brief Triggered by tap - gives the new layer and if required the initial point
*/
virtual void tap (const db::DPoint &initial);
/**
* @brief Delete the selected rulers
*
@ -383,6 +371,11 @@ protected:
*/
void selection_to_view ();
/**
* @brief starts editing at the given point.
*/
void begin_edit (const db::DPoint &p);
/**
* @brief Reimplemented by the specific implementation of the shape editors
*
@ -441,6 +434,11 @@ protected:
*/
virtual void do_cancel_edit () { }
/**
* @brief Called when a configuration parameter provided by the service base class has changed
*/
virtual void service_configuration_changed ();
/**
* @brief Install a marker for representing the edited object
*
@ -528,6 +526,16 @@ protected:
return m_max_shapes_of_instances;
}
bool editing () const
{
return m_editing;
}
/**
* @brief Point snapping with detailed return value
*/
lay::PointSnapToObjectResult snap2_details (const db::DPoint &p) const;
private:
// The layout view that the editor service is attached to
lay::LayoutView *mp_view;

View File

@ -25,8 +25,8 @@
#include "edtServiceImpl.h"
#include "edtPropertiesPages.h"
#include "edtInstPropertiesPage.h"
#include "edtPCellParametersDialog.h"
#include "edtService.h"
#include "edtPlugin.h"
#include "dbEdge.h"
#include "dbLibrary.h"
#include "dbLibraryManager.h"
@ -51,7 +51,7 @@ ShapeEditService::ShapeEditService (db::Manager *manager, lay::LayoutView *view,
: edt::Service (manager, view, shape_types),
m_layer (0), m_cv_index (0), mp_cell (0), mp_layout (0), m_combine_mode (CM_Add)
{
// .. nothing yet ..
view->current_layer_changed_event.add (this, &ShapeEditService::update_edit_layer);
}
bool
@ -69,17 +69,11 @@ void
ShapeEditService::get_edit_layer ()
{
lay::LayerPropertiesConstIterator cl = view ()->current_layer ();
if (cl.is_null ()) {
throw tl::Exception (tl::to_string (QObject::tr ("Please select a layer first")));
}
if (! cl->visible (true)) {
lay::TipDialog td (QApplication::activeWindow (),
tl::to_string (QObject::tr ("You are about to draw on a hidden layer. The result won't be visible.")),
"drawing-on-invisible-layer");
td.exec_dialog ();
}
int cv_index = cl->cellview_index ();
const lay::CellView &cv = view ()->cellview (cv_index);
int layer = cl->layer_index ();
@ -88,6 +82,13 @@ ShapeEditService::get_edit_layer ()
throw tl::Exception (tl::to_string (QObject::tr ("Please select a cell first")));
}
if (! cl->visible (true)) {
lay::TipDialog td (QApplication::activeWindow (),
tl::to_string (QObject::tr ("You are about to draw on a hidden layer. The result won't be visible.")),
"drawing-on-invisible-layer");
td.exec_dialog ();
}
if (layer < 0 || ! cv->layout ().is_valid_layer ((unsigned int) layer)) {
if (cl->has_children ()) {
@ -128,6 +129,79 @@ ShapeEditService::get_edit_layer ()
}
}
void
ShapeEditService::update_edit_layer (const lay::LayerPropertiesConstIterator &cl)
{
if (! editing ()) {
return;
}
if (cl.is_null () || cl->has_children ()) {
return;
}
int cv_index = cl->cellview_index ();
const lay::CellView &cv = view ()->cellview (cv_index);
int layer = cl->layer_index ();
if (cv_index < 0 || ! cv.is_valid ()) {
return;
}
if (cv->layout ().cell (cv.cell_index ()).is_proxy ()) {
return;
}
if (! cl->visible (true)) {
lay::TipDialog td (QApplication::activeWindow (),
tl::to_string (QObject::tr ("You are now drawing on a hidden layer. The result won't be visible.")),
"drawing-on-invisible-layer");
td.exec_dialog ();
}
if (layer < 0 || ! cv->layout ().is_valid_layer ((unsigned int) layer)) {
// create this layer now
const lay::ParsedLayerSource &source = cl->source (true /*real*/);
db::LayerProperties db_lp;
if (source.has_name ()) {
db_lp.name = source.name ();
}
db_lp.layer = source.layer ();
db_lp.datatype = source.datatype ();
cv->layout ().insert_layer (db_lp);
// update the layer index inside the layer view
cl->realize_source ();
// Hint: we could have taken the new index from insert_layer, but this
// is a nice test:
layer = cl->layer_index ();
tl_assert (layer >= 0);
}
m_layer = (unsigned int) layer;
m_cv_index = (unsigned int) cv_index;
m_trans = (cl->trans ().front () * db::CplxTrans (cv->layout ().dbu ()) * cv.context_trans ()).inverted ();
mp_layout = &(cv->layout ());
mp_cell = &(mp_layout->cell (cv.cell_index ()));
current_layer_changed ();
}
void
ShapeEditService::tap (const db::DPoint &initial)
{
if (editing ()) {
get_edit_layer ();
} else {
begin_edit (initial);
}
}
/**
* @brief Deliver a good interpolation between two points m and p
*
@ -339,9 +413,18 @@ PolygonService::set_last_point (const db::DPoint &p)
}
}
void
void
PolygonService::do_mouse_move_inactive (const db::DPoint &p)
{
lay::PointSnapToObjectResult snap_details = snap2_details (p);
mouse_cursor_from_snap_details (snap_details);
}
void
PolygonService::do_mouse_move (const db::DPoint &p)
{
do_mouse_move_inactive (p);
set_cursor (lay::Cursor::cross);
if (m_points.size () >= 2) {
set_last_point (p);
@ -366,6 +449,7 @@ void
PolygonService::do_finish_edit ()
{
deliver_shape (get_polygon ());
commit_recent (view ());
}
db::Polygon
@ -644,9 +728,18 @@ BoxService::update_marker ()
}
}
void
void
BoxService::do_mouse_move_inactive (const db::DPoint &p)
{
lay::PointSnapToObjectResult snap_details = snap2_details (p);
mouse_cursor_from_snap_details (snap_details);
}
void
BoxService::do_mouse_move (const db::DPoint &p)
{
do_mouse_move_inactive (p);
set_cursor (lay::Cursor::cross);
m_p2 = snap2 (p);
update_marker ();
@ -663,6 +756,7 @@ void
BoxService::do_finish_edit ()
{
deliver_shape (get_box ());
commit_recent (view ());
}
void
@ -738,18 +832,21 @@ TextService::do_activated ()
{
m_rot = 0;
// Show editor options dialog to allow entering of width
std::vector<edt::MainService *> edt_main_services = view ()->get_plugins <edt::MainService> ();
if (edt_main_services.size () > 0) {
edt_main_services [0]->cm_edit_options ();
}
return true; // start editing immediately
}
void
void
TextService::do_mouse_move_inactive (const db::DPoint &p)
{
lay::PointSnapToObjectResult snap_details = snap2_details (p);
mouse_cursor_from_snap_details (snap_details);
}
void
TextService::do_mouse_move (const db::DPoint &p)
{
do_mouse_move_inactive (p);
set_cursor (lay::Cursor::cross);
m_text.trans (db::DTrans (m_rot, snap2 (p) - db::DPoint ()));
update_marker ();
@ -786,6 +883,8 @@ TextService::do_finish_edit ()
cell ().shapes (layer ()).insert (get_text ());
manager ()->commit ();
commit_recent (view ());
if (! view ()->text_visible ()) {
lay::TipDialog td (QApplication::activeWindow (),
@ -904,12 +1003,6 @@ PathService::do_begin_edit (const db::DPoint &p)
bool
PathService::do_activated ()
{
// Show editor options dialog to allow entering of width
std::vector<edt::MainService *> edt_main_services = view ()->get_plugins <edt::MainService> ();
if (edt_main_services.size () > 0) {
edt_main_services [0]->cm_edit_options ();
}
return false; // don't start editing immediately
}
@ -935,9 +1028,18 @@ PathService::set_last_point (const db::DPoint &p)
}
}
void
void
PathService::do_mouse_move_inactive (const db::DPoint &p)
{
lay::PointSnapToObjectResult snap_details = snap2_details (p);
mouse_cursor_from_snap_details (snap_details);
}
void
PathService::do_mouse_move (const db::DPoint &p)
{
do_mouse_move_inactive (p);
set_cursor (lay::Cursor::cross);
if (m_points.size () >= 2) {
set_last_point (p);
@ -966,6 +1068,8 @@ PathService::do_finish_edit ()
m_points.pop_back ();
deliver_shape (get_path ());
commit_recent (view ());
}
void
@ -1094,7 +1198,7 @@ InstService::InstService (db::Manager *manager, lay::LayoutView *view)
m_array (false), m_rows (1), m_columns (1),
m_row_x (0.0), m_row_y (0.0), m_column_x (0.0), m_column_y (0.0),
m_place_origin (false), m_reference_transaction_id (0),
m_needs_update (true), m_has_valid_cell (false), m_in_drag_drop (false),
m_needs_update (true), m_parameters_changed (false), m_has_valid_cell (false), m_in_drag_drop (false),
m_current_cell (0), mp_current_layout (0), mp_pcell_decl (0), m_cv_index (-1)
{
// .. nothing yet ..
@ -1109,12 +1213,6 @@ InstService::properties_page (db::Manager *manager, QWidget *parent)
bool
InstService::do_activated ()
{
// Show editor options dialog to allow entering of parameters
std::vector<edt::MainService *> edt_main_services = view ()->get_plugins <edt::MainService> ();
if (edt_main_services.size () > 0) {
edt_main_services [0]->cm_edit_options ();
}
m_cv_index = view ()->active_cellview_index ();
m_has_valid_cell = false;
@ -1137,63 +1235,69 @@ InstService::get_default_layer_for_pcell ()
bool
InstService::drag_enter_event (const db::DPoint &p, const lay::DragDropDataBase *data)
{
{
const lay::CellDragDropData *cd = dynamic_cast <const lay::CellDragDropData *> (data);
if (view ()->is_editable () && cd && (cd->layout () == & view ()->active_cellview ()->layout () || cd->library ())) {
view ()->cancel ();
set_edit_marker (0);
m_cv_index = view ()->active_cellview_index ();
m_in_drag_drop = true;
bool switch_parameters = true;
// configure from the drag/drop data
if (cd->library ()) {
// Reject drag & drop if the target technology does not match
if (cd->library ()->for_technologies () && view ()->cellview (view ()->active_cellview_index ()).is_valid ()) {
if (! cd->library ()->is_for_technology (view ()->cellview (view ()->active_cellview_index ())->tech_name ())) {
return false;
}
}
if (m_lib_name != cd->library ()->get_name ()) {
m_lib_name = cd->library ()->get_name ();
m_pcell_parameters.clear ();
}
} else {
m_lib_name.clear ();
}
m_is_pcell = false;
if (cd->is_pcell ()) {
const db::PCellDeclaration *pcell_decl = cd->layout ()->pcell_declaration (cd->cell_index ());
if (pcell_decl) {
if (! pcell_decl) {
return false;
}
if (m_cell_or_pcell_name != pcell_decl->name ()) {
m_cell_or_pcell_name = pcell_decl->name ();
m_pcell_parameters.clear ();
}
m_is_pcell = true;
// NOTE: we reuse previous parameters for convenience unless PCell or library has changed
const std::vector<db::PCellParameterDeclaration> &pd = pcell_decl->parameter_declarations();
for (std::vector<db::PCellParameterDeclaration>::const_iterator i = pd.begin (); i != pd.end (); ++i) {
if (i->get_type () == db::PCellParameterDeclaration::t_layer && !i->is_hidden () && !i->is_readonly () && i->get_default ().is_nil ()) {
m_pcell_parameters.insert (std::make_pair (i->get_name (), get_default_layer_for_pcell ()));
} else {
m_pcell_parameters.insert (std::make_pair (i->get_name (), i->get_default ()));
}
}
do_begin_edit (p);
return true;
if (m_cell_or_pcell_name != pcell_decl->name ()) {
m_cell_or_pcell_name = pcell_decl->name ();
}
if (! cd->pcell_params ().empty ()) {
m_pcell_parameters = pcell_decl->named_parameters (cd->pcell_params ());
switch_parameters = false;
}
} else if (cd->layout ()->is_valid_cell_index (cd->cell_index ())) {
m_cell_or_pcell_name = cd->layout ()->cell_name (cd->cell_index ());
do_begin_edit (p);
return true;
} else {
return false;
}
switch_cell_or_pcell (switch_parameters);
sync_to_config ();
m_in_drag_drop = true;
view ()->switch_mode (plugin_declaration ()->id ());
do_begin_edit (p);
// action taken.
return true;
}
return false;
@ -1210,7 +1314,7 @@ InstService::drag_move_event (const db::DPoint &p, const lay::DragDropDataBase *
}
}
void
void
InstService::drag_leave_event ()
{
if (m_in_drag_drop) {
@ -1219,7 +1323,7 @@ InstService::drag_leave_event ()
}
}
bool
bool
InstService::selection_applies (const lay::ObjectInstPath &sel) const
{
return sel.is_cell_inst ();
@ -1228,53 +1332,8 @@ InstService::selection_applies (const lay::ObjectInstPath &sel) const
bool
InstService::drop_event (const db::DPoint & /*p*/, const lay::DragDropDataBase * /*data*/)
{
if (m_in_drag_drop) {
const lay::CellView &cv = view ()->cellview (m_cv_index);
if (! cv.is_valid ()) {
return false;
}
make_cell (cv);
bool accepted = true;
if (m_has_valid_cell && mp_pcell_decl) {
std::vector<tl::Variant> pv = mp_pcell_decl->map_parameters (m_pcell_parameters);
// Turn off the drag cursor for the modal dialog
QApplication::restoreOverrideCursor ();
// for PCells dragged show the parameter dialog for a chance to edit the initial parameters
if (! mp_pcell_parameters_dialog.get ()) {
mp_pcell_parameters_dialog.reset (new edt::PCellParametersDialog (view ()));
mp_pcell_parameters_dialog->parameters_changed_event.add (this, &InstService::apply_edits);
}
if (! mp_pcell_parameters_dialog->exec (mp_current_layout, view (), m_cv_index, mp_pcell_decl, pv)) {
accepted = false;
} else {
m_has_valid_cell = false;
m_pcell_parameters = mp_pcell_decl->named_parameters (mp_pcell_parameters_dialog->get_parameters ());
}
}
set_edit_marker (0);
if (accepted) {
do_finish_edit ();
} else {
do_cancel_edit ();
}
sync_to_config ();
return true;
} else {
return false;
}
m_in_drag_drop = false;
return false;
}
void
@ -1337,10 +1396,6 @@ InstService::do_begin_edit (const db::DPoint &p)
m_trans = db::VCplxTrans (1.0 / cv->layout ().dbu ()) * tv [0] * db::CplxTrans (cv->layout ().dbu ()) * cv.context_trans ();
}
lay::Marker *marker = new lay::Marker (view (), m_cv_index, ! show_shapes_of_instances (), show_shapes_of_instances () ? max_shapes_of_instances () : 0);
marker->set_vertex_shape (lay::ViewOp::Cross);
marker->set_vertex_size (9 /*cross vertex size*/);
set_edit_marker (marker);
update_marker ();
}
@ -1426,9 +1481,18 @@ InstService::make_cell (const lay::CellView &cv)
return std::pair<bool, db::cell_index_type> (true, inst_cell_index);
}
void
void
InstService::do_mouse_move_inactive (const db::DPoint &p)
{
clear_mouse_cursors ();
add_mouse_cursor (snap (p));
}
void
InstService::do_mouse_move (const db::DPoint &p)
{
do_mouse_move_inactive (p);
set_cursor (lay::Cursor::cross);
const lay::CellView &cv = view ()->cellview (m_cv_index);
@ -1470,6 +1534,14 @@ InstService::do_mouse_transform (const db::DPoint &p, db::DFTrans trans)
m_column_x = c.x ();
m_column_y = c.y ();
dispatcher ()->config_set (cfg_edit_inst_angle, m_angle);
dispatcher ()->config_set (cfg_edit_inst_mirror, m_mirror);
dispatcher ()->config_set (cfg_edit_inst_row_x, m_row_x);
dispatcher ()->config_set (cfg_edit_inst_row_y, m_row_y);
dispatcher ()->config_set (cfg_edit_inst_column_x, m_column_x);
dispatcher ()->config_set (cfg_edit_inst_column_y, m_column_y);
dispatcher ()->config_end ();
// honour the new transformation
do_mouse_move (p);
}
@ -1510,6 +1582,8 @@ InstService::do_finish_edit ()
cv->layout ().cleanup ();
manager ()->commit ();
commit_recent (view ());
if (m_in_drag_drop) {
lay::ObjectInstPath sel;
@ -1545,6 +1619,8 @@ InstService::do_cancel_edit ()
m_has_valid_cell = false;
m_in_drag_drop = false;
set_edit_marker (0);
// clean up any proxy cells created so far
const lay::CellView &cv = view ()->cellview (m_cv_index);
if (cv.is_valid ()) {
@ -1552,132 +1628,307 @@ InstService::do_cancel_edit ()
}
}
void
InstService::service_configuration_changed ()
{
m_needs_update = true;
}
bool
InstService::configure (const std::string &name, const std::string &value)
{
if (name == cfg_edit_inst_cell_name) {
m_cell_or_pcell_name = value;
m_needs_update = true;
if (value != m_cell_or_pcell_name) {
m_cell_or_pcell_name = value;
m_needs_update = true;
}
return true; // taken
}
if (name == cfg_edit_inst_lib_name) {
m_lib_name = value;
m_needs_update = true;
if (value != m_lib_name) {
m_lib_name_previous = m_lib_name;
m_lib_name = value;
m_needs_update = true;
}
return true; // taken
}
if (name == cfg_edit_inst_pcell_parameters) {
m_pcell_parameters = pcell_parameters_from_string (value);
m_is_pcell = ! value.empty ();
std::map<std::string, tl::Variant> pcp = pcell_parameters_from_string (value);
if (pcp != m_pcell_parameters) {
m_pcell_parameters = pcp;
m_is_pcell = ! value.empty ();
m_needs_update = true;
m_parameters_changed = true;
}
m_needs_update = true;
return true; // taken
}
if (name == cfg_edit_inst_place_origin) {
tl::from_string (value, m_place_origin);
m_needs_update = true;
bool f;
tl::from_string (value, f);
if (f != m_place_origin) {
m_place_origin = f;
m_needs_update = true;
}
return true; // taken
}
if (name == cfg_edit_inst_scale) {
tl::from_string (value, m_scale);
m_needs_update = true;
double s;
tl::from_string (value, s);
if (fabs (s - m_scale) > 1e-10) {
m_scale = s;
m_needs_update = true;
}
return true; // taken
}
if (name == cfg_edit_inst_angle) {
tl::from_string (value, m_angle);
m_needs_update = true;
double a;
tl::from_string (value, a);
if (fabs (a - m_angle) > 1e-10) {
m_angle = a;
m_needs_update = true;
}
return true; // taken
}
if (name == cfg_edit_inst_mirror) {
tl::from_string (value, m_mirror);
m_needs_update = true;
bool f;
tl::from_string (value, f);
if (f != m_mirror) {
m_mirror = f;
m_needs_update = true;
}
return true; // taken
}
if (name == cfg_edit_inst_array) {
tl::from_string (value, m_array);
m_needs_update = true;
bool f;
tl::from_string (value, f);
if (f != m_array) {
m_array = f;
m_needs_update = true;
}
return true; // taken
}
if (name == cfg_edit_inst_rows) {
tl::from_string (value, m_rows);
m_needs_update = true;
unsigned int v;
tl::from_string (value, v);
if (v != m_rows) {
m_rows = v;
m_needs_update = true;
}
return true; // taken
}
if (name == cfg_edit_inst_row_x) {
tl::from_string (value, m_row_x);
m_needs_update = true;
double v;
tl::from_string (value, v);
if (! db::coord_traits<double>::equal (m_row_x, v)) {
m_row_x = v;
m_needs_update = true;
}
return true; // taken
}
if (name == cfg_edit_inst_row_y) {
tl::from_string (value, m_row_y);
m_needs_update = true;
double v;
tl::from_string (value, v);
if (! db::coord_traits<double>::equal (m_row_y, v)) {
m_row_y = v;
m_needs_update = true;
}
return true; // taken
}
if (name == cfg_edit_inst_columns) {
tl::from_string (value, m_columns);
m_needs_update = true;
unsigned int v;
tl::from_string (value, v);
if (v != m_columns) {
m_columns = v;
m_needs_update = true;
}
return true; // taken
}
if (name == cfg_edit_inst_column_x) {
tl::from_string (value, m_column_x);
m_needs_update = true;
double v;
tl::from_string (value, v);
if (! db::coord_traits<double>::equal (m_column_x, v)) {
m_column_x = v;
m_needs_update = true;
}
return true; // taken
}
if (name == cfg_edit_inst_column_y) {
tl::from_string (value, m_column_y);
m_needs_update = true;
double v;
tl::from_string (value, v);
if (! db::coord_traits<double>::equal (m_column_y, v)) {
m_column_y = v;
m_needs_update = true;
}
return true; // taken
}
return edt::Service::configure (name, value);
}
void
InstService::switch_cell_or_pcell (bool switch_parameters)
{
// if the library or cell name has changed, store the current pcell parameters and try to reuse
// an existing parameter set
if (! m_cell_or_pcell_name_previous.empty () && (m_cell_or_pcell_name_previous != m_cell_or_pcell_name || m_lib_name_previous != m_lib_name)) {
m_stored_pcell_parameters[std::make_pair (m_cell_or_pcell_name_previous, m_lib_name_previous)] = m_pcell_parameters;
if (switch_parameters) {
std::map<std::pair<std::string, std::string>, std::map<std::string, tl::Variant> >::const_iterator p = m_stored_pcell_parameters.find (std::make_pair (m_cell_or_pcell_name, m_lib_name));
if (p != m_stored_pcell_parameters.end ()) {
m_pcell_parameters = p->second;
} else {
m_pcell_parameters.clear ();
}
}
}
db::Library *lib = db::LibraryManager::instance ().lib_ptr_by_name (m_lib_name);
const lay::CellView &cv = view ()->cellview (m_cv_index);
// find the layout the cell has to be looked up: that is either the layout of the current instance or
// the library selected
const db::Layout *layout = 0;
if (lib) {
layout = &lib->layout ();
} else if (cv.is_valid ()) {
layout = &cv->layout ();
}
if (layout) {
m_is_pcell = layout->pcell_by_name (m_cell_or_pcell_name.c_str ()).first;
} else {
m_is_pcell = false;
}
// remember the current cell or library name
m_cell_or_pcell_name_previous = m_cell_or_pcell_name;
m_lib_name_previous = m_lib_name;
}
void
InstService::config_finalize ()
{
if (m_needs_update) {
// don't switch parameters if they have been updated explicitly
// since the last "config_finalize". This means the sender of the configuration events
// wants the parameters to be set in a specific way. Don't interfere.
bool switch_parameters = ! m_parameters_changed;
switch_cell_or_pcell (switch_parameters);
m_has_valid_cell = false;
update_marker ();
m_needs_update = false;
if (switch_parameters) {
// Reflects any changes in PCell parameters in the configuration
// TODO: it's somewhat questionable to do this inside "config_finalize" as this method is supposed
// to reflect changes rather than induce some.
if (m_is_pcell) {
dispatcher ()->config_set (cfg_edit_inst_pcell_parameters, pcell_parameters_to_string (m_pcell_parameters));
} else {
dispatcher ()->config_set (cfg_edit_inst_pcell_parameters, std::string ());
}
}
}
m_needs_update = false;
m_parameters_changed = false;
edt::Service::config_finalize ();
}
void
InstService::apply_edits()
{
if (mp_pcell_decl && mp_pcell_parameters_dialog.get ()) {
m_pcell_parameters = mp_pcell_decl->named_parameters (mp_pcell_parameters_dialog->get_parameters ());
}
sync_to_config ();
}
void
InstService::update_marker ()
{
lay::Marker *marker = dynamic_cast<lay::Marker *> (edit_marker ());
if (marker) {
marker->set ();
if (editing ()) {
lay::Marker *marker = new lay::Marker (view (), m_cv_index, ! show_shapes_of_instances (), show_shapes_of_instances () ? max_shapes_of_instances () : 0);
marker->set_vertex_shape (lay::ViewOp::Cross);
marker->set_vertex_size (9 /*cross vertex size*/);
set_edit_marker (marker);
db::CellInstArray inst;
if (get_inst (inst)) {
marker->set (inst, m_trans);
} else {
marker->set ();
}
} else {
set_edit_marker (0);
}
}
@ -1692,14 +1943,9 @@ InstService::get_inst (db::CellInstArray &inst)
// compute the instance's transformation
db::VCplxTrans pt = (db::CplxTrans (cv->layout ().dbu ()) * m_trans).inverted ();
db::ICplxTrans trans;
if (m_in_drag_drop) {
trans = db::ICplxTrans (1.0, 0.0, false, pt * m_disp - db::Point ());
} else {
trans = db::ICplxTrans (m_scale, m_angle, m_mirror, pt * m_disp - db::Point ());
}
db::ICplxTrans trans = db::ICplxTrans (m_scale, m_angle, m_mirror, pt * m_disp - db::Point ());
if (! m_in_drag_drop && m_array && m_rows > 0 && m_columns > 0) {
if (m_array && m_rows > 0 && m_columns > 0) {
db::Vector row = db::Vector (pt * db::DVector (m_row_x, m_row_y));
db::Vector column = db::Vector (pt * db::DVector (m_column_x, m_column_y));
inst = db::CellInstArray (db::CellInst (ci.second), trans, row, column, m_rows, m_columns);

View File

@ -32,13 +32,12 @@
namespace lay
{
class CellView;
class LayerPropertiesConstIterator;
}
namespace edt
{
class PCellParametersDialog;
/**
* @brief Implementation of the edt::Service for generic shape editing
*/
@ -57,15 +56,17 @@ protected:
db::Cell &cell () const { return *mp_cell; }
db::Layout &layout () const { return *mp_layout; }
void do_mouse_move_inactive (const db::DPoint &p);
virtual void do_mouse_move_inactive (const db::DPoint &p);
virtual void tap (const db::DPoint &initial);
virtual bool configure (const std::string &name, const std::string &value);
bool configure (const std::string &name, const std::string &value);
protected:
std::pair <bool, db::DPoint> interpolate (const db::DPoint &m, const db::DPoint &o, const db::DPoint &p) const;
void deliver_shape (const db::Polygon &poly);
void deliver_shape (const db::Path &path);
void deliver_shape (const db::Box &box);
virtual void current_layer_changed () { }
private:
db::VCplxTrans m_trans;
@ -74,6 +75,8 @@ private:
db::Cell *mp_cell;
db::Layout *mp_layout;
combine_mode_type m_combine_mode;
void update_edit_layer (const lay::LayerPropertiesConstIterator &iter);
};
/**
@ -88,6 +91,7 @@ public:
virtual lay::PropertiesPage *properties_page (db::Manager *manager, QWidget *parent);
virtual void do_begin_edit (const db::DPoint &p);
virtual void do_mouse_move (const db::DPoint &p);
virtual void do_mouse_move_inactive (const db::DPoint &p);
virtual bool do_mouse_click (const db::DPoint &p);
virtual void do_finish_edit ();
virtual void do_cancel_edit ();
@ -117,6 +121,7 @@ public:
virtual lay::PropertiesPage *properties_page (db::Manager *manager, QWidget *parent);
virtual void do_begin_edit (const db::DPoint &p);
virtual void do_mouse_move (const db::DPoint &p);
virtual void do_mouse_move_inactive (const db::DPoint &p);
virtual bool do_mouse_click (const db::DPoint &p);
virtual void do_finish_edit ();
virtual void do_cancel_edit ();
@ -143,6 +148,7 @@ public:
virtual void do_begin_edit (const db::DPoint &p);
virtual void do_mouse_transform (const db::DPoint &p, db::DFTrans trans);
virtual void do_mouse_move (const db::DPoint &p);
virtual void do_mouse_move_inactive (const db::DPoint &p);
virtual bool do_mouse_click (const db::DPoint &p);
virtual void do_finish_edit ();
virtual void do_cancel_edit ();
@ -174,6 +180,7 @@ public:
virtual void do_begin_edit (const db::DPoint &p);
virtual void do_mouse_move (const db::DPoint &p);
virtual bool do_mouse_click (const db::DPoint &p);
virtual void do_mouse_move_inactive (const db::DPoint &p);
virtual void do_finish_edit ();
virtual void do_cancel_edit ();
virtual bool do_activated ();
@ -206,6 +213,7 @@ public:
virtual lay::PropertiesPage *properties_page (db::Manager *manager, QWidget *parent);
virtual void do_begin_edit (const db::DPoint &p);
virtual void do_mouse_move_inactive (const db::DPoint &p);
virtual void do_mouse_move (const db::DPoint &p);
virtual bool do_mouse_click (const db::DPoint &p);
virtual void do_mouse_transform (const db::DPoint &p, db::DFTrans trans);
@ -220,6 +228,8 @@ public:
protected:
bool configure (const std::string &name, const std::string &value);
void service_configuration_changed ();
void config_finalize ();
private:
@ -228,14 +238,16 @@ private:
bool m_mirror;
db::DPoint m_disp;
std::string m_cell_or_pcell_name, m_lib_name;
std::string m_cell_or_pcell_name_previous, m_lib_name_previous;
std::map<std::string, tl::Variant> m_pcell_parameters;
std::map<std::pair<std::string, std::string>, std::map<std::string, tl::Variant> > m_stored_pcell_parameters;
bool m_is_pcell;
bool m_array;
unsigned int m_rows, m_columns;
double m_row_x, m_row_y, m_column_x, m_column_y;
bool m_place_origin;
db::Manager::transaction_id_t m_reference_transaction_id;
bool m_needs_update;
bool m_needs_update, m_parameters_changed;
bool m_has_valid_cell;
bool m_in_drag_drop;
db::cell_index_type m_current_cell;
@ -243,14 +255,13 @@ private:
const db::PCellDeclaration *mp_pcell_decl;
int m_cv_index;
db::ICplxTrans m_trans;
std::auto_ptr<edt::PCellParametersDialog> mp_pcell_parameters_dialog;
void update_marker ();
void apply_edits ();
bool get_inst (db::CellInstArray &inst);
std::pair<bool, db::cell_index_type> make_cell (const lay::CellView &cv);
tl::Variant get_default_layer_for_pcell ();
void sync_to_config ();
void switch_cell_or_pcell (bool switch_parameters);
};
}

View File

@ -26,12 +26,121 @@
#include "dbLibrary.h"
#include "edtUtils.h"
#include "edtService.h"
#include "layCellView.h"
#include "layLayoutView.h"
#include "layEditable.h"
#include "tlException.h"
namespace edt {
// -------------------------------------------------------------
std::string pcell_parameters_to_string (const std::map<std::string, tl::Variant> &parameters)
{
std::string param;
param = "!"; // flags PCells
for (std::map<std::string, tl::Variant>::const_iterator p = parameters.begin (); p != parameters.end (); ++p) {
param += tl::to_word_or_quoted_string (p->first);
param += ":";
param += p->second.to_parsable_string ();
param += ";";
}
return param;
}
std::map<std::string, tl::Variant> pcell_parameters_from_string (const std::string &s)
{
tl::Extractor ex (s.c_str ());
std::map<std::string, tl::Variant> pm;
ex.test ("!");
try {
while (! ex.at_end ()) {
std::string n;
ex.read_word_or_quoted (n);
ex.test (":");
ex.read (pm.insert (std::make_pair (n, tl::Variant ())).first->second);
ex.test (";");
}
} catch (...) {
// ignore errors
}
return pm;
}
// -------------------------------------------------------------
// SelectionIterator implementation
SelectionIterator::SelectionIterator (lay::LayoutView *view, bool including_transient)
: m_transient_mode (false)
{
mp_edt_services = view->get_plugins <edt::Service> ();
m_current_service = mp_edt_services.begin ();
if (m_current_service != mp_edt_services.end ()) {
m_current_object = (*m_current_service)->selection ().begin ();
}
next ();
if (at_end () && including_transient) {
m_transient_mode = true;
m_current_service = mp_edt_services.begin ();
if (m_current_service != mp_edt_services.end ()) {
m_current_object = (*m_current_service)->transient_selection ().begin ();
}
next ();
}
}
bool
SelectionIterator::at_end () const
{
return m_current_service == mp_edt_services.end ();
}
void
SelectionIterator::inc ()
{
tl_assert (! at_end ());
++m_current_object;
}
void
SelectionIterator::next ()
{
if (at_end ()) {
return;
}
const edt::Service::objects *sel = m_transient_mode ? &(*m_current_service)->transient_selection () : &(*m_current_service)->selection ();
while (m_current_object == sel->end ()) {
++m_current_service;
if (m_current_service != mp_edt_services.end ()) {
sel = m_transient_mode ? &(*m_current_service)->transient_selection () : &(*m_current_service)->selection ();
m_current_object = sel->begin ();
} else {
break;
}
}
}
// -------------------------------------------------------------
// TransformationsVariants implementation
// for a lay::LayoutView

View File

@ -27,9 +27,12 @@
#include <limits>
#include <list>
#include <utility>
#include <vector>
#include <QDialog>
#include "layObjectInstPath.h"
#include "dbInstElement.h"
#include "dbClipboardData.h"
#include "dbClipboard.h"
@ -42,6 +45,18 @@ namespace lay
namespace edt {
class Service;
/**
* @brief Serializes PCell parameters to a string
*/
std::string pcell_parameters_to_string (const std::map<std::string, tl::Variant> &parameters);
/**
* @brief Deerializes PCell parameters from a string
*/
std::map<std::string, tl::Variant> pcell_parameters_from_string (const std::string &s);
/**
* @brief Fetch PCell parameters from a cell and merge the guiding shapes into them
*
@ -79,6 +94,73 @@ private:
std::map < std::pair<unsigned int, unsigned int>, std::vector<db::DCplxTrans> > m_per_cv_and_layer_tv;
};
/**
* @brief An iterator for the selected objects of all edt services in a layout view
*/
class SelectionIterator
{
public:
typedef lay::ObjectInstPath value_type;
typedef const lay::ObjectInstPath &reference;
typedef const lay::ObjectInstPath *pointer;
/**
* @brief Creates a new iterator iterating over all selected edt objects from the given view
*
* If "including_transient" is true, the transient selection will be used as fallback.
*/
SelectionIterator (lay::LayoutView *view, bool including_transient = true);
/**
* @brief Returns a value indicating whether the transient selection is taken
*/
bool is_transient () const
{
return m_transient_mode;
}
/**
* @brief Increments the iterator
*/
void operator++ ()
{
inc ();
next ();
}
/**
* @brief Dereferencing
*/
const lay::ObjectInstPath &operator* () const
{
tl_assert (! at_end ());
return *m_current_object;
}
/**
* @brief Arrow operator
*/
const lay::ObjectInstPath *operator-> () const
{
return & operator* ();
}
/**
* @brief Returns a value indicating whether the iterator has finished
*/
bool at_end () const;
private:
void inc ();
void next ();
private:
std::vector<edt::Service *> mp_edt_services;
std::vector<edt::Service *>::const_iterator m_current_service;
std::set<lay::ObjectInstPath>::const_iterator m_current_object;
bool m_transient_mode;
};
} // namespace edt
#endif

View File

@ -0,0 +1,159 @@
/*
KLayout Layout Viewer
Copyright (C) 2006-2020 Matthias Koefferlein
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "tlUnitTest.h"
#include "edtDistribute.h"
template <class Box, class Value>
static std::string plc2string (const edt::distributed_placer<Box, Value> &plc)
{
std::string s;
for (typename edt::distributed_placer<Box, Value>::iterator i = plc.begin (); i != plc.end (); ++i) {
if (! s.empty ()) {
s += ",";
}
s += tl::to_string (i->first);
s += "[";
s += tl::to_string (i->second);
s += "]";
}
return s;
}
TEST(1)
{
edt::distributed_placer<db::Box, size_t> placer;
placer.insert (db::Box (1000, 0, 1100, 200), 0);
placer.insert (db::Box (2000, 0, 2100, 500), 1);
placer.insert (db::Box (0, -100, 100, 100), 2);
placer.insert (db::Box (1000, 100, 1050, 250), 3);
placer.insert (db::Box (1050, -50, 1100, 150), 4);
edt::distributed_placer<db::Box, size_t> p;
p = placer;
p.distribute_h (-1, 2, 0, 100);
EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(200,0;300,200)[0],(400,100;450,250)[3],(550,-50;600,150)[4],(700,0;800,500)[1]");
p = placer;
p.distribute_h (-1, -1, 0, 100);
EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(200,-100;300,100)[0],(400,-100;450,50)[3],(550,-100;600,100)[4],(700,-100;800,400)[1]");
p = placer;
p.distribute_h (-1, 0, 0, 100);
EXPECT_EQ (plc2string (p), "(0,100;100,300)[2],(200,100;300,300)[0],(400,125;450,275)[3],(550,100;600,300)[4],(700,-50;800,450)[1]");
p = placer;
p.distribute_h (-1, 1, 0, 100);
EXPECT_EQ (plc2string (p), "(0,300;100,500)[2],(200,300;300,500)[0],(400,350;450,500)[3],(550,300;600,500)[4],(700,0;800,500)[1]");
p = placer;
p.distribute_h (-1, 2, 100, 0);
EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(100,0;200,200)[0],(200,100;250,250)[3],(300,-50;350,150)[4],(400,0;500,500)[1]");
p = placer;
p.distribute_h (-1, 2, 0, 0);
EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(100,0;200,200)[0],(200,100;250,250)[3],(250,-50;300,150)[4],(300,0;400,500)[1]");
p = placer;
p.distribute_h (1, 2, 0, 100);
EXPECT_EQ (plc2string (p), "(1300,-100;1400,100)[2],(1500,100;1550,250)[3],(1650,-50;1700,150)[4],(1800,0;1900,200)[0],(2000,0;2100,500)[1]");
p = placer;
p.distribute_v (-1, 2, 0, 100);
EXPECT_EQ (plc2string (p), "(0,-100;100,100)[2],(1050,200;1100,400)[4],(1000,500;1100,700)[0],(2000,800;2100,1300)[1],(1000,1400;1050,1550)[3]");
}
TEST(2)
{
edt::distributed_placer<db::Box, size_t> placer;
placer.insert (db::Box (-5, 1, 95, 101), 0);
placer.insert (db::Box (1, 95, 101, 195), 1);
placer.insert (db::Box (110, 105, 210, 205), 2);
placer.insert (db::Box (101, 0, 201, 100), 3);
edt::distributed_placer<db::Box, size_t> p;
p = placer;
p.distribute_matrix (-1, 0, 0, -1, 0, 0);
EXPECT_EQ (plc2string (p), "(-5,0;95,100)[0],(-5,100;95,200)[1],(95,100;195,200)[2],(95,0;195,100)[3]");
}
TEST(3)
{
edt::distributed_placer<db::Box, size_t> placer;
placer.insert (db::Box (0, 20, 1, 23), 0);
placer.insert (db::Box (3, 8, 8, 19), 1);
placer.insert (db::Box (6, 0, 12, 5), 2);
placer.insert (db::Box (13, 1, 19, 6), 3);
placer.insert (db::Box (10, 16, 11, 17), 4);
edt::distributed_placer<db::Box, size_t> p;
p = placer;
p.distribute_matrix (-1, 0, 0, -1, 0, 0);
EXPECT_EQ (plc2string (p), "(0,17;1,20)[0],(1,5;6,16)[1],(6,0;12,5)[2],(13,0;19,5)[3],(12,16;13,17)[4]");
}
TEST(4)
{
edt::distributed_placer<db::Box, size_t> placer;
placer.insert (db::Box (0, 16, 1, 20), 0);
placer.insert (db::Box (0, 8, 5, 19), 1);
placer.insert (db::Box (0, 0, 12, 5), 2);
placer.insert (db::Box (12, 1, 19, 6), 3);
placer.insert (db::Box (0, 18, 1, 19), 4);
edt::distributed_placer<db::Box, size_t> p;
p = placer;
p.distribute_matrix (-1, 0, 0, 1, 0, 0);
EXPECT_EQ (plc2string (p), "(6,9;7,13)[0],(1,9;6,20)[1],(0,4;12,9)[2],(12,4;19,9)[3],(0,9;1,10)[4]");
p = placer;
p.distribute_matrix (1, 10, 0, -1, 10, 0);
EXPECT_EQ (plc2string (p), "(-38,30;-37,34)[0],(-18,10;-13,21)[1],(-8,0;4,5)[2],(12,0;19,5)[3],(-28,30;-27,31)[4]");
p = placer;
p.distribute_matrix (1, 0, 1, 1, 0, 1);
EXPECT_EQ (plc2string (p), "(-9,16;-8,20)[0],(-7,9;-2,20)[1],(-1,3;11,8)[2],(12,3;19,8)[3],(-11,19;-10,20)[4]");
}

View File

@ -8,6 +8,7 @@ include($$PWD/../../lib_ut.pri)
SOURCES = \
edtBasicTests.cc \
edtDistributeTests.cc
INCLUDEPATH += $$EDT_INC $$TL_INC $$LAYBASIC_INC $$DB_INC $$GSI_INC
DEPENDPATH += $$EDT_INC $$TL_INC $$LAYBASIC_INC $$DB_INC $$GSI_INC

View File

@ -1058,16 +1058,6 @@ p, li { white-space: pre-wrap; }
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="preview_cbx">
<property name="text">
<string>Preview (Auto apply)</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
@ -1141,7 +1131,6 @@ p, li { white-space: pre-wrap; }
<tabstop>g_sb</tabstop>
<tabstop>b_slider</tabstop>
<tabstop>b_sb</tabstop>
<tabstop>preview_cbx</tabstop>
<tabstop>reset_pb</tabstop>
<tabstop>contrast_sb</tabstop>
<tabstop>gamma_sb</tabstop>

View File

@ -26,6 +26,7 @@
#include "imgStream.h"
#include "layLayoutView.h"
#include "layFileDialog.h"
#include "layQtTools.h"
#include "tlExceptions.h"
#include "tlFileUtils.h"
@ -73,6 +74,7 @@ void
PropertiesPage::init ()
{
m_no_signals = false;
m_in_color_mapping_signal = false;
setupUi (this);
@ -108,6 +110,7 @@ PropertiesPage::init ()
connect (browse_pb, SIGNAL (clicked ()), this, SLOT (browse ()));
connect (colors, SIGNAL (color_changed (std::pair<QColor, QColor>)), false_color_control, SLOT (set_current_color (std::pair<QColor, QColor>)));
connect (false_color_control, SIGNAL (selection_changed (std::pair<QColor, QColor>)), colors, SLOT (set_color (std::pair<QColor, QColor>)));
connect (false_color_control, SIGNAL (color_mapping_changed ()), this, SLOT (color_mapping_changed ()));
connect (brightness_slider, SIGNAL (valueChanged (int)), this, SLOT (brightness_slider_changed (int)));
connect (brightness_sb, SIGNAL (valueChanged (int)), this, SLOT (brightness_spinbox_changed (int)));
@ -122,15 +125,22 @@ PropertiesPage::init ()
connect (b_slider, SIGNAL (valueChanged (int)), this, SLOT (blue_slider_changed (int)));
connect (b_sb, SIGNAL (valueChanged (double)), this, SLOT (blue_spinbox_changed (double)));
connect (false_color_control, SIGNAL (color_mapping_changed ()), this, SLOT (color_mapping_changed ()));
connect (false_color_control, SIGNAL (selection_changed ()), this, SLOT (color_mapping_changed ()));
connect (from_le, SIGNAL (returnPressed ()), this, SLOT (min_max_return_pressed ()));
connect (to_le, SIGNAL (returnPressed ()), this, SLOT (min_max_return_pressed ()));
connect (value_le, SIGNAL (returnPressed ()), this, SLOT (value_return_pressed ()));
connect (from_le, SIGNAL (editingFinished ()), this, SLOT (min_max_value_changed ()));
connect (to_le, SIGNAL (editingFinished ()), this, SLOT (min_max_value_changed ()));
connect (value_le, SIGNAL (editingFinished ()), this, SLOT (value_changed ()));
connect (width_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (height_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (x_offset_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (y_offset_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (angle_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (shear_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (persp_tx_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (persp_ty_le, SIGNAL (editingFinished ()), this, SIGNAL (edited ()));
connect (mirror_cbx, SIGNAL (clicked ()), this, SIGNAL (edited ()));
connect (reset_pb, SIGNAL (clicked ()), this, SLOT (reset_pressed ()));
connect (save_pb, SIGNAL (clicked ()), this, SLOT (save_pressed ()));
connect (preview_cbx, SIGNAL (clicked ()), this, SLOT (preview_checked ()));
connect (define_landmarks_pb, SIGNAL (clicked ()), this, SLOT (define_landmarks_pressed ()));
}
@ -202,11 +212,42 @@ PropertiesPage::readonly ()
return false;
}
void
PropertiesPage::min_max_return_pressed ()
void
PropertiesPage::get_xmin_xmax (double &xmin, double &xmax, bool &has_error_out)
{
BEGIN_PROTECTED
bool has_error = false;
try {
tl::from_string (tl::to_string (from_le->text ()), xmin);
lay::indicate_error (from_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (from_le, &ex);
has_error = true;
}
try {
tl::from_string (tl::to_string (to_le->text ()), xmax);
lay::indicate_error (to_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (to_le, &ex);
has_error = true;
}
if (! has_error && xmin >= xmax) {
tl::Exception ex (tl::to_string (QObject::tr ("Invalid data value range (min. value must be less than max. value)")));
lay::indicate_error (from_le, &ex);
lay::indicate_error (to_le, &ex);
has_error = true;
}
if (has_error) {
has_error_out = true;
}
}
void
PropertiesPage::min_max_value_changed ()
{
value_le->setText (QString ());
value_le->setEnabled (false);
@ -214,10 +255,11 @@ BEGIN_PROTECTED
colors->set_single_mode (false);
double xmin, xmax;
tl::from_string (tl::to_string (from_le->text ()), xmin);
tl::from_string (tl::to_string (to_le->text ()), xmax);
if (xmin >= xmax) {
throw tl::Exception (tl::to_string (QObject::tr ("Invalid data value range (min. value must be less than max. value)")));
bool has_error = false;
get_xmin_xmax (xmin, xmax, has_error);
if (has_error) {
return;
}
if (false_color_control->has_selection () && false_color_control->selected_node () > 0 && false_color_control->selected_node () < int (false_color_control->nodes ().size ()) - 1) {
@ -236,9 +278,7 @@ BEGIN_PROTECTED
recompute_histogram ();
preview ();
END_PROTECTED
emit edited ();
}
void
@ -246,22 +286,20 @@ PropertiesPage::color_mapping_changed ()
{
if (! m_no_signals) {
bool has_error = false;
value_le->setText (QString ());
value_le->setEnabled (false);
colors->setEnabled (false_color_control->has_selection ());
colors->set_single_mode (false);
try {
if (false_color_control->has_selection () && false_color_control->selected_node () > 0 && false_color_control->selected_node () < int (false_color_control->nodes ().size ()) - 1) {
if (false_color_control->has_selection () && false_color_control->selected_node () > 0 && false_color_control->selected_node () < int (false_color_control->nodes ().size ()) - 1) {
double xmin, xmax;
get_xmin_xmax (xmin, xmax, has_error);
double xmin, xmax;
tl::from_string (tl::to_string (from_le->text ()), xmin);
tl::from_string (tl::to_string (to_le->text ()), xmax);
if (xmin >= xmax) {
throw tl::Exception ("");
}
if (! has_error) {
double x = false_color_control->nodes () [false_color_control->selected_node ()].first;
double xx = x * (xmax - xmin) + xmin;
@ -269,46 +307,57 @@ PropertiesPage::color_mapping_changed ()
value_le->setText (tl::to_qstring (tl::sprintf ("%.4g", xx)));
value_le->setEnabled (true);
} else if (false_color_control->has_selection ()) {
colors->set_single_mode (true);
}
} catch (...) { }
} else if (false_color_control->has_selection ()) {
preview ();
colors->set_single_mode (true);
}
if (! has_error) {
m_in_color_mapping_signal = true;
emit edited ();
m_in_color_mapping_signal = false;
}
}
}
void
PropertiesPage::value_return_pressed ()
PropertiesPage::value_changed ()
{
BEGIN_PROTECTED
double xx = 0;
bool has_error = false;
double xmin, xmax;
tl::from_string (tl::to_string (from_le->text ()), xmin);
tl::from_string (tl::to_string (to_le->text ()), xmax);
if (xmin >= xmax) {
throw tl::Exception (tl::to_string (QObject::tr ("Invalid data value range (min. value must be less than max. value)")));
}
get_xmin_xmax (xmin, xmax, has_error);
double x = 0.0;
tl::from_string (tl::to_string (value_le->text ()), x);
double xx = (x - xmin) / (xmax - xmin);
if (xx < 0 || xx > 1.0) {
throw tl::Exception (tl::to_string (QObject::tr ("The position entered (%g) must be between the minimum (%g) and maximum (%g) value")), x, xmin, xmax);
try {
tl::from_string (tl::to_string (value_le->text ()), x);
lay::indicate_error (value_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (value_le, &ex);
has_error = true;
}
m_no_signals = true;
false_color_control->set_current_position (xx);
m_no_signals = false;
xx = (x - xmin) / (xmax - xmin);
if (! has_error && (xx < 0 || xx > 1.0)) {
tl::Exception ex (tl::to_string (QObject::tr ("The position entered (%g) must be between the minimum (%g) and maximum (%g) value")), x, xmin, xmax);
lay::indicate_error (value_le, &ex);
has_error = true;
}
preview ();
if (! has_error) {
END_PROTECTED
m_no_signals = true;
false_color_control->set_current_position (xx);
m_no_signals = false;
emit edited ();
}
}
inline double
@ -320,6 +369,10 @@ round_to_zero (double x)
void
PropertiesPage::update ()
{
if (m_in_color_mapping_signal) {
return;
}
m_no_signals = true;
if (mp_service) {
@ -498,7 +551,7 @@ PropertiesPage::brightness_slider_changed (int value)
m_no_signals = true;
brightness_sb->setValue (value);
preview ();
emit edited ();
m_no_signals = false;
}
@ -511,7 +564,7 @@ PropertiesPage::brightness_spinbox_changed (int value)
m_no_signals = true;
brightness_slider->setValue (value);
preview ();
emit edited ();
m_no_signals = false;
}
@ -524,7 +577,7 @@ PropertiesPage::contrast_slider_changed (int value)
m_no_signals = true;
contrast_sb->setValue (value);
preview ();
emit edited ();
m_no_signals = false;
}
@ -537,7 +590,7 @@ PropertiesPage::contrast_spinbox_changed (int value)
m_no_signals = true;
contrast_slider->setValue (value);
preview ();
emit edited ();
m_no_signals = false;
}
@ -556,7 +609,7 @@ PropertiesPage::gamma_spinbox_changed (double value)
gamma_slider->setValue (50 + int (0.5 + (value - 1.0) / (max_gamma - 1.0) * 50.0));
}
preview ();
emit edited ();
m_no_signals = false;
}
@ -578,7 +631,7 @@ PropertiesPage::gamma_slider_changed (int value)
}
gamma_sb->setValue (gamma);
preview ();
emit edited ();
m_no_signals = false;
}
@ -595,7 +648,7 @@ PropertiesPage::red_slider_changed (int value)
double gain = value * 0.02;
r_sb->setValue (gain);
preview ();
emit edited ();
m_no_signals = false;
}
@ -610,7 +663,7 @@ PropertiesPage::red_spinbox_changed (double value)
m_no_signals = true;
r_slider->setValue (int (0.5 + value * 50.0));
preview ();
emit edited ();
m_no_signals = false;
}
@ -627,7 +680,7 @@ PropertiesPage::green_slider_changed (int value)
double gain = value * 0.02;
g_sb->setValue (gain);
preview ();
emit edited ();
m_no_signals = false;
}
@ -642,7 +695,7 @@ PropertiesPage::green_spinbox_changed (double value)
m_no_signals = true;
g_slider->setValue (int (0.5 + value * 50.0));
preview ();
emit edited ();
m_no_signals = false;
}
@ -659,7 +712,7 @@ PropertiesPage::blue_slider_changed (int value)
double gain = value * 0.02;
b_sb->setValue (gain);
preview ();
emit edited ();
m_no_signals = false;
}
@ -674,9 +727,9 @@ PropertiesPage::blue_spinbox_changed (double value)
m_no_signals = true;
b_slider->setValue (int (0.5 + value * 50.0));
preview ();
emit edited ();
m_no_signals = false;
m_no_signals = false;
}
void
@ -686,6 +739,7 @@ PropertiesPage::black_to_white ()
nodes.push_back (std::make_pair (0.0, std::make_pair (QColor (0, 0, 0), QColor (0, 0, 0))));
nodes.push_back (std::make_pair (1.0, std::make_pair (QColor (255, 255, 255), QColor (255, 255, 255))));
false_color_control->set_nodes (nodes);
emit edited ();
}
void
@ -695,6 +749,7 @@ PropertiesPage::white_to_black ()
nodes.push_back (std::make_pair (0.0, std::make_pair (QColor (255, 255, 255), QColor (255, 255, 255))));
nodes.push_back (std::make_pair (1.0, std::make_pair (QColor (0, 0, 0), QColor (0, 0, 0))));
false_color_control->set_nodes (nodes);
emit edited ();
}
void
@ -704,7 +759,7 @@ PropertiesPage::red_to_blue ()
nodes.push_back (std::make_pair (0.0, std::make_pair (QColor (255, 0, 0), QColor (255, 0, 0))));
nodes.push_back (std::make_pair (1.0, std::make_pair (QColor (0, 0, 255), QColor (0, 0, 255))));
false_color_control->set_nodes (nodes);
emit edited ();
}
void
@ -714,6 +769,7 @@ PropertiesPage::blue_to_red ()
nodes.push_back (std::make_pair (0.0, std::make_pair (QColor (0, 0, 255), QColor (0, 0, 255))));
nodes.push_back (std::make_pair (1.0, std::make_pair (QColor (255, 0, 0), QColor (255, 0, 0))));
false_color_control->set_nodes (nodes);
emit edited ();
}
void
@ -725,11 +781,14 @@ PropertiesPage::reverse_color_order ()
std::swap (nodes [i].second.first, nodes [nodes.size () - 1 - i].second.second);
}
false_color_control->set_nodes (nodes);
emit edited ();
}
void
PropertiesPage::apply ()
{
bool has_error = false;
db::Matrix3d matrix = mp_direct_image->matrix ();
// The observer distance for perspective distortion is the average of the images width and height.
@ -741,45 +800,89 @@ PropertiesPage::apply ()
double w = matrix.mag_x (), h = matrix.mag_y (), x = matrix.disp ().x (), y = matrix.disp ().y (),
a = matrix.angle (), sa = matrix.shear_angle (), tx = matrix.perspective_tilt_x (z), ty = matrix.perspective_tilt_y (z);
bool mirror;
if (width_le->text () != tl::to_qstring (tl::micron_to_string (matrix.mag_x ()))) {
try {
tl::from_string (tl::to_string (width_le->text ()), w);
if (w <= 0.0 || h <= 0.0) {
throw tl::Exception (tl::to_string (QObject::tr ("Pixel width or height must be positive, non-null values")));
}
lay::indicate_error (width_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (width_le, &ex);
has_error = true;
}
if (height_le->text () != tl::to_qstring (tl::micron_to_string (matrix.mag_y ()))) {
try {
tl::from_string (tl::to_string (height_le->text ()), h);
lay::indicate_error (height_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (height_le, &ex);
has_error = true;
}
if (x_offset_le->text () != tl::to_qstring (tl::micron_to_string (round_to_zero (matrix.disp ().x ())))) {
try {
tl::from_string (tl::to_string (x_offset_le->text ()), x);
lay::indicate_error (x_offset_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (x_offset_le, &ex);
has_error = true;
}
if (y_offset_le->text () != tl::to_qstring (tl::micron_to_string (round_to_zero (matrix.disp ().y ())))) {
try {
tl::from_string (tl::to_string (y_offset_le->text ()), y);
lay::indicate_error (y_offset_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (y_offset_le, &ex);
has_error = true;
}
if (angle_le->text () != tl::to_qstring (tl::to_string (round_to_zero (matrix.angle ())))) {
try {
tl::from_string (tl::to_string (angle_le->text ()), a);
lay::indicate_error (angle_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (angle_le, &ex);
has_error = true;
}
if (shear_le->text () != tl::to_qstring (tl::to_string (round_to_zero (matrix.shear_angle ())))) {
try {
tl::from_string (tl::to_string (shear_le->text ()), sa);
if (sa <= -45 || sa >= 45) {
throw tl::Exception (tl::to_string (QObject::tr ("The shear angle must be larger than -45 and less than 45 degree")));
}
lay::indicate_error (shear_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (shear_le, &ex);
has_error = true;
}
if (persp_tx_le->text () != tl::to_qstring (tl::to_string (round_to_zero (matrix.perspective_tilt_x (z))))) {
try {
tl::from_string (tl::to_string (persp_tx_le->text ()), tx);
if (tx <= -90 || tx >= 90) {
throw tl::Exception (tl::to_string (QObject::tr ("The perspective tilt angles must be larger than -90 and less than 90 degree")));
}
lay::indicate_error (persp_tx_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (persp_tx_le, &ex);
has_error = true;
}
if (persp_ty_le->text () != tl::to_qstring (tl::to_string (round_to_zero (matrix.perspective_tilt_y (z))))) {
try {
tl::from_string (tl::to_string (persp_ty_le->text ()), ty);
if (ty <= -90 || ty >= 90) {
throw tl::Exception (tl::to_string (QObject::tr ("The perspective tilt angles must be larger than -90 and less than 90 degree")));
}
lay::indicate_error (persp_ty_le, 0);
} catch (tl::Exception &ex) {
lay::indicate_error (persp_ty_le, &ex);
has_error = true;
}
mirror = mirror_cbx->isChecked ();
bool mirror = mirror_cbx->isChecked ();
if (w <= 0.0 || h <= 0.0) {
throw tl::Exception (tl::to_string (QObject::tr ("Pixel width or height must be positive, non-null values")));
}
double xmin, xmax;
get_xmin_xmax (xmin, xmax, has_error);
if (sa <= -45 || sa >= 45) {
throw tl::Exception (tl::to_string (QObject::tr ("The shear angle must be larger than -45 and less than 45 degree")));
}
if (tx <= -90 || tx >= 90 || ty <= -90 || ty >= 90) {
throw tl::Exception (tl::to_string (QObject::tr ("The perspective tilt angles must be larger than -90 and less than 90 degree")));
if (has_error) {
throw tl::Exception (tl::to_string (tr ("At least one value is invalid - see highlighted entry fields")));
}
// Compute the new observer distance
@ -790,13 +893,6 @@ PropertiesPage::apply ()
matrix = db::Matrix3d::disp (db::DVector (x, y)) * db::Matrix3d::perspective (tx, ty, z) * db::Matrix3d::rotation (a) * db::Matrix3d::shear (sa) * db::Matrix3d::mag (w, h) * db::Matrix3d::mirror (mirror);
mp_direct_image->set_matrix (matrix);
double xmin, xmax;
tl::from_string (tl::to_string (from_le->text ()), xmin);
tl::from_string (tl::to_string (to_le->text ()), xmax);
if (xmin >= xmax) {
throw tl::Exception (tl::to_string (QObject::tr ("Invalid data value range (min. value must be less than max. value)")));
}
mp_direct_image->set_min_value (xmin);
mp_direct_image->set_max_value (xmax);
@ -891,30 +987,7 @@ PropertiesPage::reset_pressed ()
m_no_signals = false;
preview ();
}
void
PropertiesPage::preview_checked ()
{
preview ();
}
void
PropertiesPage::preview ()
{
if (preview_cbx->isChecked ()) {
BEGIN_PROTECTED_CLEANUP
apply (); // this is a HACK, because it changes the current object
END_PROTECTED_CLEANUP
{
preview_cbx->setChecked (false);
}
}
emit edited ();
}
void
@ -922,7 +995,9 @@ PropertiesPage::define_landmarks_pressed ()
{
if (mp_direct_image) {
img::LandmarksDialog dialog (this, *mp_direct_image);
dialog.exec ();
if (dialog.exec ()) {
emit edited ();
}
}
}

View File

@ -64,7 +64,7 @@ public:
private slots:
void browse ();
void value_return_pressed ();
void value_changed ();
void color_mapping_changed ();
void brightness_slider_changed (int value);
void brightness_spinbox_changed (int value);
@ -83,8 +83,7 @@ private slots:
void red_to_blue ();
void blue_to_red ();
void reverse_color_order ();
void min_max_return_pressed ();
void preview_checked ();
void min_max_value_changed ();
void reset_pressed ();
void save_pressed ();
void define_landmarks_pressed ();
@ -95,11 +94,12 @@ private:
img::Service *mp_service;
img::Object *mp_direct_image;
bool m_no_signals;
bool m_in_color_mapping_signal;
void recompute_histogram ();
void invalidate ();
void init ();
void preview ();
void get_xmin_xmax (double &xmin, double &xmax, bool &has_error_out);
};
}

View File

@ -160,7 +160,7 @@ equals(HAVE_QT, "0") {
} else {
DEFINES += HAVE_QT
QT += core network xml sql
QT += core network xml sql widgets
equals(HAVE_QT5, "1") {
QT += designer printsupport

Some files were not shown because too many files have changed in this diff Show More