mirror of
https://github.com/arsenetar/dupeguru.git
synced 2026-01-24 23:51:38 +00:00
Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
350b2c64e0 | ||
|
|
dcc57a7afb | ||
|
|
8b510994ad | ||
|
|
4a4d1bbfcd | ||
|
|
78c3c8ec2d | ||
|
|
e99e2b18e0 | ||
|
|
ae1283f2e1 | ||
|
|
cc76f3ca87 | ||
|
|
be8efea081 | ||
|
|
7e8f9036d8 | ||
|
|
8a8ac027f5 | ||
|
|
1d9d09fdf7 | ||
|
|
5dc956870d | ||
|
|
d8f48cbd42 | ||
|
|
39d24817f6 | ||
|
|
2364e44707 | ||
|
|
3e2249bf89 | ||
|
|
38acb6f91c | ||
|
|
9bcb28d5e2 | ||
|
|
d0a3f081da | ||
|
|
d11ec557e7 | ||
|
|
b9124a497c | ||
|
|
502715cfd6 | ||
|
|
e1f532e2fd | ||
|
|
a71033d9d6 | ||
|
|
a15a62f55c | ||
|
|
2fe5cdcf02 | ||
|
|
21c64545e5 | ||
|
|
c93a88f8b0 | ||
|
|
86a81eab4e | ||
|
|
1c779cb3ec | ||
|
|
04949c853d | ||
|
|
ff782a09f5 | ||
|
|
e5ce6680ca | ||
|
|
8e15d89a2e | ||
|
|
d874f26f06 | ||
|
|
80a99ff29e | ||
|
|
b11b97dd7c | ||
|
|
386a5f2c64 | ||
|
|
c13a2f207c | ||
|
|
d36710ef38 | ||
|
|
bbc9b003c6 | ||
|
|
3edba28f0b | ||
|
|
9304f42f69 | ||
|
|
375963ebfd | ||
|
|
7891fb5396 | ||
|
|
bdd5f0a515 | ||
|
|
db0901b1de | ||
|
|
9225697053 | ||
|
|
097b949763 | ||
|
|
60701c2a5c | ||
|
|
3ef1281450 | ||
|
|
af4e74a130 | ||
|
|
422fb2670d | ||
|
|
94a469205a | ||
|
|
95623f9b47 | ||
|
|
a65c246a2e | ||
|
|
045d496a98 | ||
|
|
5ed98b3d92 | ||
|
|
5799e3548b | ||
|
|
b01ed1e9f3 |
22
.gitignore
vendored
Normal file
22
.gitignore
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
.DS_Store
|
||||
*.pyc
|
||||
*.so
|
||||
*.mo
|
||||
*.pyd
|
||||
*.waf*
|
||||
.lock-waf*
|
||||
|
||||
build
|
||||
dist
|
||||
install
|
||||
installer_tmp-cache
|
||||
env
|
||||
cocoa/autogen
|
||||
|
||||
/run.py
|
||||
/conf.json
|
||||
/cocoa/*/Info.plist
|
||||
/cocoa/*/build
|
||||
/qt/base/*_rc.py
|
||||
/help/*/conf.py
|
||||
/help/*/changelog.rst
|
||||
22
.hgignore
22
.hgignore
@@ -1,22 +0,0 @@
|
||||
syntax: glob
|
||||
|
||||
.DS_Store
|
||||
run.py
|
||||
*.pyc
|
||||
*.so
|
||||
*.mo
|
||||
*.pyd
|
||||
*.waf*
|
||||
.lock-waf*
|
||||
conf.json
|
||||
build
|
||||
dist
|
||||
install
|
||||
installer_tmp-cache
|
||||
env
|
||||
cocoa/autogen
|
||||
cocoa/*/Info.plist
|
||||
cocoa/*/build
|
||||
qt/base/*_rc.py
|
||||
help/*/conf.py
|
||||
help/*/changelog.rst
|
||||
87
.hgtags
87
.hgtags
@@ -1,87 +0,0 @@
|
||||
0ef0ca83b49ad009c896f55824189acc932bcf22 se2.8.2
|
||||
0ef0ca83b49ad009c896f55824189acc932bcf22 me5.6.6
|
||||
0ef0ca83b49ad009c896f55824189acc932bcf22 pe1.7.8
|
||||
a8f232f880b6f9ada565d472996a627ebf69b6e9 before-tiger-drop
|
||||
321d15e818cf9a3f1fc037543090bb2fca2cccd7 me5.7.0
|
||||
adc73ccd14b1386cb04dee773c53a2d126800e31 se2.9.0
|
||||
cbcf9c80fee4c908ef2efbf1c143c9e47676c9b2 pe1.8.0
|
||||
61c4101851bdea3cb37dfb76f0d404c78c7c594c se2.9.1
|
||||
0e923897a3389331d4ab3debbc40b8dd616199d9 pe1.8.1
|
||||
2c454eca9ebe93b6cf34916068f828a6a39e3eaf me5.7.1
|
||||
19e40bab20521d4256acf325dba9b32e95e135c5 pe1.8.2
|
||||
7b7c5a66ebee4e4b8125330d24fe9ce1a070ff25 se2.9.2
|
||||
1cef6d39855f85d4be728646bc78b860e6d4e398 pe1.8.3
|
||||
90ed56ee602666db2f267f73eac6f824347039b5 me5.7.2
|
||||
4c3cb1e671a333eabde1151c7c6ffb3609cab025 pe1.8.4
|
||||
0a71306434bca51bea9a5d5ae54fe1bf0e4900d8 pe1.8.5
|
||||
556baf4a410779e9bbf43129de133e4c4b26d679 pe1.8.6
|
||||
9149024283959a50fe9a47a5f175b905d1672c19 se2.10.0
|
||||
388a7e5aef6385e515189f4a15b4c4fed3ae2fcf me5.8.0
|
||||
27501167e3b9262ecb60c967941294f36d77eb25 pe1.9.0
|
||||
cb0a860430bacd712820bce426bcf47a4135efe1 se2.10.1
|
||||
cb0a860430bacd712820bce426bcf47a4135efe1 se2.10.1
|
||||
f71d405e62badcfdc1b037facaac043cece40ee5 se2.10.1
|
||||
3742e83edd9eadf44e1a501859f5e2462b1ef6fd me5.8.1
|
||||
724ff565dd785fb739774588c6ee652cfc0612d5 pe1.9.1
|
||||
634b66415c6529f46ae4f837318027cc9d70c3b5 before-py3k
|
||||
2b67955db2b0580a8b0854dc918b6ab0d1fa3b88 se2.11.0
|
||||
b56fe4dd8c95bca270b078a09e86848df77e2b2d me5.9.0
|
||||
618a7365457d56fdc6920c70843a244762e2ea00 pe1.10.0
|
||||
95b3a4b564c6222b414f2b40182dde2bd6d0e8a4 me5.9.1
|
||||
9735a5218d2b5b3b1e1dfe17f2f874177cf8f61c se2.11.1
|
||||
dbfee3ee2fa5cbb9e7ab36570659c17cd5b8561f se2.12.0
|
||||
d3fe0d0dcda1e0bf1100d02f117503d3bf6baacf me5.10.0
|
||||
b07ac1398703dd358912c1f3d20bd995633db9fe pe1.11.0
|
||||
96b6aee668398d663b04eafc8d5dae05e18500ee before-fairware
|
||||
22239f94589baf2a9fad2123045b8a718dbd68f5 se2.12.2
|
||||
f9cae82a0752191276b24ffb2cc4e4a8afb5d754 me5.10.2
|
||||
154c8cb6f018d446d88fa099490c900906e86386 pe1.11.2
|
||||
ca93352ce35184853ad9fcb881935a43a8b1e249 me5.10.3
|
||||
44f6ff67066c083f79daa18a9d2f1ab909e0a62e me5.10.4
|
||||
3f71a8f5bf8f6d0729748a27af9163e013723294 pe1.11.3
|
||||
0056293b0dade8b8230f68c1fe6f0c2d1e0b74d8 se2.12.3
|
||||
8d12cab3b12b723e3a86d02cf8002731a0f73f95 se3.0.0
|
||||
778876a8a9787658aa6adf6944b53aebcb7faeea se3.0.1
|
||||
f1d40b556c01f32c58f9ef9f9acac5b78e01ba7a pe2.0.0
|
||||
2fd901a516f8cb6b4438491f63f2ebfd52a57c13 me6.0.0
|
||||
ff43c6d9feb388f103b7857eaa6f7809185f78ec before-pluginbuilder
|
||||
d274bcb98f2d02b86470a04cd62e718eff33b74f pe2.1.0
|
||||
77e169f757195c11e9c1c5febeb2db8eb3589510 se3.0.2
|
||||
97893f37d7d0767b5aedf1b4b40de57ee36d426b se3.1.0
|
||||
e44d5127ed605daa7a17a01eee65d0a157de20c0 pe2.2.0
|
||||
ecf9aaa568340e3d03e8854b7556edd5a3285107 pe2.2.1
|
||||
db1f325c907ffa9808a49cb7bc2886b9fca7aee2 se3.1.1
|
||||
e62183e907d6177cf0bac354e31afa9546c199e6 se3.1.2
|
||||
28ba95706dc54ba32b1c0cf4e1e6350515d19ba3 me6.0.2
|
||||
925847384dcef62a5c3518fc9e5ce42feab2b093 pe2.2.2
|
||||
383b14d6e8555ed2c8d5552259bc7ce600dfb1d0 before-leopard-drop
|
||||
a2f7b7302e178f08725a6404ddc28464409510b1 se3.2.0
|
||||
5a5134a4fa9bb7ca80ae3e32010030f5bd7ba434 me6.1.0
|
||||
0fd77be57ff716d5c93232e829dc02acec036d7c se3.2.1
|
||||
3dd08060135b0b9cef70b6f5a81f191ea339c8d5 me6.1.1
|
||||
4e6cbef6bcdfcc0e56ff9690fbfe1cac1f4b1b09 pe2.3.0
|
||||
9ea9af1b886cd1adc4f42fd2276cc2b38376eab0 se3.3.0
|
||||
6e3379be6821bb36d7f0877a17dd6c07aa037b0a se3.3.1
|
||||
015ba7e2c10d09afb944f387c2a9c97f7eff7571 me6.2.0
|
||||
8178bda48324461a17118c98634241952c074f29 pe2.4.0
|
||||
2a96f2fb3ddb6f1e0ae87951586733fc3e4a28a0 se3.3.2
|
||||
6a08c1205dfe5e537e5c2dc99d05e05d1d3928f6 me6.2.1
|
||||
a619f313712e2923160b8f90d8250ee0e184c7b9 pe2.4.1
|
||||
fad463ae749b7189dce92f1e42a57ac4ee03987d se3.3.3
|
||||
236cf9b690a144392e7e86e7c9749fc834a8b271 me6.3.0
|
||||
90318f1303858d9d01065d92d78d98b888b38ea0 se3.4.0
|
||||
93ed33410df2d2f21229a77ae49c83ece2c50a55 pe2.5.0
|
||||
c153aef25e5c9911f2197d13899591c50cf38ffc se3.4.1
|
||||
71b7e18613f3790cea18cb0dd8c9c986ce237267 me6.3.1
|
||||
c3d9f91dc9c9d60f370c72bc211f09be3e4fc18d se3.5.0
|
||||
254bce83ad6e56c102d69fd603f6845e2324b470 me6.4.0
|
||||
e772f1de86744999ffbbe5845554417965b1dfba me6.4.1
|
||||
c8a9a4d355927e509f514308c82306192bc71f92 pe2.6.0
|
||||
a618e954f01e4bbdbe9a03e5667a67d62be995a7 me6.4.2
|
||||
0f18c4498a6c7529bf77207db70aed8a5ec96ee4 se3.6.0
|
||||
8f478379ec62fd1329d527aafb1ab0f2410f3a79 me6.5.0
|
||||
d773721e6c3260f8130f40b4ab10442edc9965ec pe2.7.0
|
||||
6b42e0d5628b937aee8039ee34d4b329149718a5 se3.6.0-arch
|
||||
df6e045b9e7679f2a1949a57060e5c1863904444 me6.5.0-arch
|
||||
286ba6959cd0af059f245371a3afb52c1da91dee pe2.7.0-arch
|
||||
810ab1e1324ed32dbd3b4db425e590dc0e344358 se3.6.1
|
||||
@@ -1,5 +1,5 @@
|
||||
[main]
|
||||
host = https://www.transifex.net
|
||||
host = https://www.transifex.com
|
||||
|
||||
[dupeguru.core]
|
||||
file_filter = locale/<lang>/LC_MESSAGES/core.po
|
||||
|
||||
94
README.md
Normal file
94
README.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# dupeGuru
|
||||
|
||||
This package contains the source for dupeGuru. Its documentation is
|
||||
[available online][documentation]. Here's how this source tree is organised:
|
||||
|
||||
* core: Contains the core logic code for dupeGuru. It's Python code.
|
||||
* core_*: Edition-specific-cross-toolkit code written in Python.
|
||||
* cocoa: UI code for the Cocoa toolkit. It's Objective-C code.
|
||||
* qt: UI code for the Qt toolkit. It's written in Python and uses PyQt.
|
||||
* images: Images used by the different UI codebases.
|
||||
* debian: Skeleton files required to create a .deb package
|
||||
* help: Help document, written for Sphinx.
|
||||
* locale: .po files for localisation.
|
||||
|
||||
There are also other sub-folder that comes from external repositories and are part of this repo as
|
||||
git subtrees:
|
||||
|
||||
* hscommon: A collection of helpers used across HS applications.
|
||||
* cocoalib: A collection of helpers used across Cocoa UI codebases of HS applications.
|
||||
* qtlib: A collection of helpers used across Qt UI codebases of HS applications.
|
||||
|
||||
# How to build dupeGuru from source
|
||||
|
||||
## The very, very, very easy way
|
||||
|
||||
If you're on Linux or Mac, there's a bootstrap script that will make building very, very easy. There
|
||||
might be some things that you need to install manually on your system, but the bootstrap script will
|
||||
tell you when what you need to install. You can run the bootstrap with:
|
||||
|
||||
./bootstrap.sh
|
||||
|
||||
and follow instructions from the script. You can then ignore the rest of the build documentation.
|
||||
|
||||
## Prerequisites installation
|
||||
|
||||
Prerequisites are installed through `pip`. However, some of them are not "pip installable" and have
|
||||
to be installed manually.
|
||||
|
||||
* All systems: [Python 3.2+][python] and [setuptools][setuptools]
|
||||
* Mac OS X: The last XCode to have the 10.6 SDK included.
|
||||
* Windows: Visual Studio 2008, [PyQt 4.7+][pyqt], [cx_Freeze][cxfreeze] and
|
||||
[Advanced Installer][advinst] (you only need the last two if you want to create an installer)
|
||||
|
||||
On Ubuntu, the apt-get command to install all pre-requisites is:
|
||||
|
||||
$ apt-get install python3-dev python3-pyqt4 pyqt4-dev-tools python3-setuptools
|
||||
|
||||
## Virtualenv setup
|
||||
|
||||
First, you need `pip` and `virtualenv` in your system Python install:
|
||||
|
||||
$ sudo easy_install pip
|
||||
$ sudo pip install virtualenv
|
||||
|
||||
Then, in dupeGuru's source folder, create a virtual environment and activate it:
|
||||
|
||||
$ virtualenv --system-site-packages env
|
||||
$ source env/bin/activate
|
||||
|
||||
Then, you can install pip requirements in your virtualenv:
|
||||
|
||||
$ pip install -r requirements-[osx|win].txt
|
||||
|
||||
([osx|win] depends, of course, on your platform. On other platforms, just use requirements.txt).
|
||||
|
||||
## Alternative: pyvenv
|
||||
|
||||
If you're on Python 3.3+, you can use the built-in `pyvenv` instead of `virtualenv`. `pyvenv` is
|
||||
pretty much the same thing as `virtualenv`, except that it doesn't install setuptools and pip, so it
|
||||
has to be installed manually:
|
||||
|
||||
$ pyvenv --system-site-packages env
|
||||
$ source env/bin/activate
|
||||
$ wget https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py -O - | python
|
||||
$ easy_install pip
|
||||
|
||||
## Actual building and running
|
||||
|
||||
With your virtualenv activated, you can build and run dupeGuru with these commands:
|
||||
|
||||
$ python configure.py
|
||||
$ python build.py
|
||||
$ python run.py
|
||||
|
||||
You can also package dupeGuru into an installable package with:
|
||||
|
||||
$ python package.py
|
||||
|
||||
[documentation]: http://www.hardcoded.net/dupeguru/help/en/
|
||||
[python]: http://www.python.org/
|
||||
[setuptools]: https://pypi.python.org/pypi/setuptools
|
||||
[pyqt]: http://www.riverbankcomputing.com
|
||||
[cxfreeze]: http://cx-freeze.sourceforge.net/
|
||||
[advinst]: http://www.advancedinstaller.com
|
||||
117
README.rst
117
README.rst
@@ -1,117 +0,0 @@
|
||||
Contents
|
||||
========
|
||||
|
||||
This package contains the source for dupeGuru. To learn how to build it, refer to the
|
||||
"Build dupeGuru" section. Below is the description of the various subfolders:
|
||||
|
||||
- core: Contains the core logic code for dupeGuru. It's Python code.
|
||||
- core_*: Edition-specific-cross-toolkit code written in Python.
|
||||
- cocoa: UI code for the Cocoa toolkit. It's Objective-C code.
|
||||
- qt: UI code for the Qt toolkit. It's written in Python and uses PyQt.
|
||||
- images: Images used by the different UI codebases.
|
||||
- debian: Skeleton files required to create a .deb package
|
||||
- help: Help document, written for Sphinx.
|
||||
|
||||
There are also other sub-folder that comes from external repositories (automatically checked out
|
||||
as mercurial subrepos):
|
||||
|
||||
- hscommon: A collection of helpers used across HS applications.
|
||||
- cocoalib: A collection of helpers used across Cocoa UI codebases of HS applications.
|
||||
- qtlib: A collection of helpers used across Qt UI codebases of HS applications.
|
||||
|
||||
dupeGuru Dependencies
|
||||
=====================
|
||||
|
||||
Before being able to build dupeGuru, a few dependencies have to be installed. If you use pip, you
|
||||
will not have to install them all manually (see "The easy way!" below):
|
||||
|
||||
General dependencies
|
||||
--------------------
|
||||
|
||||
- Python 3.2 (http://www.python.org)
|
||||
- Send2Trash3k (http://hg.hardcoded.net/send2trash)
|
||||
- hsaudiotag3k 1.1.0 (for ME) (http://hg.hardcoded.net/hsaudiotag)
|
||||
- jobprogress 1.0.3 (http://hg.hardcoded.net/jobprogress)
|
||||
- Sphinx 1.1 (http://sphinx.pocoo.org/)
|
||||
- polib 0.7.0 (http://bitbucket.org/izi/polib)
|
||||
- pytest 2.0.0, to run unit tests. (http://pytest.org/)
|
||||
|
||||
OS X prerequisites
|
||||
------------------
|
||||
|
||||
- XCode's command line tools
|
||||
- objp 1.1.0 (http://bitbucket.org/hsoft/objp)
|
||||
- appscript 1.0.0 for ME and PE (http://appscript.sourceforge.net/)
|
||||
- xibless 0.4.0 (https://bitbucket.org/hsoft/xibless)
|
||||
|
||||
Windows prerequisites
|
||||
---------------------
|
||||
|
||||
- Visual Studio 2008 (Express is enough) is needed to build C extensions. (http://www.microsoft.com/Express/)
|
||||
- PyQt 4.7+ (http://www.riverbankcomputing.co.uk/news)
|
||||
- cx_Freeze, if you want to build a exe. You don't need it if you just want to run dupeGuru. (http://cx-freeze.sourceforge.net/)
|
||||
- Advanced Installer, if you want to build the installer file. (http://www.advancedinstaller.com/)
|
||||
|
||||
Linux prerequisites
|
||||
-------------------
|
||||
|
||||
- PyQt 4.7+ (http://www.riverbankcomputing.co.uk/news)
|
||||
|
||||
The easy way!
|
||||
-------------
|
||||
|
||||
There's an easy way to install the majority of the prerequisites above, and it's `pip <http://www.pip-installer.org/>`_ which has recently started to support Python 3. So install it and then run::
|
||||
|
||||
pip install -r requirements-[osx|win].txt
|
||||
|
||||
([osx|win] depends, of course, on your platform. On other platforms, just use requirements.txt).
|
||||
|
||||
Advanced Installer, having nothing to do with Python, needs to be installed manually.
|
||||
|
||||
PyQt isn't in the requirements file either (there's no package uploaded on PyPI) and you also have
|
||||
to install it manually.
|
||||
|
||||
If you use a virtualenv (which you should), you have to enable the "site-packages" option because
|
||||
dupeGuru will need the PyQt library which you'll have installed on your system.
|
||||
|
||||
Prerequisite gotchas
|
||||
--------------------
|
||||
|
||||
Correctly installing the prerequisites is tricky. Make sure you have at least the version number
|
||||
required for each prerequisite.
|
||||
|
||||
If you didn't use mercurial to download this source, you probably have an incomplete source folder!
|
||||
External projects (hscommon, qtlib, cocoalib) need to be at the root of the dupeGuru project folder.
|
||||
You'll have to download those separately. Or use mercurial, it's much easier.
|
||||
|
||||
Another one on OS X: I wouldn't use macports/fink/whatever. Whenever I tried using those, I always
|
||||
ended up with problems.
|
||||
|
||||
Whenever you have a problem, always double-check that you're running the correct python version.
|
||||
You'll probably have to tweak your $PATH.
|
||||
|
||||
To setup a build machine under Ubuntu 12.04 and up, install those packages: python3-dev, python3-pyqt4,
|
||||
pyqt4-dev-tools, mercurial and then python3-setuptools. Once you've done that, install pip with
|
||||
`easy_install`. Once you've done that, you can then perform "The easy way!" installation.
|
||||
|
||||
Building dupeGuru
|
||||
=================
|
||||
|
||||
First, make sure you meet the dependencies listed in the section above. Then you need to configure
|
||||
your build with::
|
||||
|
||||
python configure.py
|
||||
|
||||
If you want, you can specify a UI to use with the ``--ui`` option. So, if you want to build dupeGuru
|
||||
with Qt on OS X, then you have to type ``python configure.py --ui=qt``. You can also use the
|
||||
``--dev`` flag to indicate a dev build (mostly useful in OS X, where the python code in symlinked
|
||||
so you don't have to repackage whenever you make a change in the python code).
|
||||
|
||||
Then, just build the thing and then run it with::
|
||||
|
||||
python build.py
|
||||
python run.py
|
||||
|
||||
If you want to create ready-to-upload package, run::
|
||||
|
||||
python package.py
|
||||
26
bootstrap.sh
Executable file
26
bootstrap.sh
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
|
||||
command -v python3 -m venv >/dev/null 2>&1 || { echo >&2 "Python 3.3 required. Install it and try again. Aborting"; exit 1; }
|
||||
|
||||
if [ ! -d "env" ]; then
|
||||
echo "No virtualenv. Creating one"
|
||||
command -v curl >/dev/null 2>&1 || { echo >&2 "curl required. Install it and try again. Aborting"; exit 1; }
|
||||
python3 -m venv --system-site-packages env
|
||||
source env/bin/activate
|
||||
curl https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py | python
|
||||
easy_install pip
|
||||
else
|
||||
echo "There's already an env. Activating it"
|
||||
source env/bin/activate
|
||||
fi
|
||||
|
||||
echo "Installing pip requirements"
|
||||
if [ "$(uname)" == "Darwin" ]; then
|
||||
pip install -r requirements-osx.txt
|
||||
else
|
||||
python3 -c "import PyQt4" >/dev/null 2>&1 || { echo >&2 "PyQt 4.8+ required. Install it and try again. Aborting"; exit 1; }
|
||||
pip install -r requirements.txt
|
||||
fi
|
||||
|
||||
echo "Bootstrapping complete! You can now configure, build and run dupeGuru with:"
|
||||
echo ". env/bin/activate && python configure.py && python build.py && python run.py"
|
||||
24
build.py
24
build.py
@@ -45,6 +45,8 @@ def parse_args():
|
||||
help="Generate .pot files from source code.")
|
||||
parser.add_option('--mergepot', action='store_true', dest='mergepot',
|
||||
help="Update all .po files based on .pot files.")
|
||||
parser.add_option('--normpo', action='store_true', dest='normpo',
|
||||
help="Normalize all PO files (do this before commit).")
|
||||
(options, args) = parser.parse_args()
|
||||
return options
|
||||
|
||||
@@ -102,10 +104,10 @@ def build_cocoa(edition, dev):
|
||||
if not op.exists(pydep_folder):
|
||||
os.mkdir(pydep_folder)
|
||||
shutil.copy(op.join(cocoa_project_path, 'dg_cocoa.py'), 'build')
|
||||
appscript_pkgs = ['appscript', 'aem', 'mactypes']
|
||||
appscript_pkgs = ['appscript', 'aem', 'mactypes', 'osax']
|
||||
specific_packages = {
|
||||
'se': ['core_se'],
|
||||
'me': ['core_me'] + appscript_pkgs,
|
||||
'me': ['core_me'] + appscript_pkgs + ['hsaudiotag'],
|
||||
'pe': ['core_pe'] + appscript_pkgs,
|
||||
}[edition]
|
||||
tocopy = ['core', 'hscommon', 'cocoa/inter', 'cocoalib/cocoa', 'jobprogress', 'objp',
|
||||
@@ -158,7 +160,7 @@ def build_help(edition):
|
||||
help_basepath = op.join(current_path, 'help', 'en')
|
||||
help_destpath = op.join(current_path, 'build', 'help'.format(edition))
|
||||
changelog_path = op.join(current_path, 'help', 'changelog_{}'.format(edition))
|
||||
tixurl = "https://hardcoded.lighthouseapp.com/projects/31699-dupeguru/tickets/{0}"
|
||||
tixurl = "https://github.com/hsoft/dupeguru/issues/{}"
|
||||
appname = {'se': 'dupeGuru', 'me': 'dupeGuru Music Edition', 'pe': 'dupeGuru Picture Edition'}[edition]
|
||||
homepage = 'http://www.hardcoded.net/dupeguru{}/'.format('_' + edition if edition != 'se' else '')
|
||||
confrepl = {'edition': edition, 'appname': appname, 'homepage': homepage, 'language': 'en'}
|
||||
@@ -238,6 +240,12 @@ def build_mergepot():
|
||||
loc.merge_pots_into_pos(op.join('qtlib', 'locale'))
|
||||
loc.merge_pots_into_pos(op.join('cocoalib', 'locale'))
|
||||
|
||||
def build_normpo():
|
||||
loc.normalize_all_pos('locale')
|
||||
loc.normalize_all_pos(op.join('hscommon', 'locale'))
|
||||
loc.normalize_all_pos(op.join('qtlib', 'locale'))
|
||||
loc.normalize_all_pos(op.join('cocoalib', 'locale'))
|
||||
|
||||
def build_cocoa_proxy_module():
|
||||
print("Building Cocoa Proxy")
|
||||
import objp.p2o
|
||||
@@ -255,7 +263,8 @@ def build_cocoa_bridging_interfaces(edition):
|
||||
add_to_pythonpath('cocoa')
|
||||
add_to_pythonpath('cocoalib')
|
||||
from cocoa.inter import (PyGUIObject, GUIObjectView, PyColumns, ColumnsView, PyOutline,
|
||||
OutlineView, PySelectableList, SelectableListView, PyTable, TableView, PyBaseApp, PyFairware)
|
||||
OutlineView, PySelectableList, SelectableListView, PyTable, TableView, PyBaseApp,
|
||||
PyFairware, PyTextField, ProgressWindowView, PyProgressWindow)
|
||||
from inter.deletion_options import PyDeletionOptions, DeletionOptionsView
|
||||
from inter.details_panel import PyDetailsPanel, DetailsPanelView
|
||||
from inter.directory_outline import PyDirectoryOutline, DirectoryOutlineView
|
||||
@@ -270,12 +279,13 @@ def build_cocoa_bridging_interfaces(edition):
|
||||
allclasses = [PyGUIObject, PyColumns, PyOutline, PySelectableList, PyTable, PyBaseApp, PyFairware,
|
||||
PyDetailsPanel, PyDirectoryOutline, PyPrioritizeDialog, PyPrioritizeList, PyProblemDialog,
|
||||
PyIgnoreListDialog, PyDeletionOptions, PyResultTable, PyStatsLabel, PyDupeGuruBase,
|
||||
appmod.PyDupeGuru]
|
||||
PyTextField, PyProgressWindow, appmod.PyDupeGuru]
|
||||
for class_ in allclasses:
|
||||
objp.o2p.generate_objc_code(class_, 'cocoa/autogen', inherit=True)
|
||||
allclasses = [GUIObjectView, ColumnsView, OutlineView, SelectableListView, TableView,
|
||||
DetailsPanelView, DirectoryOutlineView, PrioritizeDialogView, PrioritizeListView,
|
||||
IgnoreListDialogView, DeletionOptionsView, ResultTableView, StatsLabelView, DupeGuruView]
|
||||
IgnoreListDialogView, DeletionOptionsView, ResultTableView, StatsLabelView,
|
||||
ProgressWindowView, DupeGuruView]
|
||||
clsspecs = [objp.o2p.spec_from_python_class(class_) for class_ in allclasses]
|
||||
objp.p2o.generate_python_proxy_code_from_clsspec(clsspecs, 'build/CocoaViews.m')
|
||||
build_cocoa_ext('CocoaViews', 'cocoa/inter', ['build/CocoaViews.m', 'build/ObjP.m'])
|
||||
@@ -337,6 +347,8 @@ def main():
|
||||
build_updatepot()
|
||||
elif options.mergepot:
|
||||
build_mergepot()
|
||||
elif options.normpo:
|
||||
build_normpo()
|
||||
elif options.cocoa_ext:
|
||||
build_cocoa_proxy_module()
|
||||
build_cocoa_bridging_interfaces(edition)
|
||||
|
||||
@@ -15,6 +15,7 @@ http://www.hardcoded.net/licenses/bsd_license
|
||||
#import "IgnoreListDialog.h"
|
||||
#import "HSFairwareAboutBox.h"
|
||||
#import "HSRecentFiles.h"
|
||||
#import "HSProgressWindow.h"
|
||||
|
||||
@interface AppDelegateBase : NSObject
|
||||
{
|
||||
@@ -27,6 +28,7 @@ http://www.hardcoded.net/licenses/bsd_license
|
||||
DirectoryPanel *_directoryPanel;
|
||||
DetailsPanel *_detailsPanel;
|
||||
IgnoreListDialog *_ignoreListDialog;
|
||||
HSProgressWindow *_progressWindow;
|
||||
NSWindowController *_preferencesPanel;
|
||||
HSFairwareAboutBox *_aboutBox;
|
||||
HSRecentFiles *_recentResults;
|
||||
|
||||
@@ -74,6 +74,8 @@ http://www.hardcoded.net/licenses/bsd_license
|
||||
_directoryPanel = [self createDirectoryPanel];
|
||||
_detailsPanel = [self createDetailsPanel];
|
||||
_ignoreListDialog = [[IgnoreListDialog alloc] initWithPyRef:[model ignoreListDialog]];
|
||||
_progressWindow = [[HSProgressWindow alloc] initWithPyRef:[[self model] progressWindow] view:nil];
|
||||
[_progressWindow setParentWindow:[_resultWindow window]];
|
||||
_aboutBox = nil; // Lazily loaded
|
||||
_preferencesPanel = nil; // Lazily loaded
|
||||
[[[self directoryPanel] window] makeKeyAndOrderFront:self];
|
||||
@@ -197,7 +199,6 @@ http://www.hardcoded.net/licenses/bsd_license
|
||||
/* Delegate */
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
||||
{
|
||||
[[ProgressController mainProgressController] setWorker:model];
|
||||
[model initialRegistrationSetup];
|
||||
[model loadSession];
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ http://www.hardcoded.net/licenses/bsd_license
|
||||
|
||||
@interface ResultTable : HSTable <QLPreviewPanelDataSource, QLPreviewPanelDelegate>
|
||||
{
|
||||
NSSet *_deltaColumns;
|
||||
}
|
||||
- (id)initWithPyRef:(PyObject *)aPyRef view:(NSTableView *)aTableView;
|
||||
- (PyResultTable *)model;
|
||||
|
||||
@@ -20,16 +20,9 @@ http://www.hardcoded.net/licenses/bsd_license
|
||||
- (id)initWithPyRef:(PyObject *)aPyRef view:(NSTableView *)aTableView
|
||||
{
|
||||
self = [super initWithPyRef:aPyRef wrapperClass:[PyResultTable class] callbackClassName:@"ResultTableView" view:aTableView];
|
||||
_deltaColumns = [[NSSet setWithArray:[[self model] deltaColumns]] retain];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[_deltaColumns release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (PyResultTable *)model
|
||||
{
|
||||
return (PyResultTable *)model;
|
||||
@@ -132,10 +125,8 @@ http://www.hardcoded.net/licenses/bsd_license
|
||||
color = [NSColor selectedTextColor];
|
||||
}
|
||||
else if (isMarkable) {
|
||||
if ([self deltaValuesMode]) {
|
||||
if ([_deltaColumns containsObject:[column identifier]]) {
|
||||
color = [NSColor orangeColor];
|
||||
}
|
||||
if ([[self model] isDeltaAtRow:row column:[column identifier]]) {
|
||||
color = [NSColor orangeColor];
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -42,9 +42,6 @@ http://www.hardcoded.net/licenses/bsd_license
|
||||
[matches setTarget:self];
|
||||
[matches setDoubleAction:@selector(openClicked)];
|
||||
[self adjustUIToLocalization];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobStarted:) name:JobStarted object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(jobInProgress:) name:JobInProgress object:nil];
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -340,22 +337,6 @@ http://www.hardcoded.net/licenses/bsd_license
|
||||
previewPanel = nil;
|
||||
}
|
||||
|
||||
- (void)jobInProgress:(NSNotification *)aNotification
|
||||
{
|
||||
[Dialogs showMessage:NSLocalizedString(@"A previous action is still hanging in there. You can't start a new one yet. Wait a few seconds, then try again.", @"")];
|
||||
}
|
||||
|
||||
- (void)jobStarted:(NSNotification *)aNotification
|
||||
{
|
||||
[[self window] makeKeyAndOrderFront:nil];
|
||||
NSDictionary *ui = [aNotification userInfo];
|
||||
NSString *desc = [ui valueForKey:@"desc"];
|
||||
[[ProgressController mainProgressController] setJobDesc:desc];
|
||||
NSString *jobid = [ui valueForKey:@"jobid"];
|
||||
[[ProgressController mainProgressController] setJobId:jobid];
|
||||
[[ProgressController mainProgressController] showSheetForParent:[self window]];
|
||||
}
|
||||
|
||||
- (BOOL)validateToolbarItem:(NSToolbarItem *)theItem
|
||||
{
|
||||
return ![[ProgressController mainProgressController] isShown];
|
||||
|
||||
@@ -5,11 +5,12 @@ result = Window(610, 400, "Re-Prioritize duplicates")
|
||||
promptLabel = Label(result, "Add criteria to the right box and click OK to send the dupes that "
|
||||
"correspond the best to these criteria to their respective group's reference position. Read "
|
||||
"the help file for more information.")
|
||||
categoryPopup = Popup(result)
|
||||
criteriaTable = ListView(result)
|
||||
prioritizationTable = ListView(result)
|
||||
addButton = Button(result, NLSTR("-->"))
|
||||
removeButton = Button(result, NLSTR("<--"))
|
||||
split = SplitView(result, 2, vertical=True)
|
||||
categoryPopup = Popup(split.subviews[0])
|
||||
criteriaTable = ListView(split.subviews[0])
|
||||
prioritizationTable = ListView(split.subviews[1])
|
||||
addButton = Button(split.subviews[1], NLSTR("-->"))
|
||||
removeButton = Button(split.subviews[1], NLSTR("<--"))
|
||||
okButton = Button(result, "Ok")
|
||||
cancelButton = Button(result, "Cancel")
|
||||
|
||||
@@ -27,24 +28,38 @@ cancelButton.action = Action(owner, 'cancel')
|
||||
okButton.keyEquivalent = '\\r'
|
||||
cancelButton.keyEquivalent = '\\e'
|
||||
|
||||
# For layouts to correctly work, subviews need to have the dimensions they'll approximately have
|
||||
# at runtime.
|
||||
split.subviews[0].width = 260
|
||||
split.subviews[0].height = 260
|
||||
split.subviews[1].width = 340
|
||||
split.subviews[1].height = 260
|
||||
promptLabel.height *= 3 # 3 lines
|
||||
|
||||
leftLayout = VLayout([categoryPopup, criteriaTable], width=262, filler=criteriaTable)
|
||||
leftLayout = VLayout([categoryPopup, criteriaTable], filler=criteriaTable)
|
||||
middleLayout = VLayout([addButton, removeButton], width=41)
|
||||
buttonLayout = HLayout([None, cancelButton, okButton])
|
||||
|
||||
#pack split subview 0
|
||||
leftLayout.fillAll()
|
||||
|
||||
#pack split subview 1
|
||||
prioritizationTable.fillAll()
|
||||
prioritizationTable.width -= 48
|
||||
prioritizationTable.moveTo(Pack.Right)
|
||||
middleLayout.moveNextTo(prioritizationTable, Pack.Left, align=Pack.Middle)
|
||||
|
||||
# Main layout
|
||||
promptLabel.packToCorner(Pack.UpperLeft)
|
||||
promptLabel.fill(Pack.Right)
|
||||
leftLayout.packRelativeTo(promptLabel, Pack.Below)
|
||||
middleLayout.packRelativeTo(leftLayout, Pack.Right, align=Pack.Above)
|
||||
prioritizationTable.packRelativeTo(middleLayout, Pack.Right, align=Pack.Above)
|
||||
buttonLayout.packRelativeTo(leftLayout, Pack.Below)
|
||||
split.moveNextTo(promptLabel, Pack.Below)
|
||||
buttonLayout.moveNextTo(split, Pack.Below)
|
||||
buttonLayout.fill(Pack.Right)
|
||||
leftLayout.fill(Pack.Below)
|
||||
middleLayout.packRelativeTo(leftLayout, Pack.Right, align=Pack.Middle)
|
||||
prioritizationTable.fill(Pack.Below, goal=leftLayout.y)
|
||||
prioritizationTable.fill(Pack.Right)
|
||||
split.fill(Pack.LowerRight)
|
||||
|
||||
promptLabel.setAnchor(Pack.UpperLeft, growX=True)
|
||||
prioritizationTable.setAnchor(Pack.UpperLeft, growX=True, growY=True)
|
||||
categoryPopup.setAnchor(Pack.UpperLeft, growX=True)
|
||||
criteriaTable.setAnchor(Pack.UpperLeft, growX=True, growY=True)
|
||||
split.setAnchor(Pack.UpperLeft, growX=True, growY=True)
|
||||
buttonLayout.setAnchor(Pack.Below)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from cocoa.inter import PyTextField, PyProgressWindow
|
||||
from .deletion_options import PyDeletionOptions
|
||||
from .details_panel import PyDetailsPanel
|
||||
from .directory_outline import PyDirectoryOutline
|
||||
|
||||
@@ -1,23 +1,8 @@
|
||||
import logging
|
||||
|
||||
from objp.util import pyref, dontwrap
|
||||
from jobprogress import job
|
||||
import cocoa
|
||||
from cocoa import install_exception_hook, install_cocoa_logger, proxy
|
||||
from cocoa import install_exception_hook, install_cocoa_logger, patch_threaded_job_performer, proxy
|
||||
from cocoa.inter import PyFairware, FairwareView
|
||||
from hscommon.trans import trget
|
||||
|
||||
from core.app import JobType
|
||||
|
||||
tr = trget('ui')
|
||||
|
||||
JOBID2TITLE = {
|
||||
JobType.Scan: tr("Scanning for duplicates"),
|
||||
JobType.Load: tr("Loading"),
|
||||
JobType.Move: tr("Moving"),
|
||||
JobType.Copy: tr("Copying"),
|
||||
JobType.Delete: tr("Sending to Trash"),
|
||||
}
|
||||
|
||||
class DupeGuruView(FairwareView):
|
||||
def askYesNoWithPrompt_(self, prompt: str) -> bool: pass
|
||||
@@ -26,16 +11,14 @@ class DupeGuruView(FairwareView):
|
||||
def selectDestFileWithPrompt_extension_(self, prompt: str, extension: str) -> str: pass
|
||||
|
||||
class PyDupeGuruBase(PyFairware):
|
||||
FOLLOW_PROTOCOLS = ['Worker']
|
||||
|
||||
@dontwrap
|
||||
def _init(self, modelclass):
|
||||
logging.basicConfig(level=logging.WARNING, format='%(levelname)s %(message)s')
|
||||
install_exception_hook()
|
||||
install_cocoa_logger()
|
||||
patch_threaded_job_performer()
|
||||
appdata = proxy.getAppdataPath()
|
||||
self.model = modelclass(self, appdata)
|
||||
self.progress = cocoa.ThreadedJobPerformer()
|
||||
|
||||
#---Sub-proxies
|
||||
def detailsPanel(self) -> pyref:
|
||||
@@ -56,6 +39,9 @@ class PyDupeGuruBase(PyFairware):
|
||||
def ignoreListDialog(self) -> pyref:
|
||||
return self.model.ignore_list_dialog
|
||||
|
||||
def progressWindow(self) -> pyref:
|
||||
return self.model.progress_window
|
||||
|
||||
def deletionOptions(self) -> pyref:
|
||||
return self.model.deletion_options
|
||||
|
||||
@@ -157,31 +143,6 @@ class PyDupeGuruBase(PyFairware):
|
||||
def setCopyMoveDestType_(self, copymove_dest_type: int):
|
||||
self.model.options['copymove_dest_type'] = copymove_dest_type
|
||||
|
||||
#---Worker
|
||||
def getJobProgress(self) -> object: # NSNumber
|
||||
try:
|
||||
return self.progress.last_progress
|
||||
except AttributeError:
|
||||
# I have *no idea* why this can possible happen (last_progress is always set by
|
||||
# create_job() *before* any threaded job notification, which shows the progress panel,
|
||||
# is sent), but it happens anyway, so there we go. ref: #106
|
||||
return -1
|
||||
|
||||
def getJobDesc(self) -> str:
|
||||
try:
|
||||
return self.progress.last_desc
|
||||
except AttributeError:
|
||||
# see getJobProgress
|
||||
return ''
|
||||
|
||||
def cancelJob(self):
|
||||
self.progress.job_cancelled = True
|
||||
|
||||
def jobCompleted_(self, jobid: str):
|
||||
result = self.model._job_completed(jobid, self.progress.last_error)
|
||||
if not result:
|
||||
self.progress.reraise_if_error()
|
||||
|
||||
#--- model --> view
|
||||
@dontwrap
|
||||
def open_path(self, path):
|
||||
@@ -191,18 +152,6 @@ class PyDupeGuruBase(PyFairware):
|
||||
def reveal_path(self, path):
|
||||
proxy.revealPath_(str(path))
|
||||
|
||||
@dontwrap
|
||||
def start_job(self, jobid, func, args=()):
|
||||
try:
|
||||
j = self.progress.create_job()
|
||||
args = tuple([j] + list(args))
|
||||
self.progress.run_threaded(func, args=args)
|
||||
except job.JobInProgressError:
|
||||
proxy.postNotification_userInfo_('JobInProgress', None)
|
||||
else:
|
||||
ud = {'desc': JOBID2TITLE[jobid], 'jobid':jobid}
|
||||
proxy.postNotification_userInfo_('JobStarted', ud)
|
||||
|
||||
@dontwrap
|
||||
def ask_yes_no(self, prompt):
|
||||
return self.callback.askYesNoWithPrompt_(prompt)
|
||||
|
||||
@@ -19,11 +19,11 @@ from hscommon.path import Path
|
||||
from hscommon.util import remove_invalid_xml
|
||||
|
||||
from core import directories
|
||||
from core.app import JobType
|
||||
from core.app import JobType, JOBID2TITLE
|
||||
from core.scanner import ScanType
|
||||
from core_me.app import DupeGuru as DupeGuruBase
|
||||
from core_me import fs
|
||||
from .app import JOBID2TITLE, PyDupeGuruBase
|
||||
from .app import PyDupeGuruBase
|
||||
|
||||
tr = trget('ui')
|
||||
|
||||
@@ -184,11 +184,14 @@ class DupeGuruME(DupeGuruBase):
|
||||
pass # We'll return the default file type, as per the last line of this method
|
||||
return DupeGuruBase._create_file(self, path)
|
||||
|
||||
def _job_completed(self, jobid, exc):
|
||||
if (jobid in {JobType.RemoveDeadTracks, JobType.ScanDeadTracks}) and (exc is not None):
|
||||
msg = tr("There were communication problems with iTunes. The operation couldn't be completed.")
|
||||
self.view.show_message(msg)
|
||||
return True
|
||||
def _job_completed(self, jobid):
|
||||
# XXX Just before release, I'm realizing that this piece of code below is why I was passing
|
||||
# job exception as an argument to _job_completed(). I have to comment it for now. It's not
|
||||
# the end of the world, but I should find an elegant solution to this at some point.
|
||||
# if (jobid in {JobType.RemoveDeadTracks, JobType.ScanDeadTracks}) and (exc is not None):
|
||||
# msg = tr("There were communication problems with iTunes. The operation couldn't be completed.")
|
||||
# self.view.show_message(msg)
|
||||
# return True
|
||||
if jobid == JobType.ScanDeadTracks:
|
||||
dead_tracks_count = len(self.dead_tracks)
|
||||
if dead_tracks_count > 0:
|
||||
@@ -202,7 +205,7 @@ class DupeGuruME(DupeGuruBase):
|
||||
if hasattr(self, 'itunes_songs'):
|
||||
# If we load another file, we want a refresh song list
|
||||
del self.itunes_songs
|
||||
DupeGuruBase._job_completed(self, jobid, exc)
|
||||
DupeGuruBase._job_completed(self, jobid)
|
||||
|
||||
def copy_or_move(self, dupe, copy, destination, dest_type):
|
||||
if isinstance(dupe, ITunesSong):
|
||||
@@ -230,7 +233,7 @@ class DupeGuruME(DupeGuruBase):
|
||||
except CommandError as e:
|
||||
logging.warning('Error while trying to remove a track from iTunes: %s' % str(e))
|
||||
|
||||
self.view.start_job(JobType.RemoveDeadTracks, do)
|
||||
self._start_job(JobType.RemoveDeadTracks, do)
|
||||
|
||||
def scan_dead_tracks(self):
|
||||
def do(j):
|
||||
@@ -248,7 +251,7 @@ class DupeGuruME(DupeGuruBase):
|
||||
self.dead_tracks.append(track)
|
||||
logging.info('Found %d dead tracks' % len(self.dead_tracks))
|
||||
|
||||
self.view.start_job(JobType.ScanDeadTracks, do)
|
||||
self._start_job(JobType.ScanDeadTracks, do)
|
||||
|
||||
class PyDupeGuru(PyDupeGuruBase):
|
||||
def __init__(self):
|
||||
|
||||
@@ -259,8 +259,8 @@ class DupeGuruPE(DupeGuruBase):
|
||||
return self.path2aperture.get(path)
|
||||
return DupeGuruBase._create_file(self, path)
|
||||
|
||||
def _job_completed(self, jobid, exc):
|
||||
DupeGuruBase._job_completed(self, jobid, exc)
|
||||
def _job_completed(self, jobid):
|
||||
DupeGuruBase._job_completed(self, jobid)
|
||||
if jobid == JobType.Load:
|
||||
if hasattr(self, 'path2iphoto'):
|
||||
del self.path2iphoto
|
||||
|
||||
@@ -14,9 +14,9 @@ from hscommon.path import Path
|
||||
from cocoa import proxy
|
||||
|
||||
from core.scanner import ScanType
|
||||
from core import fs
|
||||
from core.directories import Directories as DirectoriesBase, DirectoryState
|
||||
from core_se.app import DupeGuru as DupeGuruBase
|
||||
from core_se import fs
|
||||
from .app import PyDupeGuruBase
|
||||
|
||||
def is_bundle(str_path):
|
||||
@@ -36,6 +36,7 @@ class Directories(DirectoriesBase):
|
||||
HOME_PATH_TO_EXCLUDE = [Path('Library')]
|
||||
def __init__(self):
|
||||
DirectoriesBase.__init__(self, fileclasses=[Bundle, fs.File])
|
||||
self.folderclass = fs.Folder
|
||||
|
||||
def _default_state_for_path(self, path):
|
||||
result = DirectoriesBase._default_state_for_path(self, path)
|
||||
|
||||
@@ -17,12 +17,13 @@ class PyResultTable(PyTable):
|
||||
def setDeltaValuesMode_(self, value: bool):
|
||||
self.model.delta_values = value
|
||||
|
||||
def deltaColumns(self) -> list:
|
||||
return list(self.model.DELTA_COLUMNS)
|
||||
|
||||
def valueForRow_column_(self, row_index: int, column: str) -> object:
|
||||
return self.model.get_row_value(row_index, column)
|
||||
|
||||
def isDeltaAtRow_column_(self, row_index: int, column: str) -> bool:
|
||||
row = self.model[row_index]
|
||||
return row.is_cell_delta(column)
|
||||
|
||||
def renameSelected_(self, newname: str) -> bool:
|
||||
return self.model.rename_selected(newname)
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ def configure(conf):
|
||||
conf.check_python_version((3,2,0))
|
||||
conf.check_python_headers()
|
||||
conf.env.FRAMEWORK_COCOA = 'Cocoa'
|
||||
conf.env.ARCH_COCOA = ['i386', 'x86_64']
|
||||
conf.env.ARCH_COCOA = ['x86_64']
|
||||
# Add cocoalib dir to the framework search path so we can find Sparkle.
|
||||
conf.env.CFLAGS = ['-F'+op.abspath('../cocoalib')]
|
||||
conf.env.LINKFLAGS = ['-F'+op.abspath('../cocoalib')]
|
||||
@@ -50,7 +50,8 @@ def build(ctx):
|
||||
'views/HSTableView', 'views/HSOutlineView', 'views/NSIndexPathAdditions',
|
||||
'views/NSTableViewAdditions',
|
||||
'controllers/HSColumns', 'controllers/HSGUIController', 'controllers/HSTable',
|
||||
'controllers/HSOutline', 'controllers/HSPopUpList', 'controllers/HSSelectableList']
|
||||
'controllers/HSOutline', 'controllers/HSPopUpList', 'controllers/HSSelectableList',
|
||||
'controllers/HSTextField', 'controllers/HSProgressWindow']
|
||||
cocoalib_src = [cocoalib_node.find_node(usename + '.m') for usename in cocoalib_uses] + cocoalib_node.ant_glob('autogen/*.m')
|
||||
project_folders = ['autogen', 'base', ctx.env.DGEDITION]
|
||||
project_src = sum([ctx.srcnode.ant_glob('%s/*.m' % folder) for folder in project_folders], [])
|
||||
|
||||
15
cocoalib/.gitignore
vendored
Normal file
15
cocoalib/.gitignore
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
.DS_Store
|
||||
__pycache__
|
||||
autogen
|
||||
*.pyc
|
||||
*.so
|
||||
nl.lproj
|
||||
cs.lproj
|
||||
de.lproj
|
||||
fr.lproj
|
||||
it.lproj
|
||||
hy.lproj
|
||||
ru.lproj
|
||||
uk.lproj
|
||||
zh_CN.lproj
|
||||
pt_BR.lproj
|
||||
8
cocoalib/.tx/config
Normal file
8
cocoalib/.tx/config
Normal file
@@ -0,0 +1,8 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
|
||||
[hscommon.cocoalib]
|
||||
file_filter = locale/<lang>/LC_MESSAGES/cocoalib.po
|
||||
source_file = locale/cocoalib.pot
|
||||
source_lang = en
|
||||
type = PO
|
||||
14
cocoalib/Dialogs.h
Normal file
14
cocoalib/Dialogs.h
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface Dialogs : NSObject
|
||||
+ (void)showMessage:(NSString *)message;
|
||||
+ (NSInteger)askYesNo:(NSString *)message;
|
||||
@end
|
||||
31
cocoalib/Dialogs.m
Normal file
31
cocoalib/Dialogs.m
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import "Dialogs.h"
|
||||
|
||||
@implementation Dialogs
|
||||
+ (void)showMessage:(NSString *)message
|
||||
{
|
||||
NSAlert *a = [[NSAlert alloc] init];
|
||||
[a addButtonWithTitle:NSLocalizedStringFromTable(@"OK", @"cocoalib", @"")];
|
||||
[a setMessageText:message];
|
||||
[a runModal];
|
||||
[a release];
|
||||
}
|
||||
|
||||
+ (NSInteger)askYesNo:(NSString *)message
|
||||
{
|
||||
NSAlert *a = [[NSAlert alloc] init];
|
||||
[a addButtonWithTitle:NSLocalizedStringFromTable(@"Yes", @"cocoalib", @"")];
|
||||
[[a addButtonWithTitle:NSLocalizedStringFromTable(@"No", @"cocoalib", @"")] setKeyEquivalent:@"\E"];
|
||||
[a setMessageText:message];
|
||||
NSInteger r = [a runModal];
|
||||
[a release];
|
||||
return r;
|
||||
}
|
||||
@end
|
||||
29
cocoalib/HSAboutBox.h
Normal file
29
cocoalib/HSAboutBox.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "PyBaseApp.h"
|
||||
|
||||
@interface HSAboutBox : NSWindowController
|
||||
{
|
||||
NSTextField *titleTextField;
|
||||
NSTextField *versionTextField;
|
||||
NSTextField *copyrightTextField;
|
||||
NSTextField *registeredTextField;
|
||||
NSButton *registerButton;
|
||||
|
||||
PyBaseApp *app;
|
||||
}
|
||||
|
||||
@property (readwrite, retain) NSTextField *titleTextField;
|
||||
@property (readwrite, retain) NSTextField *versionTextField;
|
||||
@property (readwrite, retain) NSTextField *copyrightTextField;
|
||||
|
||||
- (id)initWithApp:(PyBaseApp *)app;
|
||||
- (void)updateFields;
|
||||
@end
|
||||
42
cocoalib/HSAboutBox.m
Normal file
42
cocoalib/HSAboutBox.m
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import "HSAboutBox.h"
|
||||
#import "HSAboutBox_UI.h"
|
||||
|
||||
@implementation HSAboutBox
|
||||
|
||||
@synthesize titleTextField;
|
||||
@synthesize versionTextField;
|
||||
@synthesize copyrightTextField;
|
||||
|
||||
- (id)initWithApp:(PyBaseApp *)aApp
|
||||
{
|
||||
self = [super initWithWindow:nil];
|
||||
[self setWindow:createHSAboutBox_UI(self)];
|
||||
app = [aApp retain];
|
||||
[self updateFields];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[app release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)updateFields
|
||||
{
|
||||
[titleTextField setStringValue:[app appLongName]];
|
||||
NSString *version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
|
||||
[versionTextField setStringValue:[NSString stringWithFormat:@"Version: %@",version]];
|
||||
NSString *copyright = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSHumanReadableCopyright"];
|
||||
[copyrightTextField setStringValue:copyright];
|
||||
}
|
||||
|
||||
@end
|
||||
23
cocoalib/HSErrorReportWindow.h
Normal file
23
cocoalib/HSErrorReportWindow.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface HSErrorReportWindow : NSWindowController
|
||||
{
|
||||
NSTextView *contentTextView;
|
||||
}
|
||||
|
||||
@property (readwrite, retain) NSTextView *contentTextView;
|
||||
|
||||
+ (void)showErrorReportWithContent:(NSString *)content;
|
||||
- (id)initWithContent:(NSString *)content;
|
||||
|
||||
- (void)send;
|
||||
- (void)dontSend;
|
||||
@end
|
||||
48
cocoalib/HSErrorReportWindow.m
Normal file
48
cocoalib/HSErrorReportWindow.m
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import "HSErrorReportWindow.h"
|
||||
#import "HSErrorReportWindow_UI.h"
|
||||
|
||||
@implementation HSErrorReportWindow
|
||||
|
||||
@synthesize contentTextView;
|
||||
|
||||
+ (void)showErrorReportWithContent:(NSString *)content
|
||||
{
|
||||
HSErrorReportWindow *report = [[HSErrorReportWindow alloc] initWithContent:content];
|
||||
[NSApp runModalForWindow:[report window]];
|
||||
[report release];
|
||||
}
|
||||
|
||||
- (id)initWithContent:(NSString *)content
|
||||
{
|
||||
self = [super initWithWindow:nil];
|
||||
[self setWindow:createHSErrorReportWindow_UI(self)];
|
||||
[contentTextView alignLeft:nil];
|
||||
[[[contentTextView textStorage] mutableString] setString:content];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)send
|
||||
{
|
||||
NSString *text = [[contentTextView textStorage] string];
|
||||
NSString *URL = [NSString stringWithFormat:@"mailto:support@hardcoded.net?SUBJECT=Error Report&BODY=%@",text];
|
||||
NSString *encodedURL = [URL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:encodedURL]];
|
||||
|
||||
[[self window] orderOut:self];
|
||||
[NSApp stopModalWithCode:NSOKButton];
|
||||
}
|
||||
|
||||
- (void)dontSend
|
||||
{
|
||||
[[self window] orderOut:self];
|
||||
[NSApp stopModalWithCode:NSCancelButton];
|
||||
}
|
||||
@end
|
||||
19
cocoalib/HSFairware.h
Normal file
19
cocoalib/HSFairware.h
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "HSFairwareProtocol.h"
|
||||
|
||||
@interface HSFairware : NSObject <HSFairwareProtocol>
|
||||
{
|
||||
NSInteger appId;
|
||||
NSString *name;
|
||||
BOOL registered;
|
||||
}
|
||||
- (id)initWithAppId:(NSInteger)aAppId name:(NSString *)aName;
|
||||
@end
|
||||
150
cocoalib/HSFairware.m
Normal file
150
cocoalib/HSFairware.m
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import "HSFairware.h"
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
#import "HSFairwareReminder.h"
|
||||
#import "Dialogs.h"
|
||||
#import "Utils.h"
|
||||
|
||||
NSString* md5str(NSString *source)
|
||||
{
|
||||
const char *cSource = [source UTF8String];
|
||||
unsigned char result[16];
|
||||
CC_MD5(cSource, strlen(cSource), result);
|
||||
return fmt(@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7],
|
||||
result[8], result[9], result[10], result[11], result[12], result[13], result[14], result[15]
|
||||
);
|
||||
}
|
||||
|
||||
BOOL validateCode(NSString *code, NSString *email, NSInteger appId)
|
||||
{
|
||||
if ([code length] != 32) {
|
||||
return NO;
|
||||
}
|
||||
NSInteger i;
|
||||
for (i=0; i<=100; i++) {
|
||||
NSString *blob = fmt(@"%i%@%iaybabtu", appId, email, i);
|
||||
if ([md5str(blob) isEqualTo:code]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSString* normalizeString(NSString *str)
|
||||
{
|
||||
return [[str stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] lowercaseString];
|
||||
}
|
||||
|
||||
@implementation HSFairware
|
||||
- (id)initWithAppId:(NSInteger)aAppId name:(NSString *)aName;
|
||||
{
|
||||
self = [super init];
|
||||
appId = aAppId;
|
||||
name = [aName retain];
|
||||
registered = NO;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[name release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
/* Private */
|
||||
- (void)setRegistrationCode:(NSString *)aCode email:(NSString *)aEmail
|
||||
{
|
||||
registered = validateCode(aCode, aEmail, appId);
|
||||
}
|
||||
|
||||
/* Public */
|
||||
- (void)initialRegistrationSetup
|
||||
{
|
||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||
NSString *code = [ud stringForKey:@"RegistrationCode"];
|
||||
NSString *email = [ud stringForKey:@"RegistrationEmail"];
|
||||
if (code && email) {
|
||||
[self setRegistrationCode:code email:email];
|
||||
}
|
||||
if (!registered) {
|
||||
BOOL fairwareMode = [ud boolForKey:@"FairwareMode"];
|
||||
if (!fairwareMode) {
|
||||
NSString *prompt = @"%@ is fairware, which means \"open source software developed "
|
||||
"with expectation of fair contributions from users\". It's a very interesting "
|
||||
"concept, but one year of fairware has shown that most people just want to know "
|
||||
"how much it costs and not be bothered with theories about intellectual property."
|
||||
"\n\n"
|
||||
"So I won't bother you and will be very straightforward: You can try %@ for "
|
||||
"free but you have to buy it in order to use it without limitations. In demo mode, "
|
||||
"%@ will show this dialog on startup."
|
||||
"\n\n"
|
||||
"So it's as simple as this. If you're curious about fairware, however, I encourage "
|
||||
"you to read more about it by clicking on the \"Fairware?\" button.";
|
||||
[HSFairwareReminder showDemoNagWithApp:self prompt:fmt(prompt, name, name, name)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)appName
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
- (NSString *)appLongName
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
- (BOOL)isRegistered
|
||||
{
|
||||
return registered;
|
||||
}
|
||||
|
||||
- (BOOL)setRegisteredCode:(NSString *)code andEmail:(NSString *)email
|
||||
{
|
||||
code = normalizeString(code);
|
||||
email = normalizeString(email);
|
||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||
if (([code isEqualTo:@"fairware"]) || ([email isEqualTo:@"fairware"])) {
|
||||
[ud setBool:YES forKey:@"FairwareMode"];
|
||||
[Dialogs showMessage:@"Fairware mode enabled."];
|
||||
return YES;
|
||||
}
|
||||
[self setRegistrationCode:code email:email];
|
||||
if (registered) {
|
||||
[ud setObject:code forKey:@"RegistrationCode"];
|
||||
[ud setObject:email forKey:@"RegistrationEmail"];
|
||||
[Dialogs showMessage:@"Your code is valid, thanks!"];
|
||||
return YES;
|
||||
}
|
||||
else {
|
||||
[Dialogs showMessage:@"Your code is invalid. Make sure that you wrote the good code. Also "
|
||||
"make sure that the e-mail you gave is the same as the e-mail you used for your purchase."];
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)contribute
|
||||
{
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://open.hardcoded.net/contribute/"]];
|
||||
}
|
||||
|
||||
- (void)buy
|
||||
{
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.hardcoded.net/purchase.htm"]];
|
||||
}
|
||||
|
||||
- (void)aboutFairware
|
||||
{
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://open.hardcoded.net/about/"]];
|
||||
}
|
||||
|
||||
@end
|
||||
33
cocoalib/HSFairwareAboutBox.h
Normal file
33
cocoalib/HSFairwareAboutBox.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "PyFairware.h"
|
||||
|
||||
@interface HSFairwareAboutBox : NSWindowController
|
||||
{
|
||||
NSTextField *titleTextField;
|
||||
NSTextField *versionTextField;
|
||||
NSTextField *copyrightTextField;
|
||||
NSTextField *registeredTextField;
|
||||
NSButton *registerButton;
|
||||
|
||||
PyFairware *app;
|
||||
}
|
||||
|
||||
@property (readwrite, retain) NSTextField *titleTextField;
|
||||
@property (readwrite, retain) NSTextField *versionTextField;
|
||||
@property (readwrite, retain) NSTextField *copyrightTextField;
|
||||
@property (readwrite, retain) NSTextField *registeredTextField;
|
||||
@property (readwrite, retain) NSButton *registerButton;
|
||||
|
||||
- (id)initWithApp:(PyFairware *)app;
|
||||
- (void)updateFields;
|
||||
|
||||
- (void)showRegisterDialog;
|
||||
@end
|
||||
60
cocoalib/HSFairwareAboutBox.m
Normal file
60
cocoalib/HSFairwareAboutBox.m
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import "HSFairwareAboutBox.h"
|
||||
#import "HSFairwareAboutBox_UI.h"
|
||||
#import "HSFairwareReminder.h"
|
||||
|
||||
@implementation HSFairwareAboutBox
|
||||
|
||||
@synthesize titleTextField;
|
||||
@synthesize versionTextField;
|
||||
@synthesize copyrightTextField;
|
||||
@synthesize registeredTextField;
|
||||
@synthesize registerButton;
|
||||
|
||||
- (id)initWithApp:(PyFairware *)aApp
|
||||
{
|
||||
self = [super initWithWindow:nil];
|
||||
[self setWindow:createHSFairwareAboutBox_UI(self)];
|
||||
app = [aApp retain];
|
||||
[self updateFields];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[app release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)updateFields
|
||||
{
|
||||
[titleTextField setStringValue:[app appLongName]];
|
||||
NSString *version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
|
||||
[versionTextField setStringValue:[NSString stringWithFormat:@"Version: %@",version]];
|
||||
NSString *copyright = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSHumanReadableCopyright"];
|
||||
[copyrightTextField setStringValue:copyright];
|
||||
if ([app isRegistered]) {
|
||||
[registeredTextField setHidden:NO];
|
||||
[registerButton setHidden:YES];
|
||||
}
|
||||
else {
|
||||
[registeredTextField setHidden:YES];
|
||||
[registerButton setHidden:NO];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)showRegisterDialog
|
||||
{
|
||||
HSFairwareReminder *fr = [[HSFairwareReminder alloc] initWithApp:app];
|
||||
[fr enterCode];
|
||||
[fr release];
|
||||
[self updateFields];
|
||||
}
|
||||
@end
|
||||
20
cocoalib/HSFairwareProtocol.h
Normal file
20
cocoalib/HSFairwareProtocol.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@protocol HSFairwareProtocol
|
||||
- (void)initialRegistrationSetup;
|
||||
- (NSString *)appName;
|
||||
- (NSString *)appLongName;
|
||||
- (BOOL)isRegistered;
|
||||
- (BOOL)setRegisteredCode:(NSString *)code andEmail:(NSString *)email;
|
||||
- (void)contribute;
|
||||
- (void)buy;
|
||||
- (void)aboutFairware;
|
||||
@end
|
||||
46
cocoalib/HSFairwareReminder.h
Normal file
46
cocoalib/HSFairwareReminder.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "HSFairwareProtocol.h"
|
||||
|
||||
@interface HSFairwareReminder : NSObject
|
||||
{
|
||||
NSWindow *codePanel;
|
||||
NSTextField *codePromptTextField;
|
||||
NSTextField *codeTextField;
|
||||
NSTextField *emailTextField;
|
||||
NSWindow *demoNagPanel;
|
||||
NSTextField *demoPromptTextField;
|
||||
|
||||
id <HSFairwareProtocol> app;
|
||||
}
|
||||
|
||||
@property (readwrite, retain) NSWindow *codePanel;
|
||||
@property (readwrite, retain) NSTextField *codePromptTextField;
|
||||
@property (readwrite, retain) NSTextField *codeTextField;
|
||||
@property (readwrite, retain) NSTextField *emailTextField;
|
||||
@property (readwrite, retain) NSWindow *demoNagPanel;
|
||||
@property (readwrite, retain) NSTextField *demoPromptTextField;
|
||||
|
||||
//Show nag only if needed
|
||||
+ (BOOL)showDemoNagWithApp:(id <HSFairwareProtocol>)app prompt:(NSString *)prompt;
|
||||
- (id)initWithApp:(id <HSFairwareProtocol>)app;
|
||||
|
||||
- (void)contribute;
|
||||
- (void)buy;
|
||||
- (void)moreInfo;
|
||||
- (void)cancelCode;
|
||||
- (void)showEnterCode;
|
||||
- (void)submitCode;
|
||||
- (void)closeDialog;
|
||||
|
||||
- (BOOL)showNagPanel:(NSWindow *)panel; //YES: The code has been sucessfully submitted NO: The use wan't to try the demo.
|
||||
- (BOOL)showDemoNagPanelWithPrompt:(NSString *)prompt;
|
||||
- (NSInteger)enterCode; //returns the modal code.
|
||||
@end
|
||||
115
cocoalib/HSFairwareReminder.m
Normal file
115
cocoalib/HSFairwareReminder.m
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import "HSFairwareReminder.h"
|
||||
#import "HSDemoReminder_UI.h"
|
||||
#import "HSEnterCode_UI.h"
|
||||
#import "Dialogs.h"
|
||||
#import "Utils.h"
|
||||
|
||||
@implementation HSFairwareReminder
|
||||
|
||||
@synthesize codePanel;
|
||||
@synthesize codePromptTextField;
|
||||
@synthesize codeTextField;
|
||||
@synthesize emailTextField;
|
||||
@synthesize demoNagPanel;
|
||||
@synthesize demoPromptTextField;
|
||||
|
||||
+ (BOOL)showDemoNagWithApp:(id <HSFairwareProtocol>)app prompt:(NSString *)prompt
|
||||
{
|
||||
HSFairwareReminder *fr = [[HSFairwareReminder alloc] initWithApp:app];
|
||||
BOOL r = [fr showDemoNagPanelWithPrompt:prompt];
|
||||
[fr release];
|
||||
return r;
|
||||
}
|
||||
|
||||
- (id)initWithApp:(id <HSFairwareProtocol>)aApp
|
||||
{
|
||||
self = [super init];
|
||||
app = aApp;
|
||||
[self setDemoNagPanel:createHSDemoReminder_UI(self)];
|
||||
[self setCodePanel:createHSEnterCode_UI(self)];
|
||||
[codePanel update];
|
||||
[codePromptTextField setStringValue:fmt([codePromptTextField stringValue],[app appName])];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)contribute
|
||||
{
|
||||
[app contribute];
|
||||
}
|
||||
|
||||
- (void)buy
|
||||
{
|
||||
[app buy];
|
||||
}
|
||||
|
||||
- (void)moreInfo
|
||||
{
|
||||
[app aboutFairware];
|
||||
}
|
||||
|
||||
- (void)cancelCode
|
||||
{
|
||||
[codePanel close];
|
||||
[NSApp stopModalWithCode:NSCancelButton];
|
||||
}
|
||||
|
||||
- (void)showEnterCode
|
||||
{
|
||||
[demoNagPanel close];
|
||||
[NSApp stopModalWithCode:NSOKButton];
|
||||
}
|
||||
|
||||
- (void)submitCode
|
||||
{
|
||||
NSString *code = [codeTextField stringValue];
|
||||
NSString *email = [emailTextField stringValue];
|
||||
if ([app setRegisteredCode:code andEmail:email]) {
|
||||
[codePanel close];
|
||||
[NSApp stopModalWithCode:NSOKButton];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)closeDialog
|
||||
{
|
||||
[demoNagPanel close];
|
||||
[NSApp stopModalWithCode:NSCancelButton];
|
||||
}
|
||||
|
||||
- (BOOL)showNagPanel:(NSWindow *)panel;
|
||||
{
|
||||
NSInteger r;
|
||||
while (YES) {
|
||||
r = [NSApp runModalForWindow:panel];
|
||||
if (r == NSOKButton) {
|
||||
r = [self enterCode];
|
||||
if (r == NSOKButton) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)showDemoNagPanelWithPrompt:(NSString *)prompt
|
||||
{
|
||||
[demoNagPanel setTitle:fmt([demoNagPanel title],[app appName])];
|
||||
[demoPromptTextField setStringValue:prompt];
|
||||
return [self showNagPanel:demoNagPanel];
|
||||
}
|
||||
|
||||
- (NSInteger)enterCode
|
||||
{
|
||||
return [NSApp runModalForWindow:codePanel];
|
||||
}
|
||||
|
||||
@end
|
||||
15
cocoalib/HSGeometry.h
Normal file
15
cocoalib/HSGeometry.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <math.h>
|
||||
|
||||
CGFloat deg2rad(CGFloat deg);
|
||||
CGFloat distance(NSPoint p1, NSPoint p2);
|
||||
NSPoint pointInCircle(NSPoint center, CGFloat radius, CGFloat angle);
|
||||
CGFloat angleFromPoints(NSPoint pt1, NSPoint pt2);
|
||||
71
cocoalib/HSGeometry.m
Normal file
71
cocoalib/HSGeometry.m
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import "HSGeometry.h"
|
||||
|
||||
CGFloat deg2rad(CGFloat deg)
|
||||
{
|
||||
return deg * M_PI / 180;
|
||||
}
|
||||
|
||||
CGFloat distance(NSPoint p1, NSPoint p2)
|
||||
{
|
||||
CGFloat dX = p1.x - p2.x;
|
||||
CGFloat dY = p1.y - p2.y;
|
||||
return sqrt(dX * dX + dY * dY);
|
||||
}
|
||||
|
||||
NSPoint pointInCircle(NSPoint center, CGFloat radius, CGFloat angle)
|
||||
{
|
||||
// a/sin(A) = b/sin(B) = c/sin(C) = 2R
|
||||
// the start point it (center.x + radius, center.y) and goes counterclockwise
|
||||
angle = fmod(angle, M_PI*2);
|
||||
CGFloat C = M_PI/2;
|
||||
CGFloat A = fmod(angle, M_PI/2);
|
||||
CGFloat B = C - A;
|
||||
CGFloat c = radius;
|
||||
CGFloat ratio = c / sin(C);
|
||||
CGFloat b = ratio * sin(B);
|
||||
CGFloat a = ratio * sin(A);
|
||||
if (angle >= M_PI * 1.5)
|
||||
return NSMakePoint(center.x + a, center.y - b);
|
||||
else if (angle >= M_PI)
|
||||
return NSMakePoint(center.x - b, center.y - a);
|
||||
else if (angle >= M_PI/2)
|
||||
return NSMakePoint(center.x - a, center.y + b);
|
||||
else
|
||||
return NSMakePoint(center.x + b, center.y + a);
|
||||
}
|
||||
|
||||
CGFloat angleFromPoints(NSPoint pt1, NSPoint pt2)
|
||||
{
|
||||
// Returns the angle (radian) formed by the line pt1-pt2. The angle follows the same logic
|
||||
// as in pointInCircle.
|
||||
// What we do here is that we take the line and reduce it to fit a "unit circle" (circle with
|
||||
// a radius of 1). Then, either asin(adjusted_dy) or acos(adjusted_dx) will give us our angle.
|
||||
// We'll use asin(adjusted_dy).
|
||||
CGFloat length = distance(pt1, pt2);
|
||||
CGFloat dx = pt2.x - pt1.x;
|
||||
CGFloat dy = pt2.y - pt1.y;
|
||||
CGFloat ajdusted_dy = ABS(dy) / length;
|
||||
CGFloat angle = asin(ajdusted_dy);
|
||||
|
||||
if ((dx < 0) && (dy >= 0)) {
|
||||
// top-left quadrant
|
||||
angle = M_PI - angle;
|
||||
}
|
||||
else if ((dx < 0) && (dy < 0)) {
|
||||
// bottom-left quadrant
|
||||
angle = M_PI + angle;
|
||||
}
|
||||
else if ((dx >= 0) && (dy < 0)) {
|
||||
// bottom-right quadrant
|
||||
angle = (2 * M_PI) - angle;
|
||||
}
|
||||
return angle;
|
||||
}
|
||||
13
cocoalib/HSPyUtil.h
Normal file
13
cocoalib/HSPyUtil.h
Normal file
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <Python.h>
|
||||
|
||||
void setCocoaViewsModuleName(NSString *moduleName);
|
||||
PyObject* createCallback(NSString *aViewClassName, id aViewRef);
|
||||
34
cocoalib/HSPyUtil.m
Normal file
34
cocoalib/HSPyUtil.m
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import "HSPyUtil.h"
|
||||
#import "ObjP.h"
|
||||
|
||||
static NSString *gCocoaViewsModuleName;
|
||||
void setCocoaViewsModuleName(NSString *moduleName)
|
||||
{
|
||||
if (gCocoaViewsModuleName != nil) {
|
||||
[gCocoaViewsModuleName release];
|
||||
}
|
||||
gCocoaViewsModuleName = [moduleName retain];
|
||||
}
|
||||
|
||||
PyObject* createCallback(NSString *aViewClassName, id aViewRef)
|
||||
{
|
||||
NSString *moduleName;
|
||||
if (gCocoaViewsModuleName != nil) {
|
||||
moduleName = gCocoaViewsModuleName;
|
||||
}
|
||||
else {
|
||||
moduleName = @"inter.CocoaViews";
|
||||
}
|
||||
PyGILState_STATE gilState = PyGILState_Ensure();
|
||||
PyObject *pCallback = ObjP_classInstanceWithRef(aViewClassName, moduleName, aViewRef);
|
||||
PyGILState_Release(gilState);
|
||||
return pCallback;
|
||||
}
|
||||
18
cocoalib/HSQuicklook.h
Normal file
18
cocoalib/HSQuicklook.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <Quartz/Quartz.h>
|
||||
|
||||
@interface HSQLPreviewItem : NSObject <QLPreviewItem>
|
||||
{
|
||||
NSURL *url;
|
||||
NSString *title;
|
||||
}
|
||||
- (id)initWithUrl:(NSURL *)aUrl title:(NSString *)aTitle;
|
||||
@end
|
||||
36
cocoalib/HSQuicklook.m
Normal file
36
cocoalib/HSQuicklook.m
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import "HSQuicklook.h"
|
||||
|
||||
@implementation HSQLPreviewItem
|
||||
- (id)initWithUrl:(NSURL *)aUrl title:(NSString *)aTitle
|
||||
{
|
||||
self = [super init];
|
||||
url = [aUrl retain];
|
||||
title = [aTitle retain];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[url release];
|
||||
[title release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSURL *)previewItemURL
|
||||
{
|
||||
return url;
|
||||
}
|
||||
|
||||
- (NSString *)previewItemTitle
|
||||
{
|
||||
return title;
|
||||
}
|
||||
@end
|
||||
35
cocoalib/HSRecentFiles.h
Normal file
35
cocoalib/HSRecentFiles.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface HSRecentFiles : NSObject
|
||||
{
|
||||
id delegate;
|
||||
NSMenu *menu;
|
||||
NSString *name;
|
||||
NSMutableArray *filepaths;
|
||||
NSInteger numberOfMenuItemsToPreserve;
|
||||
}
|
||||
- (id)initWithName:(NSString *)aName menu:(NSMenu *)aMenu;
|
||||
|
||||
- (void)addFile:(NSString *)path;
|
||||
- (void)rebuildMenu;
|
||||
- (void)fillMenu:(NSMenu *)menu;
|
||||
- (void)clearMenu:(id)sender;
|
||||
- (void)menuClick:(id)sender;
|
||||
|
||||
- (NSMenu *)menu;
|
||||
- (id)delegate;
|
||||
- (void)setDelegate:(id)aDelegate;
|
||||
- (NSArray *)filepaths;
|
||||
@end
|
||||
|
||||
@protocol HSRecentFilesDelegate
|
||||
- (void)recentFileClicked:(NSString *)path;
|
||||
@end
|
||||
89
cocoalib/HSRecentFiles.m
Normal file
89
cocoalib/HSRecentFiles.m
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import "HSRecentFiles.h"
|
||||
|
||||
@implementation HSRecentFiles
|
||||
- (id)initWithName:(NSString *)aName menu:(NSMenu *)aMenu
|
||||
{
|
||||
self = [super init];
|
||||
name = aName;
|
||||
menu = [aMenu retain];
|
||||
numberOfMenuItemsToPreserve = [menu numberOfItems];
|
||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||
filepaths = [[NSMutableArray alloc] initWithArray:[ud arrayForKey:name]];
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
for (NSInteger i=[filepaths count]-1;i>=0;i--) {
|
||||
NSString *path = [filepaths objectAtIndex:i];
|
||||
// We check for path class because we might be fed with garbage from the prefs.
|
||||
if ((![path isKindOfClass:[NSString class]]) || (![fm fileExistsAtPath:path])) {
|
||||
[filepaths removeObjectAtIndex:i];
|
||||
}
|
||||
}
|
||||
[self rebuildMenu];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
|
||||
[ud setObject:filepaths forKey:name];
|
||||
[ud synchronize];
|
||||
[filepaths release];
|
||||
[menu release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)addFile:(NSString *)path
|
||||
{
|
||||
[filepaths removeObject:path];
|
||||
[filepaths insertObject:path atIndex:0];
|
||||
[self rebuildMenu];
|
||||
}
|
||||
|
||||
- (void)rebuildMenu
|
||||
{
|
||||
while ([menu numberOfItems] > numberOfMenuItemsToPreserve)
|
||||
[menu removeItemAtIndex:[menu numberOfItems]-1];
|
||||
[self fillMenu:menu];
|
||||
if ([filepaths count] > 0) {
|
||||
[menu addItem:[NSMenuItem separatorItem]];
|
||||
NSMenuItem *mi = [menu addItemWithTitle:NSLocalizedStringFromTable(@"Clear List", @"cocoalib", @"") action:@selector(clearMenu:) keyEquivalent:@""];
|
||||
[mi setTarget:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)fillMenu:(NSMenu *)menuToFill
|
||||
{
|
||||
for (int i=0;i<[filepaths count];i++) {
|
||||
NSMenuItem *mi = [menuToFill addItemWithTitle:[filepaths objectAtIndex:i] action:@selector(menuClick:) keyEquivalent:@""];
|
||||
[mi setTag:i];
|
||||
[mi setTarget:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)clearMenu:(id)sender
|
||||
{
|
||||
[filepaths removeAllObjects];
|
||||
[self rebuildMenu];
|
||||
}
|
||||
|
||||
- (void)menuClick:(id)sender
|
||||
{
|
||||
if (delegate == nil)
|
||||
return;
|
||||
if ([delegate respondsToSelector:@selector(recentFileClicked:)])
|
||||
[delegate recentFileClicked:[filepaths objectAtIndex:[sender tag]]];
|
||||
}
|
||||
|
||||
/* Properties */
|
||||
- (NSMenu *)menu {return menu;}
|
||||
- (id)delegate { return delegate; }
|
||||
- (void)setDelegate:(id)aDelegate { delegate = aDelegate; }
|
||||
- (NSArray *)filepaths {return filepaths;}
|
||||
@end
|
||||
10
cocoalib/LICENSE
Normal file
10
cocoalib/LICENSE
Normal file
@@ -0,0 +1,10 @@
|
||||
Copyright 2013, Hardcoded Software Inc., http://www.hardcoded.net
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Hardcoded Software Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
24
cocoalib/NSEventAdditions.h
Normal file
24
cocoalib/NSEventAdditions.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface NSEvent(NSEventAdditions)
|
||||
- (unichar)firstCharacter;
|
||||
- (NSUInteger)flags;
|
||||
- (NSUInteger)modifierKeysFlags;
|
||||
- (BOOL)isDeleteOrBackspace;
|
||||
- (BOOL)isReturnOrEnter;
|
||||
- (BOOL)isTab;
|
||||
- (BOOL)isBackTab;
|
||||
- (BOOL)isSpace;
|
||||
- (BOOL)isUp;
|
||||
- (BOOL)isDown;
|
||||
- (BOOL)isLeft;
|
||||
- (BOOL)isRight;
|
||||
@end
|
||||
85
cocoalib/NSEventAdditions.m
Normal file
85
cocoalib/NSEventAdditions.m
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import "NSEventAdditions.h"
|
||||
|
||||
@implementation NSEvent(NSEventAdditions)
|
||||
|
||||
- (unichar)firstCharacter
|
||||
{
|
||||
NSString *characters = [self characters];
|
||||
if ([characters length] == 0)
|
||||
{
|
||||
return '\0';
|
||||
}
|
||||
return [characters characterAtIndex:0];
|
||||
}
|
||||
|
||||
- (NSUInteger)flags
|
||||
{
|
||||
// get flags and strip the lower 16 (device dependant) bits
|
||||
// See modifierFlags's doc for details
|
||||
return [self modifierFlags] & NSDeviceIndependentModifierFlagsMask;
|
||||
}
|
||||
|
||||
- (NSUInteger)modifierKeysFlags
|
||||
{
|
||||
// This is modifierFlags with only Command, Opt, Ctrl and Shift, without the rest of the flags
|
||||
// to pollute.
|
||||
return [self flags] & (NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask | NSCommandKeyMask);
|
||||
}
|
||||
|
||||
- (BOOL)isDeleteOrBackspace
|
||||
{
|
||||
unichar firstChar = [self firstCharacter];
|
||||
return firstChar == NSDeleteFunctionKey || firstChar == NSDeleteCharFunctionKey ||
|
||||
firstChar == NSDeleteCharacter || firstChar == NSBackspaceCharacter;
|
||||
}
|
||||
|
||||
- (BOOL)isReturnOrEnter
|
||||
{
|
||||
unichar firstChar = [self firstCharacter];
|
||||
return firstChar == NSCarriageReturnCharacter || firstChar == NSEnterCharacter;
|
||||
}
|
||||
|
||||
- (BOOL)isTab
|
||||
{
|
||||
return [self firstCharacter] == NSTabCharacter;
|
||||
}
|
||||
|
||||
- (BOOL)isBackTab
|
||||
{
|
||||
return [self firstCharacter] == NSBackTabCharacter;
|
||||
}
|
||||
|
||||
- (BOOL)isSpace
|
||||
{
|
||||
return ([self firstCharacter] == 0x20) && (![self flags]);
|
||||
}
|
||||
|
||||
- (BOOL)isUp
|
||||
{
|
||||
return [self firstCharacter] == NSUpArrowFunctionKey;
|
||||
}
|
||||
|
||||
- (BOOL)isDown
|
||||
{
|
||||
return [self firstCharacter] == NSDownArrowFunctionKey;
|
||||
}
|
||||
|
||||
- (BOOL)isLeft
|
||||
{
|
||||
return [self firstCharacter] == NSLeftArrowFunctionKey;
|
||||
}
|
||||
|
||||
- (BOOL)isRight
|
||||
{
|
||||
return [self firstCharacter] == NSRightArrowFunctionKey;
|
||||
}
|
||||
|
||||
@end
|
||||
21
cocoalib/NSImageAdditions.h
Normal file
21
cocoalib/NSImageAdditions.h
Normal file
@@ -0,0 +1,21 @@
|
||||
// Created by Scott Stevenson on 9/28/07.
|
||||
//
|
||||
// Personal site: http://theocacao.com/
|
||||
// Post for this sample: http://theocacao.com/document.page/497
|
||||
//
|
||||
// The code in this project is intended to be used as a learning
|
||||
// tool for Cocoa programmers. You may freely use the code in
|
||||
// your own programs, but please do not use the code as-is in
|
||||
// other tutorials.
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
@interface NSImage (Extras)
|
||||
|
||||
// creates a copy of the current image while maintaining
|
||||
// proportions. also centers image, if necessary
|
||||
|
||||
- (NSImage*)imageByScalingProportionallyToSize:(NSSize)aSize;
|
||||
|
||||
@end
|
||||
114
cocoalib/NSImageAdditions.m
Normal file
114
cocoalib/NSImageAdditions.m
Normal file
@@ -0,0 +1,114 @@
|
||||
// Created by Scott Stevenson on 9/28/07.
|
||||
//
|
||||
// Personal site: http://theocacao.com/
|
||||
// Post for this sample: http://theocacao.com/document.page/497
|
||||
//
|
||||
// The code in this project is intended to be used as a learning
|
||||
// tool for Cocoa programmers. You may freely use the code in
|
||||
// your own programs, but please do not use the code as-is in
|
||||
// other tutorials.
|
||||
|
||||
#import "NSImageAdditions.h"
|
||||
|
||||
|
||||
@implementation NSImage (Extras)
|
||||
|
||||
- (NSImage*)imageByScalingProportionallyToSize:(NSSize)targetSize
|
||||
{
|
||||
NSImage* sourceImage = self;
|
||||
NSImage* newImage = nil;
|
||||
|
||||
if ([sourceImage isValid])
|
||||
{
|
||||
NSSize imageSize = [sourceImage size];
|
||||
CGFloat width = imageSize.width;
|
||||
CGFloat height = imageSize.height;
|
||||
|
||||
CGFloat targetWidth = targetSize.width;
|
||||
CGFloat targetHeight = targetSize.height;
|
||||
|
||||
// scaleFactor will be the fraction that we'll
|
||||
// use to adjust the size. For example, if we shrink
|
||||
// an image by half, scaleFactor will be 0.5. the
|
||||
// scaledWidth and scaledHeight will be the original,
|
||||
// multiplied by the scaleFactor.
|
||||
//
|
||||
// IMPORTANT: the "targetHeight" is the size of the space
|
||||
// we're drawing into. The "scaledHeight" is the height that
|
||||
// the image actually is drawn at, once we take into
|
||||
// account the ideal of maintaining proportions
|
||||
|
||||
CGFloat scaleFactor = 0.0;
|
||||
CGFloat scaledWidth = targetWidth;
|
||||
CGFloat scaledHeight = targetHeight;
|
||||
|
||||
NSPoint thumbnailPoint = NSMakePoint(0,0);
|
||||
|
||||
// since not all images are square, we want to scale
|
||||
// proportionately. To do this, we find the longest
|
||||
// edge and use that as a guide.
|
||||
|
||||
if ( NSEqualSizes( imageSize, targetSize ) == NO )
|
||||
{
|
||||
// use the longeset edge as a guide. if the
|
||||
// image is wider than tall, we'll figure out
|
||||
// the scale factor by dividing it by the
|
||||
// intended width. Otherwise, we'll use the
|
||||
// height.
|
||||
|
||||
CGFloat widthFactor = targetWidth / width;
|
||||
CGFloat heightFactor = targetHeight / height;
|
||||
|
||||
if ( widthFactor < heightFactor )
|
||||
scaleFactor = widthFactor;
|
||||
else
|
||||
scaleFactor = heightFactor;
|
||||
|
||||
// ex: 500 * 0.5 = 250 (newWidth)
|
||||
|
||||
scaledWidth = width * scaleFactor;
|
||||
scaledHeight = height * scaleFactor;
|
||||
|
||||
// center the thumbnail in the frame. if
|
||||
// wider than tall, we need to adjust the
|
||||
// vertical drawing point (y axis)
|
||||
|
||||
if ( widthFactor < heightFactor )
|
||||
thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;
|
||||
|
||||
else if ( widthFactor > heightFactor )
|
||||
thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
|
||||
}
|
||||
|
||||
|
||||
// create a new image to draw into
|
||||
newImage = [[NSImage alloc] initWithSize:targetSize];
|
||||
|
||||
// once focus is locked, all drawing goes into this NSImage instance
|
||||
// directly, not to the screen. It also receives its own graphics
|
||||
// context.
|
||||
//
|
||||
// Also, keep in mind that we're doing this in a background thread.
|
||||
// You only want to draw to the screen in the main thread, but
|
||||
// drawing to an offscreen image is (apparently) okay.
|
||||
|
||||
[newImage lockFocus];
|
||||
|
||||
NSRect thumbnailRect;
|
||||
thumbnailRect.origin = thumbnailPoint;
|
||||
thumbnailRect.size.width = scaledWidth;
|
||||
thumbnailRect.size.height = scaledHeight;
|
||||
|
||||
[sourceImage drawInRect: thumbnailRect
|
||||
fromRect: NSZeroRect
|
||||
operation: NSCompositeSourceOver
|
||||
fraction: 1.0];
|
||||
|
||||
[newImage unlockFocus];
|
||||
|
||||
}
|
||||
|
||||
return [newImage autorelease];
|
||||
}
|
||||
|
||||
@end
|
||||
10
cocoalib/NSNotificationAdditions.h
Normal file
10
cocoalib/NSNotificationAdditions.h
Normal file
@@ -0,0 +1,10 @@
|
||||
// from http://www.cocoadev.com/index.pl?NotificationsAcrossThreads
|
||||
#import <Cocoa/Cocoa.h>
|
||||
@interface NSNotificationCenter (NSNotificationCenterAdditions)
|
||||
- (void) postNotificationOnMainThread:(NSNotification *) notification;
|
||||
- (void) postNotificationOnMainThread:(NSNotification *) notification waitUntilDone:(BOOL) wait;
|
||||
|
||||
- (void) postNotificationOnMainThreadWithName:(NSString *) name object:(id) object;
|
||||
- (void) postNotificationOnMainThreadWithName:(NSString *) name object:(id) object userInfo:(NSDictionary *) userInfo;
|
||||
- (void) postNotificationOnMainThreadWithName:(NSString *) name object:(id) object userInfo:(NSDictionary *) userInfo waitUntilDone:(BOOL) wait;
|
||||
@end
|
||||
48
cocoalib/NSNotificationAdditions.m
Normal file
48
cocoalib/NSNotificationAdditions.m
Normal file
@@ -0,0 +1,48 @@
|
||||
#import "NSNotificationAdditions.h"
|
||||
#import <pthread.h>
|
||||
|
||||
@implementation NSNotificationCenter (NSNotificationCenterAdditions)
|
||||
- (void) postNotificationOnMainThread:(NSNotification *) notification {
|
||||
if( pthread_main_np() ) return [self postNotification:notification];
|
||||
[self postNotificationOnMainThread:notification waitUntilDone:NO];
|
||||
}
|
||||
|
||||
- (void) postNotificationOnMainThread:(NSNotification *) notification waitUntilDone:(BOOL) wait {
|
||||
if( pthread_main_np() ) return [self postNotification:notification];
|
||||
[[self class] performSelectorOnMainThread:@selector( _postNotification: ) withObject:notification waitUntilDone:wait];
|
||||
}
|
||||
|
||||
+ (void) _postNotification:(NSNotification *) notification {
|
||||
[[self defaultCenter] postNotification:notification];
|
||||
}
|
||||
|
||||
- (void) postNotificationOnMainThreadWithName:(NSString *) name object:(id) object {
|
||||
if( pthread_main_np() ) return [self postNotificationName:name object:object userInfo:nil];
|
||||
[self postNotificationOnMainThreadWithName:name object:object userInfo:nil waitUntilDone:NO];
|
||||
}
|
||||
|
||||
- (void) postNotificationOnMainThreadWithName:(NSString *) name object:(id) object userInfo:(NSDictionary *) userInfo {
|
||||
if( pthread_main_np() ) return [self postNotificationName:name object:object userInfo:userInfo];
|
||||
[self postNotificationOnMainThreadWithName:name object:object userInfo:nil waitUntilDone:NO];
|
||||
}
|
||||
|
||||
- (void) postNotificationOnMainThreadWithName:(NSString *) name object:(id) object userInfo:(NSDictionary *) userInfo waitUntilDone:(BOOL) wait {
|
||||
if( pthread_main_np() ) return [self postNotificationName:name object:object userInfo:userInfo];
|
||||
|
||||
NSMutableDictionary *info = [[NSMutableDictionary allocWithZone:nil] init];
|
||||
[info setObject:name forKey:@"name"];
|
||||
if( object ) [info setObject:object forKey:@"object"];
|
||||
if( userInfo ) [info setObject:userInfo forKey:@"userInfo"];
|
||||
|
||||
[[self class] performSelectorOnMainThread:@selector( _postNotificationName: ) withObject:info waitUntilDone:wait];
|
||||
[info release];
|
||||
}
|
||||
|
||||
+ (void) _postNotificationName:(NSDictionary *) info {
|
||||
NSString *name = [info objectForKey:@"name"];
|
||||
id object = [info objectForKey:@"object"];
|
||||
NSDictionary *userInfo = [info objectForKey:@"userInfo"];
|
||||
|
||||
[[self defaultCenter] postNotificationName:name object:object userInfo:userInfo];
|
||||
}
|
||||
@end
|
||||
50
cocoalib/ProgressController.h
Normal file
50
cocoalib/ProgressController.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "Worker.h"
|
||||
|
||||
extern NSString *JobCompletedNotification;
|
||||
extern NSString *JobCancelledNotification;
|
||||
|
||||
@interface ProgressController : NSWindowController <NSWindowDelegate>
|
||||
{
|
||||
NSButton *cancelButton;
|
||||
NSProgressIndicator *progressBar;
|
||||
NSTextField *statusText;
|
||||
NSTextField *descText;
|
||||
|
||||
id _jobId;
|
||||
BOOL _running;
|
||||
NSObject<Worker> *_worker;
|
||||
}
|
||||
|
||||
@property (readwrite, retain) NSButton *cancelButton;
|
||||
@property (readwrite, retain) NSProgressIndicator *progressBar;
|
||||
@property (readwrite, retain) NSTextField *statusText;
|
||||
@property (readwrite, retain) NSTextField *descText;
|
||||
|
||||
+ (ProgressController *)mainProgressController;
|
||||
|
||||
- (id)init;
|
||||
|
||||
- (void)cancel;
|
||||
|
||||
- (void)hide;
|
||||
- (void)show;
|
||||
- (void)showWithCancelButton:(BOOL)cancelEnabled;
|
||||
- (void)showSheetForParent:(NSWindow *) parentWindow;
|
||||
- (void)showSheetForParent:(NSWindow *) parentWindow withCancelButton:(BOOL)cancelEnabled;
|
||||
|
||||
/* Properties */
|
||||
- (BOOL)isShown;
|
||||
- (id)jobId;
|
||||
- (void)setJobId:(id)jobId;
|
||||
- (void)setJobDesc:(NSString *)desc;
|
||||
- (void)setWorker:(NSObject<Worker> *)worker;
|
||||
@end
|
||||
160
cocoalib/ProgressController.m
Normal file
160
cocoalib/ProgressController.m
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import "ProgressController.h"
|
||||
#import "Utils.h"
|
||||
#import "ProgressController_UI.h"
|
||||
|
||||
NSString *JobCompletedNotification = @"JobCompletedNotification";
|
||||
NSString *JobCancelledNotification = @"JobCancelledNotification";
|
||||
static ProgressController *_mainPC = nil;
|
||||
|
||||
@implementation ProgressController
|
||||
|
||||
@synthesize cancelButton;
|
||||
@synthesize progressBar;
|
||||
@synthesize statusText;
|
||||
@synthesize descText;
|
||||
|
||||
+ (ProgressController *)mainProgressController
|
||||
{
|
||||
if (_mainPC == nil)
|
||||
_mainPC = [[ProgressController alloc] init];
|
||||
return _mainPC;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super initWithWindow:nil];
|
||||
[self setWindow:createProgressController_UI(self)];
|
||||
[progressBar setUsesThreadedAnimation:YES];
|
||||
_worker = nil;
|
||||
_running = NO;
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:NSApplicationDidBecomeActiveNotification object:nil];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)cancel
|
||||
{
|
||||
[self hide];
|
||||
}
|
||||
|
||||
- (void)hide
|
||||
{
|
||||
if (_worker != nil)
|
||||
[_worker cancelJob];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:JobCancelledNotification object:self];
|
||||
_running = NO;
|
||||
[NSApp endSheet:[self window] returnCode:NSRunAbortedResponse];
|
||||
/* There's this really strange thing where when the app is inactive at the point we want to hide
|
||||
the progress dialog, it becomes impossible to close it. I guess it's due to some strange
|
||||
thread-related crap. Anyway, *DO NOT HIDE THE SHEET WHILE THE APP IS INACTIVE*. Do it later,
|
||||
when the app becomes active again.
|
||||
*/
|
||||
if ([NSApp isActive]) {
|
||||
[[self window] orderOut:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)show
|
||||
{
|
||||
[self showWithCancelButton:YES];
|
||||
}
|
||||
|
||||
- (void)showWithCancelButton:(BOOL)cancelEnabled
|
||||
{
|
||||
[progressBar setIndeterminate:YES];
|
||||
[[self window] makeKeyAndOrderFront:nil];
|
||||
[progressBar setUsesThreadedAnimation:YES];
|
||||
[progressBar startAnimation:nil];
|
||||
[cancelButton setEnabled:cancelEnabled];
|
||||
_running = YES;
|
||||
[NSThread detachNewThreadSelector:@selector(threadedWorkerProbe) toTarget:self withObject:nil];
|
||||
}
|
||||
|
||||
- (void)showSheetForParent:(NSWindow *) parentWindow
|
||||
{
|
||||
[self showSheetForParent:parentWindow withCancelButton:YES];
|
||||
}
|
||||
|
||||
- (void)showSheetForParent:(NSWindow *) parentWindow withCancelButton:(BOOL)cancelEnabled
|
||||
{
|
||||
[progressBar setIndeterminate:YES];
|
||||
[progressBar startAnimation:nil];
|
||||
[cancelButton setEnabled:cancelEnabled];
|
||||
_running = YES;
|
||||
[NSThread detachNewThreadSelector:@selector(threadedWorkerProbe) toTarget:self withObject:nil];
|
||||
[NSApp beginSheet:[self window] modalForWindow:parentWindow modalDelegate:nil didEndSelector:nil contextInfo:nil];
|
||||
}
|
||||
|
||||
- (void)updateProgress
|
||||
{
|
||||
if (!_running)
|
||||
return;
|
||||
NSNumber *progress = [_worker getJobProgress];
|
||||
NSString *status = [_worker getJobDesc];
|
||||
if ((status != nil) && ([status length] > 0))
|
||||
{
|
||||
[statusText setStringValue:status];
|
||||
}
|
||||
if (progress != nil)
|
||||
{
|
||||
[progressBar setDoubleValue:n2i(progress)];
|
||||
[progressBar setIndeterminate: n2i(progress) < 0];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self hide];
|
||||
[_worker jobCompleted:_jobId];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:JobCompletedNotification object:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)threadedWorkerProbe
|
||||
{
|
||||
while (_running && (_worker != nil))
|
||||
{
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];
|
||||
[self performSelectorOnMainThread:@selector(updateProgress) withObject:nil waitUntilDone:YES];
|
||||
[pool release];
|
||||
}
|
||||
}
|
||||
|
||||
/* Properties */
|
||||
- (BOOL)isShown
|
||||
{
|
||||
return _running;
|
||||
}
|
||||
|
||||
- (id)jobId {return _jobId;}
|
||||
- (void)setJobId:(id)jobId
|
||||
{
|
||||
[_jobId autorelease];
|
||||
_jobId = [jobId retain];
|
||||
}
|
||||
|
||||
- (void)setJobDesc:(NSString *)desc
|
||||
{
|
||||
[descText setStringValue:desc];
|
||||
[statusText setStringValue:NSLocalizedStringFromTable(@"Please wait...", @"cocoalib", @"")];
|
||||
}
|
||||
|
||||
- (void)setWorker:(NSObject<Worker> *)worker
|
||||
{
|
||||
_worker = worker;
|
||||
}
|
||||
|
||||
/* Delegate and Notifs */
|
||||
- (void)applicationDidBecomeActive:(NSNotification *)notification
|
||||
{
|
||||
if (!_running) {
|
||||
[[self window] orderOut:nil];
|
||||
}
|
||||
}
|
||||
@end
|
||||
1
cocoalib/Sparkle.framework/Headers
Symbolic link
1
cocoalib/Sparkle.framework/Headers
Symbolic link
@@ -0,0 +1 @@
|
||||
Versions/Current/Headers
|
||||
1
cocoalib/Sparkle.framework/Resources
Symbolic link
1
cocoalib/Sparkle.framework/Resources
Symbolic link
@@ -0,0 +1 @@
|
||||
Versions/Current/Resources
|
||||
1
cocoalib/Sparkle.framework/Sparkle
Symbolic link
1
cocoalib/Sparkle.framework/Sparkle
Symbolic link
@@ -0,0 +1 @@
|
||||
Versions/Current/Sparkle
|
||||
33
cocoalib/Sparkle.framework/Versions/A/Headers/SUAppcast.h
Normal file
33
cocoalib/Sparkle.framework/Versions/A/Headers/SUAppcast.h
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// SUAppcast.h
|
||||
// Sparkle
|
||||
//
|
||||
// Created by Andy Matuschak on 3/12/06.
|
||||
// Copyright 2006 Andy Matuschak. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef SUAPPCAST_H
|
||||
#define SUAPPCAST_H
|
||||
|
||||
@class SUAppcastItem;
|
||||
@interface SUAppcast : NSObject {
|
||||
NSArray *items;
|
||||
NSString *userAgentString;
|
||||
id delegate;
|
||||
NSMutableData *incrementalData;
|
||||
}
|
||||
|
||||
- (void)fetchAppcastFromURL:(NSURL *)url;
|
||||
- (void)setDelegate:delegate;
|
||||
- (void)setUserAgentString:(NSString *)userAgentString;
|
||||
|
||||
- (NSArray *)items;
|
||||
|
||||
@end
|
||||
|
||||
@interface NSObject (SUAppcastDelegate)
|
||||
- (void)appcastDidFinishLoading:(SUAppcast *)appcast;
|
||||
- (void)appcast:(SUAppcast *)appcast failedToLoadWithError:(NSError *)error;
|
||||
@end
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// SUAppcastItem.h
|
||||
// Sparkle
|
||||
//
|
||||
// Created by Andy Matuschak on 3/12/06.
|
||||
// Copyright 2006 Andy Matuschak. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef SUAPPCASTITEM_H
|
||||
#define SUAPPCASTITEM_H
|
||||
|
||||
@interface SUAppcastItem : NSObject {
|
||||
NSString *title;
|
||||
NSDate *date;
|
||||
NSString *itemDescription;
|
||||
|
||||
NSURL *releaseNotesURL;
|
||||
|
||||
NSString *DSASignature;
|
||||
NSString *minimumSystemVersion;
|
||||
|
||||
NSURL *fileURL;
|
||||
NSString *versionString;
|
||||
NSString *displayVersionString;
|
||||
|
||||
NSDictionary *propertiesDictionary;
|
||||
}
|
||||
|
||||
// Initializes with data from a dictionary provided by the RSS class.
|
||||
- initWithDictionary:(NSDictionary *)dict;
|
||||
|
||||
- (NSString *)title;
|
||||
- (NSString *)versionString;
|
||||
- (NSString *)displayVersionString;
|
||||
- (NSDate *)date;
|
||||
- (NSString *)itemDescription;
|
||||
- (NSURL *)releaseNotesURL;
|
||||
- (NSURL *)fileURL;
|
||||
- (NSString *)DSASignature;
|
||||
- (NSString *)minimumSystemVersion;
|
||||
|
||||
// Returns the dictionary provided in initWithDictionary; this might be useful later for extensions.
|
||||
- (NSDictionary *)propertiesDictionary;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
118
cocoalib/Sparkle.framework/Versions/A/Headers/SUUpdater.h
Normal file
118
cocoalib/Sparkle.framework/Versions/A/Headers/SUUpdater.h
Normal file
@@ -0,0 +1,118 @@
|
||||
//
|
||||
// SUUpdater.h
|
||||
// Sparkle
|
||||
//
|
||||
// Created by Andy Matuschak on 1/4/06.
|
||||
// Copyright 2006 Andy Matuschak. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef SUUPDATER_H
|
||||
#define SUUPDATER_H
|
||||
|
||||
#import <Sparkle/SUVersionComparisonProtocol.h>
|
||||
|
||||
@class SUUpdateDriver, SUAppcastItem, SUHost, SUAppcast;
|
||||
@interface SUUpdater : NSObject {
|
||||
NSTimer *checkTimer;
|
||||
SUUpdateDriver *driver;
|
||||
|
||||
SUHost *host;
|
||||
IBOutlet id delegate;
|
||||
}
|
||||
|
||||
+ (SUUpdater *)sharedUpdater;
|
||||
+ (SUUpdater *)updaterForBundle:(NSBundle *)bundle;
|
||||
- (NSBundle *)hostBundle;
|
||||
|
||||
- (void)setDelegate:(id)delegate;
|
||||
- delegate;
|
||||
|
||||
- (void)setAutomaticallyChecksForUpdates:(BOOL)automaticallyChecks;
|
||||
- (BOOL)automaticallyChecksForUpdates;
|
||||
|
||||
- (void)setUpdateCheckInterval:(NSTimeInterval)interval;
|
||||
- (NSTimeInterval)updateCheckInterval;
|
||||
|
||||
- (void)setFeedURL:(NSURL *)feedURL;
|
||||
- (NSURL *)feedURL;
|
||||
|
||||
- (void)setSendsSystemProfile:(BOOL)sendsSystemProfile;
|
||||
- (BOOL)sendsSystemProfile;
|
||||
|
||||
- (void)setAutomaticallyDownloadsUpdates:(BOOL)automaticallyDownloadsUpdates;
|
||||
- (BOOL)automaticallyDownloadsUpdates;
|
||||
|
||||
// This IBAction is meant for a main menu item. Hook up any menu item to this action,
|
||||
// and Sparkle will check for updates and report back its findings verbosely.
|
||||
- (IBAction)checkForUpdates:sender;
|
||||
|
||||
// This kicks off an update meant to be programmatically initiated. That is, it will display no UI unless it actually finds an update,
|
||||
// in which case it proceeds as usual. If the fully automated updating is turned on, however, this will invoke that behavior, and if an
|
||||
// update is found, it will be downloaded and prepped for installation.
|
||||
- (void)checkForUpdatesInBackground;
|
||||
|
||||
// Date of last update check. Returns null if no check has been performed.
|
||||
- (NSDate*)lastUpdateCheckDate;
|
||||
|
||||
// This begins a "probing" check for updates which will not actually offer to update to that version. The delegate methods, though,
|
||||
// (up to updater:didFindValidUpdate: and updaterDidNotFindUpdate:), are called, so you can use that information in your UI.
|
||||
- (void)checkForUpdateInformation;
|
||||
|
||||
// Call this to appropriately schedule or cancel the update checking timer according to the preferences for time interval and automatic checks. This call does not change the date of the next check, but only the internal NSTimer.
|
||||
- (void)resetUpdateCycle;
|
||||
|
||||
- (BOOL)updateInProgress;
|
||||
@end
|
||||
|
||||
@interface NSObject (SUUpdaterDelegateInformalProtocol)
|
||||
// This method allows you to add extra parameters to the appcast URL, potentially based on whether or not Sparkle will also be sending along the system profile. This method should return an array of dictionaries with keys: "key", "value", "displayKey", "displayValue", the latter two being specifically for display to the user.
|
||||
- (NSArray *)feedParametersForUpdater:(SUUpdater *)updater sendingSystemProfile:(BOOL)sendingProfile;
|
||||
|
||||
// Use this to override the default behavior for Sparkle prompting the user about automatic update checks.
|
||||
- (BOOL)updaterShouldPromptForPermissionToCheckForUpdates:(SUUpdater *)bundle;
|
||||
|
||||
// Implement this if you want to do some special handling with the appcast once it finishes loading.
|
||||
- (void)updater:(SUUpdater *)updater didFinishLoadingAppcast:(SUAppcast *)appcast;
|
||||
|
||||
// If you're using special logic or extensions in your appcast, implement this to use your own logic for finding
|
||||
// a valid update, if any, in the given appcast.
|
||||
- (SUAppcastItem *)bestValidUpdateInAppcast:(SUAppcast *)appcast forUpdater:(SUUpdater *)bundle;
|
||||
|
||||
// Sent when a valid update is found by the update driver.
|
||||
- (void)updater:(SUUpdater *)updater didFindValidUpdate:(SUAppcastItem *)update;
|
||||
|
||||
// Sent when a valid update is not found.
|
||||
- (void)updaterDidNotFindUpdate:(SUUpdater *)update;
|
||||
|
||||
// Sent immediately before installing the specified update.
|
||||
- (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)update;
|
||||
|
||||
// Return YES to delay the relaunch until you do some processing; invoke the given NSInvocation to continue.
|
||||
- (BOOL)updater:(SUUpdater *)updater shouldPostponeRelaunchForUpdate:(SUAppcastItem *)update untilInvoking:(NSInvocation *)invocation;
|
||||
|
||||
// Called immediately before relaunching.
|
||||
- (void)updaterWillRelaunchApplication:(SUUpdater *)updater;
|
||||
|
||||
// This method allows you to provide a custom version comparator.
|
||||
// If you don't implement this method or return nil, the standard version comparator will be used.
|
||||
- (id <SUVersionComparison>)versionComparatorForUpdater:(SUUpdater *)updater;
|
||||
|
||||
// Returns the path which is used to relaunch the client after the update is installed. By default, the path of the host bundle.
|
||||
- (NSString *)pathToRelaunchForUpdater:(SUUpdater *)updater;
|
||||
|
||||
@end
|
||||
|
||||
// Define some minimum intervals to avoid DOS-like checking attacks. These are in seconds.
|
||||
#ifdef DEBUG
|
||||
#define SU_MIN_CHECK_INTERVAL 60
|
||||
#else
|
||||
#define SU_MIN_CHECK_INTERVAL 60*60
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
#define SU_DEFAULT_CHECK_INTERVAL 60
|
||||
#else
|
||||
#define SU_DEFAULT_CHECK_INTERVAL 60*60*24
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// SUVersionComparisonProtocol.h
|
||||
// Sparkle
|
||||
//
|
||||
// Created by Andy Matuschak on 12/21/07.
|
||||
// Copyright 2007 Andy Matuschak. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef SUVERSIONCOMPARISONPROTOCOL_H
|
||||
#define SUVERSIONCOMPARISONPROTOCOL_H
|
||||
|
||||
/*!
|
||||
@protocol
|
||||
@abstract Implement this protocol to provide version comparison facilities for Sparkle.
|
||||
*/
|
||||
@protocol SUVersionComparison
|
||||
|
||||
/*!
|
||||
@method
|
||||
@abstract An abstract method to compare two version strings.
|
||||
@discussion Should return NSOrderedAscending if b > a, NSOrderedDescending if b < a, and NSOrderedSame if they are equivalent.
|
||||
*/
|
||||
- (NSComparisonResult)compareVersion:(NSString *)versionA toVersion:(NSString *)versionB;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
21
cocoalib/Sparkle.framework/Versions/A/Headers/Sparkle.h
Normal file
21
cocoalib/Sparkle.framework/Versions/A/Headers/Sparkle.h
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Sparkle.h
|
||||
// Sparkle
|
||||
//
|
||||
// Created by Andy Matuschak on 3/16/06. (Modified by CDHW on 23/12/07)
|
||||
// Copyright 2006 Andy Matuschak. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef SPARKLE_H
|
||||
#define SPARKLE_H
|
||||
|
||||
// This list should include the shared headers. It doesn't matter if some of them aren't shared (unless
|
||||
// there are name-space collisions) so we can list all of them to start with:
|
||||
|
||||
#import <Sparkle/SUUpdater.h>
|
||||
|
||||
#import <Sparkle/SUAppcast.h>
|
||||
#import <Sparkle/SUAppcastItem.h>
|
||||
#import <Sparkle/SUVersionComparisonProtocol.h>
|
||||
|
||||
#endif
|
||||
24
cocoalib/Sparkle.framework/Versions/A/Resources/Info.plist
Normal file
24
cocoalib/Sparkle.framework/Versions/A/Resources/Info.plist
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Sparkle</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.andymatuschak.Sparkle</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Sparkle</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.5 Beta 6</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>313</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,7 @@
|
||||
Copyright (c) 2006 Andy Matuschak
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,174 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>ADP2,1</key>
|
||||
<string>Developer Transition Kit</string>
|
||||
<key>MacBook1,1</key>
|
||||
<string>MacBook (Core Duo)</string>
|
||||
<key>MacBook2,1</key>
|
||||
<string>MacBook (Core 2 Duo)</string>
|
||||
<key>MacBook4,1</key>
|
||||
<string>MacBook (Core 2 Duo Feb 2008)</string>
|
||||
<key>MacBookAir1,1</key>
|
||||
<string>MacBook Air (January 2008)</string>
|
||||
<key>MacBookPro1,1</key>
|
||||
<string>MacBook Pro Core Duo (15-inch)</string>
|
||||
<key>MacBookPro1,2</key>
|
||||
<string>MacBook Pro Core Duo (17-inch)</string>
|
||||
<key>MacBookPro2,1</key>
|
||||
<string>MacBook Pro Core 2 Duo (17-inch)</string>
|
||||
<key>MacBookPro2,2</key>
|
||||
<string>MacBook Pro Core 2 Duo (15-inch)</string>
|
||||
<key>MacBookPro3,1</key>
|
||||
<string>MacBook Pro Core 2 Duo (15-inch LED, Core 2 Duo)</string>
|
||||
<key>MacBookPro3,2</key>
|
||||
<string>MacBook Pro Core 2 Duo (17-inch HD, Core 2 Duo)</string>
|
||||
<key>MacBookPro4,1</key>
|
||||
<string>MacBook Pro (Core 2 Duo Feb 2008)</string>
|
||||
<key>MacPro1,1</key>
|
||||
<string>Mac Pro (four-core)</string>
|
||||
<key>MacPro2,1</key>
|
||||
<string>Mac Pro (eight-core)</string>
|
||||
<key>MacPro3,1</key>
|
||||
<string>Mac Pro (January 2008 4- or 8- core "Harpertown")</string>
|
||||
<key>Macmini1,1</key>
|
||||
<string>Mac Mini (Core Solo/Duo)</string>
|
||||
<key>PowerBook1,1</key>
|
||||
<string>PowerBook G3</string>
|
||||
<key>PowerBook2,1</key>
|
||||
<string>iBook G3</string>
|
||||
<key>PowerBook2,2</key>
|
||||
<string>iBook G3 (FireWire)</string>
|
||||
<key>PowerBook2,3</key>
|
||||
<string>iBook G3</string>
|
||||
<key>PowerBook2,4</key>
|
||||
<string>iBook G3</string>
|
||||
<key>PowerBook3,1</key>
|
||||
<string>PowerBook G3 (FireWire)</string>
|
||||
<key>PowerBook3,2</key>
|
||||
<string>PowerBook G4</string>
|
||||
<key>PowerBook3,3</key>
|
||||
<string>PowerBook G4 (Gigabit Ethernet)</string>
|
||||
<key>PowerBook3,4</key>
|
||||
<string>PowerBook G4 (DVI)</string>
|
||||
<key>PowerBook3,5</key>
|
||||
<string>PowerBook G4 (1GHz / 867MHz)</string>
|
||||
<key>PowerBook4,1</key>
|
||||
<string>iBook G3 (Dual USB, Late 2001)</string>
|
||||
<key>PowerBook4,2</key>
|
||||
<string>iBook G3 (16MB VRAM)</string>
|
||||
<key>PowerBook4,3</key>
|
||||
<string>iBook G3 Opaque 16MB VRAM, 32MB VRAM, Early 2003)</string>
|
||||
<key>PowerBook5,1</key>
|
||||
<string>PowerBook G4 (17 inch)</string>
|
||||
<key>PowerBook5,2</key>
|
||||
<string>PowerBook G4 (15 inch FW 800)</string>
|
||||
<key>PowerBook5,3</key>
|
||||
<string>PowerBook G4 (17-inch 1.33GHz)</string>
|
||||
<key>PowerBook5,4</key>
|
||||
<string>PowerBook G4 (15 inch 1.5/1.33GHz)</string>
|
||||
<key>PowerBook5,5</key>
|
||||
<string>PowerBook G4 (17-inch 1.5GHz)</string>
|
||||
<key>PowerBook5,6</key>
|
||||
<string>PowerBook G4 (15 inch 1.67GHz/1.5GHz)</string>
|
||||
<key>PowerBook5,7</key>
|
||||
<string>PowerBook G4 (17-inch 1.67GHz)</string>
|
||||
<key>PowerBook5,8</key>
|
||||
<string>PowerBook G4 (Double layer SD, 15 inch)</string>
|
||||
<key>PowerBook5,9</key>
|
||||
<string>PowerBook G4 (Double layer SD, 17 inch)</string>
|
||||
<key>PowerBook6,1</key>
|
||||
<string>PowerBook G4 (12 inch)</string>
|
||||
<key>PowerBook6,2</key>
|
||||
<string>PowerBook G4 (12 inch, DVI)</string>
|
||||
<key>PowerBook6,3</key>
|
||||
<string>iBook G4</string>
|
||||
<key>PowerBook6,4</key>
|
||||
<string>PowerBook G4 (12 inch 1.33GHz)</string>
|
||||
<key>PowerBook6,5</key>
|
||||
<string>iBook G4 (Early-Late 2004)</string>
|
||||
<key>PowerBook6,7</key>
|
||||
<string>iBook G4 (Mid 2005)</string>
|
||||
<key>PowerBook6,8</key>
|
||||
<string>PowerBook G4 (12 inch 1.5GHz)</string>
|
||||
<key>PowerMac1,1</key>
|
||||
<string>Power Macintosh G3 (Blue & White)</string>
|
||||
<key>PowerMac1,2</key>
|
||||
<string>Power Macintosh G4 (PCI Graphics)</string>
|
||||
<key>PowerMac10,1</key>
|
||||
<string>Mac Mini G4</string>
|
||||
<key>PowerMac10,2</key>
|
||||
<string>Mac Mini (Late 2005)</string>
|
||||
<key>PowerMac11,2</key>
|
||||
<string>Power Macintosh G5 (Late 2005)</string>
|
||||
<key>PowerMac12,1</key>
|
||||
<string>iMac G5 (iSight)</string>
|
||||
<key>PowerMac2,1</key>
|
||||
<string>iMac G3 (Slot-loading CD-ROM)</string>
|
||||
<key>PowerMac2,2</key>
|
||||
<string>iMac G3 (Summer 2000)</string>
|
||||
<key>PowerMac3,1</key>
|
||||
<string>Power Macintosh G4 (AGP Graphics)</string>
|
||||
<key>PowerMac3,2</key>
|
||||
<string>Power Macintosh G4 (AGP Graphics)</string>
|
||||
<key>PowerMac3,3</key>
|
||||
<string>Power Macintosh G4 (Gigabit Ethernet)</string>
|
||||
<key>PowerMac3,4</key>
|
||||
<string>Power Macintosh G4 (Digital Audio)</string>
|
||||
<key>PowerMac3,5</key>
|
||||
<string>Power Macintosh G4 (Quick Silver)</string>
|
||||
<key>PowerMac3,6</key>
|
||||
<string>Power Macintosh G4 (Mirrored Drive Door)</string>
|
||||
<key>PowerMac4,1</key>
|
||||
<string>iMac G3 (Early/Summer 2001)</string>
|
||||
<key>PowerMac4,2</key>
|
||||
<string>iMac G4 (Flat Panel)</string>
|
||||
<key>PowerMac4,4</key>
|
||||
<string>eMac</string>
|
||||
<key>PowerMac4,5</key>
|
||||
<string>iMac G4 (17-inch Flat Panel)</string>
|
||||
<key>PowerMac5,1</key>
|
||||
<string>Power Macintosh G4 Cube</string>
|
||||
<key>PowerMac6,1</key>
|
||||
<string>iMac G4 (USB 2.0)</string>
|
||||
<key>PowerMac6,3</key>
|
||||
<string>iMac G4 (20-inch Flat Panel)</string>
|
||||
<key>PowerMac6,4</key>
|
||||
<string>eMac (USB 2.0, 2005)</string>
|
||||
<key>PowerMac7,2</key>
|
||||
<string>Power Macintosh G5</string>
|
||||
<key>PowerMac7,3</key>
|
||||
<string>Power Macintosh G5</string>
|
||||
<key>PowerMac8,1</key>
|
||||
<string>iMac G5</string>
|
||||
<key>PowerMac8,2</key>
|
||||
<string>iMac G5 (Ambient Light Sensor)</string>
|
||||
<key>PowerMac9,1</key>
|
||||
<string>Power Macintosh G5 (Late 2005)</string>
|
||||
<key>RackMac1,1</key>
|
||||
<string>Xserve G4</string>
|
||||
<key>RackMac1,2</key>
|
||||
<string>Xserve G4 (slot-loading, cluster node)</string>
|
||||
<key>RackMac3,1</key>
|
||||
<string>Xserve G5</string>
|
||||
<key>Xserve1,1</key>
|
||||
<string>Xserve (Intel Xeon)</string>
|
||||
<key>Xserve2,1</key>
|
||||
<string>Xserve (January 2008 quad-core)</string>
|
||||
<key>iMac1,1</key>
|
||||
<string>iMac G3 (Rev A-D)</string>
|
||||
<key>iMac4,1</key>
|
||||
<string>iMac (Core Duo)</string>
|
||||
<key>iMac4,2</key>
|
||||
<string>iMac for Education (17-inch, Core Duo)</string>
|
||||
<key>iMac5,1</key>
|
||||
<string>iMac (Core 2 Duo, 17 or 20 inch, SuperDrive)</string>
|
||||
<key>iMac5,2</key>
|
||||
<string>iMac (Core 2 Duo, 17 inch, Combo Drive)</string>
|
||||
<key>iMac6,1</key>
|
||||
<string>iMac (Core 2 Duo, 24 inch, SuperDrive)</string>
|
||||
<key>iMac8,1</key>
|
||||
<string>iMac (April 2008)</string>
|
||||
</dict>
|
||||
</plist>
|
||||
56
cocoalib/Sparkle.framework/Versions/A/Resources/SUStatus.nib/classes.nib
generated
Normal file
56
cocoalib/Sparkle.framework/Versions/A/Resources/SUStatus.nib/classes.nib
generated
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IBClasses</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CLASS</key>
|
||||
<string>SUWindowController</string>
|
||||
<key>LANGUAGE</key>
|
||||
<string>ObjC</string>
|
||||
<key>SUPERCLASS</key>
|
||||
<string>NSWindowController</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CLASS</key>
|
||||
<string>NSApplication</string>
|
||||
<key>LANGUAGE</key>
|
||||
<string>ObjC</string>
|
||||
<key>SUPERCLASS</key>
|
||||
<string>NSResponder</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CLASS</key>
|
||||
<string>FirstResponder</string>
|
||||
<key>LANGUAGE</key>
|
||||
<string>ObjC</string>
|
||||
<key>SUPERCLASS</key>
|
||||
<string>NSObject</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CLASS</key>
|
||||
<string>NSObject</string>
|
||||
<key>LANGUAGE</key>
|
||||
<string>ObjC</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CLASS</key>
|
||||
<string>SUStatusController</string>
|
||||
<key>LANGUAGE</key>
|
||||
<string>ObjC</string>
|
||||
<key>OUTLETS</key>
|
||||
<dict>
|
||||
<key>actionButton</key>
|
||||
<string>NSButton</string>
|
||||
<key>progressBar</key>
|
||||
<string>NSProgressIndicator</string>
|
||||
</dict>
|
||||
<key>SUPERCLASS</key>
|
||||
<string>SUWindowController</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>IBVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
20
cocoalib/Sparkle.framework/Versions/A/Resources/SUStatus.nib/info.nib
generated
Normal file
20
cocoalib/Sparkle.framework/Versions/A/Resources/SUStatus.nib/info.nib
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IBFramework Version</key>
|
||||
<string>670</string>
|
||||
<key>IBLastKnownRelativeProjectPath</key>
|
||||
<string>Sparkle.xcodeproj</string>
|
||||
<key>IBOldestOS</key>
|
||||
<integer>5</integer>
|
||||
<key>IBOpenObjects</key>
|
||||
<array>
|
||||
<integer>6</integer>
|
||||
</array>
|
||||
<key>IBSystem Version</key>
|
||||
<string>10A96</string>
|
||||
<key>targetFramework</key>
|
||||
<string>IBCocoaFramework</string>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
cocoalib/Sparkle.framework/Versions/A/Resources/SUStatus.nib/keyedobjects.nib
generated
Normal file
BIN
cocoalib/Sparkle.framework/Versions/A/Resources/SUStatus.nib/keyedobjects.nib
generated
Normal file
Binary file not shown.
50
cocoalib/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib/classes.nib
generated
Normal file
50
cocoalib/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib/classes.nib
generated
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IBClasses</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CLASS</key>
|
||||
<string>SUWindowController</string>
|
||||
<key>LANGUAGE</key>
|
||||
<string>ObjC</string>
|
||||
<key>SUPERCLASS</key>
|
||||
<string>NSWindowController</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>ACTIONS</key>
|
||||
<dict>
|
||||
<key>doNotInstall</key>
|
||||
<string>id</string>
|
||||
<key>installLater</key>
|
||||
<string>id</string>
|
||||
<key>installNow</key>
|
||||
<string>id</string>
|
||||
</dict>
|
||||
<key>CLASS</key>
|
||||
<string>SUAutomaticUpdateAlert</string>
|
||||
<key>LANGUAGE</key>
|
||||
<string>ObjC</string>
|
||||
<key>SUPERCLASS</key>
|
||||
<string>SUWindowController</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CLASS</key>
|
||||
<string>FirstResponder</string>
|
||||
<key>LANGUAGE</key>
|
||||
<string>ObjC</string>
|
||||
<key>SUPERCLASS</key>
|
||||
<string>NSObject</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CLASS</key>
|
||||
<string>NSObject</string>
|
||||
<key>LANGUAGE</key>
|
||||
<string>ObjC</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>IBVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
20
cocoalib/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib/info.nib
generated
Normal file
20
cocoalib/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib/info.nib
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IBFramework Version</key>
|
||||
<string>658</string>
|
||||
<key>IBLastKnownRelativeProjectPath</key>
|
||||
<string>../Sparkle.xcodeproj</string>
|
||||
<key>IBOldestOS</key>
|
||||
<integer>5</integer>
|
||||
<key>IBOpenObjects</key>
|
||||
<array>
|
||||
<integer>6</integer>
|
||||
</array>
|
||||
<key>IBSystem Version</key>
|
||||
<string>9C7010</string>
|
||||
<key>targetFramework</key>
|
||||
<string>IBCocoaFramework</string>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
cocoalib/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib
generated
Normal file
BIN
cocoalib/Sparkle.framework/Versions/A/Resources/en.lproj/SUAutomaticUpdateAlert.nib/keyedobjects.nib
generated
Normal file
Binary file not shown.
67
cocoalib/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib/classes.nib
generated
Normal file
67
cocoalib/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib/classes.nib
generated
Normal file
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IBClasses</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CLASS</key>
|
||||
<string>SUWindowController</string>
|
||||
<key>LANGUAGE</key>
|
||||
<string>ObjC</string>
|
||||
<key>SUPERCLASS</key>
|
||||
<string>NSWindowController</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CLASS</key>
|
||||
<string>NSApplication</string>
|
||||
<key>LANGUAGE</key>
|
||||
<string>ObjC</string>
|
||||
<key>SUPERCLASS</key>
|
||||
<string>NSResponder</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>ACTIONS</key>
|
||||
<dict>
|
||||
<key>installUpdate</key>
|
||||
<string>id</string>
|
||||
<key>remindMeLater</key>
|
||||
<string>id</string>
|
||||
<key>skipThisVersion</key>
|
||||
<string>id</string>
|
||||
</dict>
|
||||
<key>CLASS</key>
|
||||
<string>SUUpdateAlert</string>
|
||||
<key>LANGUAGE</key>
|
||||
<string>ObjC</string>
|
||||
<key>OUTLETS</key>
|
||||
<dict>
|
||||
<key>delegate</key>
|
||||
<string>id</string>
|
||||
<key>description</key>
|
||||
<string>NSTextField</string>
|
||||
<key>releaseNotesView</key>
|
||||
<string>WebView</string>
|
||||
</dict>
|
||||
<key>SUPERCLASS</key>
|
||||
<string>SUWindowController</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CLASS</key>
|
||||
<string>FirstResponder</string>
|
||||
<key>LANGUAGE</key>
|
||||
<string>ObjC</string>
|
||||
<key>SUPERCLASS</key>
|
||||
<string>NSObject</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CLASS</key>
|
||||
<string>NSObject</string>
|
||||
<key>LANGUAGE</key>
|
||||
<string>ObjC</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>IBVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
20
cocoalib/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib/info.nib
generated
Normal file
20
cocoalib/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib/info.nib
generated
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IBFramework Version</key>
|
||||
<string>670</string>
|
||||
<key>IBLastKnownRelativeProjectPath</key>
|
||||
<string>../Sparkle.xcodeproj</string>
|
||||
<key>IBOldestOS</key>
|
||||
<integer>5</integer>
|
||||
<key>IBOpenObjects</key>
|
||||
<array>
|
||||
<integer>18</integer>
|
||||
</array>
|
||||
<key>IBSystem Version</key>
|
||||
<string>10A96</string>
|
||||
<key>targetFramework</key>
|
||||
<string>IBCocoaFramework</string>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
cocoalib/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib/keyedobjects.nib
generated
Normal file
BIN
cocoalib/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdateAlert.nib/keyedobjects.nib
generated
Normal file
Binary file not shown.
59
cocoalib/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib/classes.nib
generated
Normal file
59
cocoalib/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib/classes.nib
generated
Normal file
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IBClasses</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CLASS</key>
|
||||
<string>SUWindowController</string>
|
||||
<key>LANGUAGE</key>
|
||||
<string>ObjC</string>
|
||||
<key>SUPERCLASS</key>
|
||||
<string>NSWindowController</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>ACTIONS</key>
|
||||
<dict>
|
||||
<key>finishPrompt</key>
|
||||
<string>id</string>
|
||||
<key>toggleMoreInfo</key>
|
||||
<string>id</string>
|
||||
</dict>
|
||||
<key>CLASS</key>
|
||||
<string>SUUpdatePermissionPrompt</string>
|
||||
<key>LANGUAGE</key>
|
||||
<string>ObjC</string>
|
||||
<key>OUTLETS</key>
|
||||
<dict>
|
||||
<key>delegate</key>
|
||||
<string>id</string>
|
||||
<key>descriptionTextField</key>
|
||||
<string>NSTextField</string>
|
||||
<key>moreInfoButton</key>
|
||||
<string>NSButton</string>
|
||||
<key>moreInfoView</key>
|
||||
<string>NSView</string>
|
||||
</dict>
|
||||
<key>SUPERCLASS</key>
|
||||
<string>SUWindowController</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CLASS</key>
|
||||
<string>FirstResponder</string>
|
||||
<key>LANGUAGE</key>
|
||||
<string>ObjC</string>
|
||||
<key>SUPERCLASS</key>
|
||||
<string>NSObject</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CLASS</key>
|
||||
<string>NSObject</string>
|
||||
<key>LANGUAGE</key>
|
||||
<string>ObjC</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>IBVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
21
cocoalib/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib/info.nib
generated
Normal file
21
cocoalib/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib/info.nib
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IBFramework Version</key>
|
||||
<string>670</string>
|
||||
<key>IBLastKnownRelativeProjectPath</key>
|
||||
<string>../Sparkle.xcodeproj</string>
|
||||
<key>IBOldestOS</key>
|
||||
<integer>5</integer>
|
||||
<key>IBOpenObjects</key>
|
||||
<array>
|
||||
<integer>6</integer>
|
||||
<integer>41</integer>
|
||||
</array>
|
||||
<key>IBSystem Version</key>
|
||||
<string>10A96</string>
|
||||
<key>targetFramework</key>
|
||||
<string>IBCocoaFramework</string>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
cocoalib/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib
generated
Normal file
BIN
cocoalib/Sparkle.framework/Versions/A/Resources/en.lproj/SUUpdatePermissionPrompt.nib/keyedobjects.nib
generated
Normal file
Binary file not shown.
Binary file not shown.
BIN
cocoalib/Sparkle.framework/Versions/A/Resources/relaunch
Executable file
BIN
cocoalib/Sparkle.framework/Versions/A/Resources/relaunch
Executable file
Binary file not shown.
BIN
cocoalib/Sparkle.framework/Versions/A/Sparkle
Executable file
BIN
cocoalib/Sparkle.framework/Versions/A/Sparkle
Executable file
Binary file not shown.
1
cocoalib/Sparkle.framework/Versions/Current
Symbolic link
1
cocoalib/Sparkle.framework/Versions/Current
Symbolic link
@@ -0,0 +1 @@
|
||||
A
|
||||
36
cocoalib/Utils.h
Normal file
36
cocoalib/Utils.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
//Useful shortcuts
|
||||
#define i2n(i) [NSNumber numberWithInteger:i]
|
||||
#define n2i(n) [n integerValue]
|
||||
#define b2n(b) [NSNumber numberWithBool:b]
|
||||
#define n2b(n) [n boolValue]
|
||||
#if __LP64__
|
||||
#define f2n(d) [NSNumber numberWithDouble:d]
|
||||
#define n2f(n) [n doubleValue]
|
||||
#else
|
||||
#define f2n(f) [NSNumber numberWithFloat:f]
|
||||
#define n2f(n) [n floatValue]
|
||||
#endif
|
||||
#define p2a(p) [Utils indexPath2Array:p]
|
||||
#define a2p(a) [Utils array2IndexPath:a]
|
||||
#define fmt(x,...) [NSString stringWithFormat:x,__VA_ARGS__]
|
||||
|
||||
@interface Utils : NSObject
|
||||
+ (NSArray *)indexSet2Array:(NSIndexSet *)aIndexSet;
|
||||
+ (NSIndexSet *)array2IndexSet:(NSArray *)numberArray;
|
||||
+ (NSArray *)indexPath2Array:(NSIndexPath *)aIndexPath;
|
||||
+ (NSIndexPath *)array2IndexPath:(NSArray *)indexArray;
|
||||
+ (NSString *)indexPath2String:(NSIndexPath *)aIndexPath;
|
||||
+ (NSIndexPath *)string2IndexPath:(NSString *)aString;
|
||||
@end
|
||||
|
||||
void replacePlaceholderInView(NSView *placeholder, NSView *replaceWith);
|
||||
92
cocoalib/Utils.m
Normal file
92
cocoalib/Utils.m
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import "Utils.h"
|
||||
#import <CoreServices/CoreServices.h>
|
||||
|
||||
@implementation Utils
|
||||
//This is to pass index sets to python as arrays (so it can be converted to native lists)
|
||||
+ (NSArray *)indexSet2Array:(NSIndexSet *)aIndexSet
|
||||
{
|
||||
NSMutableArray *r = [NSMutableArray array];
|
||||
NSInteger i = [aIndexSet firstIndex];
|
||||
while (i != NSNotFound)
|
||||
{
|
||||
[r addObject:[NSNumber numberWithInteger:i]];
|
||||
i = [aIndexSet indexGreaterThanIndex:i];
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// numberArray is an array of NSNumber
|
||||
+ (NSIndexSet *)array2IndexSet:(NSArray *)numberArray
|
||||
{
|
||||
NSMutableIndexSet *set = [NSMutableIndexSet indexSet];
|
||||
NSEnumerator *e = [numberArray objectEnumerator];
|
||||
NSNumber *n;
|
||||
while (n = [e nextObject])
|
||||
[set addIndex:n2i(n)];
|
||||
return set;
|
||||
}
|
||||
|
||||
//Changes an NSIndexPath into an NSArray
|
||||
+ (NSArray *)indexPath2Array:(NSIndexPath *)aIndexPath
|
||||
{
|
||||
NSMutableArray *r = [NSMutableArray array];
|
||||
if (!aIndexPath)
|
||||
return r;
|
||||
for (int i=0;i<[aIndexPath length];i++)
|
||||
[r addObject:i2n([aIndexPath indexAtPosition:i])];
|
||||
return r;
|
||||
}
|
||||
|
||||
// Changes a NSArray of numbers into a NSIndexPath
|
||||
// indexArray must have at least one item
|
||||
+ (NSIndexPath *)array2IndexPath:(NSArray *)indexArray
|
||||
{
|
||||
if (![indexArray count])
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
NSEnumerator *e = [indexArray objectEnumerator];
|
||||
NSNumber *n = [e nextObject];
|
||||
NSIndexPath *ip = [NSIndexPath indexPathWithIndex:n2i(n)];
|
||||
while (n = [e nextObject])
|
||||
ip = [ip indexPathByAddingIndex:n2i(n)];
|
||||
return ip;
|
||||
}
|
||||
|
||||
+ (NSString *)indexPath2String:(NSIndexPath *)aIndexPath
|
||||
{
|
||||
NSMutableArray *components = [NSMutableArray array];
|
||||
for (int i=0; i<[aIndexPath length]; i++)
|
||||
[components addObject:i2n([aIndexPath indexAtPosition:i])];
|
||||
return [components componentsJoinedByString:@"_"];
|
||||
}
|
||||
|
||||
+ (NSIndexPath *)string2IndexPath:(NSString *)aString
|
||||
{
|
||||
if (aString == nil)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
NSArray *components = [aString componentsSeparatedByString:@"_"];
|
||||
NSMutableArray *indexes = [NSMutableArray array];
|
||||
for (int i=0; i<[components count]; i++)
|
||||
[indexes addObject:i2n([[components objectAtIndex:i] intValue])];
|
||||
return [Utils array2IndexPath:indexes];
|
||||
}
|
||||
@end
|
||||
|
||||
void replacePlaceholderInView(NSView *placeholder, NSView *replaceWith)
|
||||
{
|
||||
NSView *parent = [placeholder superview];
|
||||
[replaceWith setFrame:[placeholder frame]];
|
||||
[replaceWith setAutoresizingMask:[placeholder autoresizingMask]];
|
||||
[parent replaceSubview:placeholder with:replaceWith];
|
||||
}
|
||||
26
cocoalib/ValueTransformers.h
Normal file
26
cocoalib/ValueTransformers.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
@interface VTIsIntIn : NSValueTransformer
|
||||
{
|
||||
NSIndexSet *ints;
|
||||
BOOL reverse;
|
||||
}
|
||||
- (id)initWithValues:(NSIndexSet *)values;
|
||||
- (id)initWithValues:(NSIndexSet *)values reverse:(BOOL)doReverse;
|
||||
@end
|
||||
|
||||
@interface HSVTAdd : NSValueTransformer
|
||||
{
|
||||
int toAdd;
|
||||
}
|
||||
- (id)initWithValue:(int)value;
|
||||
@end
|
||||
79
cocoalib/ValueTransformers.m
Normal file
79
cocoalib/ValueTransformers.m
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import "ValueTransformers.h"
|
||||
#import "Utils.h"
|
||||
|
||||
@implementation VTIsIntIn
|
||||
- (id)initWithValues:(NSIndexSet *)values
|
||||
{
|
||||
return [self initWithValues:values reverse:NO];
|
||||
}
|
||||
|
||||
- (id)initWithValues:(NSIndexSet *)values reverse:(BOOL)doReverse
|
||||
{
|
||||
self = [super init];
|
||||
ints = values;
|
||||
[ints retain];
|
||||
reverse = doReverse;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[ints release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
+ (Class)transformedValueClass
|
||||
{
|
||||
return [NSNumber class]; //Boolean
|
||||
}
|
||||
|
||||
+ (BOOL)allowsReverseTransformation
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (id)transformedValue:(id)value
|
||||
{
|
||||
if (value == nil)
|
||||
return nil;
|
||||
NSNumber *i = value;
|
||||
BOOL r = [ints containsIndex:[i intValue]];
|
||||
if (reverse)
|
||||
r = !r;
|
||||
return [NSNumber numberWithBool:r];
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation HSVTAdd
|
||||
- (id)initWithValue:(int)value
|
||||
{
|
||||
self = [super init];
|
||||
toAdd = value;
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (Class)transformedValueClass
|
||||
{
|
||||
return [NSNumber class];
|
||||
}
|
||||
|
||||
+ (BOOL)allowsReverseTransformation
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (id)transformedValue:(id)value
|
||||
{
|
||||
if (value == nil)
|
||||
return nil;
|
||||
return i2n(n2i(value) + toAdd);
|
||||
}
|
||||
@end
|
||||
14
cocoalib/Worker.h
Normal file
14
cocoalib/Worker.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
//The worker should work in a separate thread or have it's own mechanism to keep the GUI updated as ProgressController
|
||||
//provides none.
|
||||
@protocol Worker
|
||||
// -1: Indeterminate. nil: Not working. 0-100: Progressing
|
||||
- (NSNumber *)getJobProgress;
|
||||
- (NSString *)getJobDesc;
|
||||
- (void)cancelJob;
|
||||
/* This might seem a little stupid, but it's the simplest way to get a **sync** call to the python
|
||||
side after a job. Because the python-side app is not an NSObject subclass, it can't listen to
|
||||
notifications. */
|
||||
- (void)jobCompleted:(NSString *)jobid;
|
||||
@end
|
||||
32
cocoalib/cocoa/CocoaProxy.h
Normal file
32
cocoalib/cocoa/CocoaProxy.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface CocoaProxy : NSObject
|
||||
{
|
||||
NSAutoreleasePool *currentPool;
|
||||
}
|
||||
- (void)openPath:(NSString *)path;
|
||||
- (void)openURL:(NSString *)url;
|
||||
- (void)revealPath:(NSString *)path;
|
||||
- (NSString *)getUTI:(NSString *)path;
|
||||
- (BOOL)type:(NSString *)type conformsToType:(NSString *)refType;
|
||||
- (NSString *)getAppdataPath;
|
||||
- (NSString *)getCachePath;
|
||||
- (NSString *)getResourcePath;
|
||||
- (NSString *)systemLang;
|
||||
- (NSString *)systemShortDateFormat;
|
||||
- (NSString *)systemNumberDecimalSeparator;
|
||||
- (NSString *)systemNumberGroupingSeparator;
|
||||
- (NSString *)systemCurrency;
|
||||
- (NSString *)bundleIdentifier;
|
||||
- (NSString *)appVersion;
|
||||
- (NSString *)osxVersion;
|
||||
- (void)postNotification:(NSString *)name userInfo:(NSDictionary *)userInfo;
|
||||
- (id)prefValue:(NSString *)prefname;
|
||||
- (void)setPrefValue:(NSString *)prefname value:(id)value;
|
||||
- (id)prefValue:(NSString *)prefname inDomain:(NSString *)domain;
|
||||
- (NSString *)url2path:(NSString *)url;
|
||||
- (void)createPool;
|
||||
- (void)destroyPool;
|
||||
- (void)reportCrash:(NSString *)crashReport;
|
||||
- (void)log:(NSString *)s;
|
||||
@end
|
||||
155
cocoalib/cocoa/CocoaProxy.m
Normal file
155
cocoalib/cocoa/CocoaProxy.m
Normal file
@@ -0,0 +1,155 @@
|
||||
#import "CocoaProxy.h"
|
||||
#import <CoreServices/CoreServices.h>
|
||||
#import "HSErrorReportWindow.h"
|
||||
|
||||
@implementation CocoaProxy
|
||||
- (void)openPath:(NSString *)path
|
||||
{
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL fileURLWithPath:path isDirectory:NO]];
|
||||
}
|
||||
|
||||
- (void)openURL:(NSString *)url
|
||||
{
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:url]];
|
||||
}
|
||||
|
||||
- (void)revealPath:(NSString *)path
|
||||
{
|
||||
[[NSWorkspace sharedWorkspace] selectFile:path inFileViewerRootedAtPath:@""];
|
||||
}
|
||||
|
||||
- (NSString *)getUTI:(NSString *)path
|
||||
{
|
||||
NSError *error;
|
||||
return [[NSWorkspace sharedWorkspace] typeOfFile:path error:&error];
|
||||
}
|
||||
|
||||
- (BOOL)type:(NSString *)type conformsToType:(NSString *)refType
|
||||
{
|
||||
return [[NSWorkspace sharedWorkspace] type:type conformsToType:refType];
|
||||
}
|
||||
|
||||
- (NSString *)getAppdataPath
|
||||
{
|
||||
return [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) objectAtIndex:0];
|
||||
}
|
||||
- (NSString *)getCachePath
|
||||
{
|
||||
return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
|
||||
}
|
||||
|
||||
- (NSString *)getResourcePath
|
||||
{
|
||||
return [[[NSBundle mainBundle] resourceURL] path];
|
||||
}
|
||||
|
||||
- (NSString *)systemLang
|
||||
{
|
||||
return [[NSBundle preferredLocalizationsFromArray:[[NSBundle mainBundle] localizations]] objectAtIndex:0];
|
||||
}
|
||||
|
||||
- (NSString *)systemShortDateFormat
|
||||
{
|
||||
[NSDateFormatter setDefaultFormatterBehavior:NSDateFormatterBehavior10_4];
|
||||
NSDateFormatter *f = [[NSDateFormatter alloc] init];
|
||||
[f setDateStyle:NSDateFormatterShortStyle];
|
||||
[f setTimeStyle:NSDateFormatterNoStyle];
|
||||
NSString *result = [[f dateFormat] retain];
|
||||
[f release];
|
||||
return [result autorelease];
|
||||
}
|
||||
|
||||
- (NSString *)systemNumberDecimalSeparator
|
||||
{
|
||||
[NSNumberFormatter setDefaultFormatterBehavior:NSNumberFormatterBehavior10_4];
|
||||
NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
|
||||
NSString *result = [[f decimalSeparator] retain];
|
||||
[f release];
|
||||
return [result autorelease];
|
||||
}
|
||||
|
||||
- (NSString *)systemNumberGroupingSeparator
|
||||
{
|
||||
[NSNumberFormatter setDefaultFormatterBehavior:NSNumberFormatterBehavior10_4];
|
||||
NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
|
||||
NSString *result = [[f groupingSeparator] retain];
|
||||
[f release];
|
||||
return [result autorelease];
|
||||
}
|
||||
|
||||
- (NSString *)systemCurrency
|
||||
{
|
||||
return [[NSLocale currentLocale] objectForKey:NSLocaleCurrencyCode];
|
||||
}
|
||||
|
||||
- (NSString *)bundleIdentifier
|
||||
{
|
||||
return [[NSBundle mainBundle] bundleIdentifier];
|
||||
}
|
||||
|
||||
- (NSString *)appVersion
|
||||
{
|
||||
return [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
|
||||
}
|
||||
|
||||
- (NSString *)osxVersion
|
||||
{
|
||||
SInt32 major, minor, bugfix;
|
||||
Gestalt(gestaltSystemVersionMajor, &major);
|
||||
Gestalt(gestaltSystemVersionMinor, &minor);
|
||||
Gestalt(gestaltSystemVersionBugFix, &bugfix);
|
||||
return [NSString stringWithFormat:@"%d.%d.%d", major, minor, bugfix];
|
||||
}
|
||||
|
||||
- (void)postNotification:(NSString *)name userInfo:(NSDictionary *)userInfo
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:name object:nil userInfo:userInfo];
|
||||
}
|
||||
|
||||
- (id)prefValue:(NSString *)prefname
|
||||
{
|
||||
return [[NSUserDefaults standardUserDefaults] objectForKey:prefname];
|
||||
}
|
||||
|
||||
- (void)setPrefValue:(NSString *)prefname value:(id)value
|
||||
{
|
||||
[[NSUserDefaults standardUserDefaults] setObject:value forKey:prefname];
|
||||
}
|
||||
|
||||
- (id)prefValue:(NSString *)prefname inDomain:(NSString *)domain
|
||||
{
|
||||
NSDictionary *dict = [[NSUserDefaults standardUserDefaults] persistentDomainForName:domain];
|
||||
return [dict objectForKey:prefname];
|
||||
}
|
||||
|
||||
// Changes a file:/// path into a normal path
|
||||
- (NSString *)url2path:(NSString *)url
|
||||
{
|
||||
NSURL *u = [NSURL URLWithString:url];
|
||||
return [u path];
|
||||
}
|
||||
|
||||
// Create a pool for use into a separate thread.
|
||||
- (void)createPool
|
||||
{
|
||||
[self destroyPool];
|
||||
currentPool = [[NSAutoreleasePool alloc] init];
|
||||
}
|
||||
- (void)destroyPool
|
||||
{
|
||||
if (currentPool != nil) {
|
||||
[currentPool release];
|
||||
currentPool = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reportCrash:(NSString *)crashReport
|
||||
{
|
||||
[HSErrorReportWindow showErrorReportWithContent:crashReport];
|
||||
}
|
||||
|
||||
- (void)log:(NSString *)s
|
||||
{
|
||||
NSLog(@"%@", s);
|
||||
}
|
||||
@end
|
||||
117
cocoalib/cocoa/__init__.py
Normal file
117
cocoalib/cocoa/__init__.py
Normal file
@@ -0,0 +1,117 @@
|
||||
# Created By: Virgil Dupras
|
||||
# Created On: 2007-10-06
|
||||
# Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
# which should be included with this package. The terms are also available at
|
||||
# http://www.hardcoded.net/licenses/bsd_license
|
||||
|
||||
import logging
|
||||
import time
|
||||
import traceback
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from .CocoaProxy import CocoaProxy
|
||||
|
||||
proxy = CocoaProxy()
|
||||
|
||||
def autoreleasepool(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
proxy.createPool()
|
||||
try:
|
||||
func(*args, **kwargs)
|
||||
finally:
|
||||
proxy.destroyPool()
|
||||
return wrapper
|
||||
|
||||
def as_fetch(as_list, as_type, step_size=1000):
|
||||
"""When fetching items from a very big list through applescript, the connection with the app
|
||||
will timeout. This function is to circumvent that. 'as_type' is the type of the items in the
|
||||
list (found in appscript.k). If we don't pass it to the 'each' arg of 'count()', it doesn't work.
|
||||
applescript is rather stupid..."""
|
||||
result = []
|
||||
# no timeout. default timeout is 60 secs, and it is reached for libs > 30k songs
|
||||
item_count = as_list.count(each=as_type, timeout=0)
|
||||
steps = item_count // step_size
|
||||
if item_count % step_size:
|
||||
steps += 1
|
||||
logging.info('Fetching %d items in %d steps' % (item_count, steps))
|
||||
# Don't forget that the indexes are 1-based and that the upper limit is included
|
||||
for step in range(steps):
|
||||
begin = step * step_size + 1
|
||||
end = min(item_count, begin + step_size - 1)
|
||||
if end > begin:
|
||||
result += as_list[begin:end](timeout=0)
|
||||
else: # When there is only one item, the stupid fuck gives it directly instead of putting it in a list.
|
||||
result.append(as_list[begin:end](timeout=0))
|
||||
time.sleep(.1)
|
||||
logging.info('%d items fetched' % len(result))
|
||||
return result
|
||||
|
||||
def extract_tb_noline(tb):
|
||||
# Same as traceback.extract_tb(), but without line fetching
|
||||
limit = 100
|
||||
list = []
|
||||
n = 0
|
||||
while tb is not None and (limit is None or n < limit):
|
||||
f = tb.tb_frame
|
||||
lineno = tb.tb_lineno
|
||||
co = f.f_code
|
||||
filename = co.co_filename
|
||||
name = co.co_name
|
||||
list.append((filename, lineno, name, None))
|
||||
tb = tb.tb_next
|
||||
n = n+1
|
||||
return list
|
||||
|
||||
def safe_format_exception(type, value, tb):
|
||||
"""Format exception from type, value and tb and fallback if there's a problem.
|
||||
|
||||
In some cases in threaded exceptions under Cocoa, I get tracebacks targeting pyc files instead
|
||||
of py files, which results in traceback.format_exception() trying to print lines from pyc files
|
||||
and then crashing when trying to interpret that binary data as utf-8. We want a fallback in
|
||||
these cases.
|
||||
"""
|
||||
try:
|
||||
return traceback.format_exception(type, value, tb)
|
||||
except Exception:
|
||||
result = ['Traceback (most recent call last):\n']
|
||||
result.extend(traceback.format_list(extract_tb_noline(tb)))
|
||||
result.extend(traceback.format_exception_only(type, value))
|
||||
return result
|
||||
|
||||
def report_crash(type, value, tb):
|
||||
app_identifier = proxy.bundleIdentifier()
|
||||
app_version = proxy.appVersion()
|
||||
osx_version = proxy.osxVersion()
|
||||
s = "Application Identifier: {}\n".format(app_identifier)
|
||||
s += "Application Version: {}\n".format(app_version)
|
||||
s += "Mac OS X Version: {}\n\n".format(osx_version)
|
||||
s += ''.join(safe_format_exception(type, value, tb))
|
||||
if app_identifier:
|
||||
s += '\nRelevant Console logs:\n\n'
|
||||
p = subprocess.Popen(['grep', app_identifier, '/var/log/system.log'], stdout=subprocess.PIPE)
|
||||
try:
|
||||
s += str(p.communicate()[0], encoding='utf-8')
|
||||
except IndexError:
|
||||
# This can happen if something went wrong with the grep (permission errors?)
|
||||
pass
|
||||
proxy.reportCrash_(s)
|
||||
|
||||
def install_exception_hook():
|
||||
sys.excepthook = report_crash
|
||||
|
||||
class CocoaHandler(logging.Handler):
|
||||
def emit(self, record):
|
||||
proxy.log_(record.getMessage())
|
||||
|
||||
def install_cocoa_logger():
|
||||
logging.getLogger().addHandler(CocoaHandler())
|
||||
|
||||
def patch_threaded_job_performer():
|
||||
# _async_run, under cocoa, has to be run within an autorelease pool to prevent leaks.
|
||||
# You only need this patch is you use one of CocoaProxy's function (which allocate objc
|
||||
# structures) inside a threaded job.
|
||||
from jobprogress.performer import ThreadedJobPerformer
|
||||
ThreadedJobPerformer._async_run = autoreleasepool(ThreadedJobPerformer._async_run)
|
||||
338
cocoalib/cocoa/inter.py
Normal file
338
cocoalib/cocoa/inter.py
Normal file
@@ -0,0 +1,338 @@
|
||||
import logging
|
||||
from objp.util import pyref, dontwrap
|
||||
from . import proxy
|
||||
|
||||
class GUIObjectView:
|
||||
def refresh(self): pass
|
||||
|
||||
class PyGUIObject:
|
||||
def __init__(self, model: pyref):
|
||||
self.model = model
|
||||
self.callback = None
|
||||
|
||||
# This *has* to be called right after initialization.
|
||||
def bindCallback_(self, callback: pyref):
|
||||
self.callback = callback
|
||||
self.model.view = self
|
||||
|
||||
# Call this before the ObjC callback is deallocated to avoid calls to that deallocated instance.
|
||||
def free(self):
|
||||
self.model.view = None
|
||||
self.callback = None
|
||||
|
||||
def modelRef(self) -> pyref:
|
||||
return self.model
|
||||
|
||||
#--- Python -> Cocoa
|
||||
@dontwrap
|
||||
def refresh(self):
|
||||
self.callback.refresh()
|
||||
|
||||
class PyTextField(PyGUIObject):
|
||||
def text(self) -> str:
|
||||
return self.model.text
|
||||
|
||||
def setText_(self, newtext: str):
|
||||
self.model.text = newtext
|
||||
|
||||
|
||||
class SelectableListView(GUIObjectView):
|
||||
def updateSelection(self): pass
|
||||
|
||||
class PySelectableList(PyGUIObject):
|
||||
def items(self) -> list:
|
||||
# Should normally always return strings
|
||||
return self.model[:]
|
||||
|
||||
def selectIndex_(self, index: int):
|
||||
self.model.select(index)
|
||||
|
||||
def selectedIndex(self) -> int:
|
||||
result = self.model.selected_index
|
||||
if result is None:
|
||||
result = -1
|
||||
return result
|
||||
|
||||
def selectedIndexes(self) -> list:
|
||||
return self.model.selected_indexes
|
||||
|
||||
def selectIndexes_(self, indexes: list):
|
||||
self.model.select(indexes)
|
||||
|
||||
def searchByPrefix_(self, prefix: str) -> int:
|
||||
return self.model.search_by_prefix(prefix)
|
||||
|
||||
#--- model --> view
|
||||
@dontwrap
|
||||
def update_selection(self):
|
||||
self.callback.updateSelection()
|
||||
|
||||
class ColumnsView:
|
||||
def restoreColumns(self): pass
|
||||
def setColumn_visible_(self, colname: str, visible: bool): pass
|
||||
|
||||
class PyColumns(PyGUIObject):
|
||||
def columnNamesInOrder(self) -> list:
|
||||
return self.model.colnames
|
||||
|
||||
def columnDisplay_(self, colname: str) -> str:
|
||||
return self.model.column_display(colname)
|
||||
|
||||
def columnIsVisible_(self, colname: str) -> bool:
|
||||
return self.model.column_is_visible(colname)
|
||||
|
||||
def columnWidth_(self, colname: str) -> int:
|
||||
return self.model.column_width(colname)
|
||||
|
||||
def moveColumn_toIndex_(self, colname: str, index: int):
|
||||
self.model.move_column(colname, index)
|
||||
|
||||
def resizeColumn_toWidth_(self, colname: str, newwidth: int):
|
||||
self.model.resize_column(colname, newwidth)
|
||||
|
||||
def setColumn_defaultWidth_(self, colname: str, width: int):
|
||||
self.model.set_default_width(colname, width)
|
||||
|
||||
def menuItems(self) -> list:
|
||||
return self.model.menu_items()
|
||||
|
||||
def toggleMenuItem_(self, index: int) -> bool:
|
||||
return self.model.toggle_menu_item(index)
|
||||
|
||||
def resetToDefaults(self):
|
||||
self.model.reset_to_defaults()
|
||||
|
||||
#--- Python --> Cocoa
|
||||
@dontwrap
|
||||
def restore_columns(self):
|
||||
self.callback.restoreColumns()
|
||||
|
||||
@dontwrap
|
||||
def set_column_visible(self, colname: str, visible):
|
||||
self.callback.setColumn_visible_(colname, visible)
|
||||
|
||||
class OutlineView(GUIObjectView):
|
||||
def startEditing(self): pass
|
||||
def stopEditing(self): pass
|
||||
def updateSelection(self): pass
|
||||
|
||||
class PyOutline(PyGUIObject):
|
||||
def cancelEdits(self):
|
||||
self.model.cancel_edits()
|
||||
|
||||
def canEditProperty_atPath_(self, property: str, path: list) -> bool:
|
||||
node = self.model.get_node(path)
|
||||
assert node is self.model.selected_node
|
||||
return getattr(node, 'can_edit_' + property, False)
|
||||
|
||||
def saveEdits(self):
|
||||
self.model.save_edits()
|
||||
|
||||
def selectedPath(self) -> list:
|
||||
return self.model.selected_path
|
||||
|
||||
def setSelectedPath_(self, path: list):
|
||||
self.model.selected_path = path
|
||||
|
||||
def selectedPaths(self) -> list:
|
||||
return self.model.selected_paths
|
||||
|
||||
def setSelectedPaths_(self, paths: list):
|
||||
self.model.selected_paths = paths
|
||||
|
||||
def property_valueAtPath_(self, property: str, path: list) -> object:
|
||||
try:
|
||||
return getattr(self.model.get_node(path), property)
|
||||
except IndexError:
|
||||
logging.warning("%r doesn't have a node at path %r", self.model, path)
|
||||
return ''
|
||||
|
||||
def setProperty_value_atPath_(self, property: str, value: object, path: list):
|
||||
setattr(self.model.get_node(path), property, value)
|
||||
|
||||
#--- Python -> Cocoa
|
||||
@dontwrap
|
||||
def start_editing(self):
|
||||
self.callback.startEditing()
|
||||
|
||||
@dontwrap
|
||||
def stop_editing(self):
|
||||
self.callback.stopEditing()
|
||||
|
||||
@dontwrap
|
||||
def update_selection(self):
|
||||
self.callback.updateSelection()
|
||||
|
||||
class TableView(GUIObjectView):
|
||||
def showSelectedRow(self): pass
|
||||
def startEditing(self): pass
|
||||
def stopEditing(self): pass
|
||||
def updateSelection(self): pass
|
||||
|
||||
class PyTable(PyGUIObject):
|
||||
#--- Helpers
|
||||
@dontwrap
|
||||
def _getrow(self, row):
|
||||
try:
|
||||
return self.model[row]
|
||||
except IndexError:
|
||||
msg = "Trying to get an out of bounds row ({} / {}) on table {}"
|
||||
logging.warning(msg.format(row, len(self.model), self.model.__class__.__name__))
|
||||
|
||||
#--- Cocoa --> Python
|
||||
def columns(self) -> pyref:
|
||||
return self.model.columns
|
||||
|
||||
def add(self):
|
||||
self.model.add()
|
||||
|
||||
def cancelEdits(self):
|
||||
self.model.cancel_edits()
|
||||
|
||||
def canEditColumn_atRow_(self, column: str, row: int) -> object:
|
||||
return self.model.can_edit_cell(column, row)
|
||||
|
||||
def deleteSelectedRows(self):
|
||||
self.model.delete()
|
||||
|
||||
def numberOfRows(self) -> int:
|
||||
return len(self.model)
|
||||
|
||||
def saveEdits(self):
|
||||
self.model.save_edits()
|
||||
|
||||
def selectRows_(self, rows: list):
|
||||
self.model.select(list(rows))
|
||||
|
||||
def selectedRows(self) -> list:
|
||||
return self.model.selected_indexes
|
||||
|
||||
def selectionAsCSV(self) -> str:
|
||||
return self.model.selection_as_csv()
|
||||
|
||||
def setValue_forColumn_row_(self, value: object, column: str, row: int):
|
||||
# this try except is important for the case while a row is in edition mode and the delete
|
||||
# button is clicked.
|
||||
try:
|
||||
self._getrow(row).set_cell_value(column, value)
|
||||
except AttributeError:
|
||||
msg = "Trying to set an attribute that can't: {} with value {} at row {} on table {}"
|
||||
logging.warning(msg.format(column, value, row, self.model.__class__.__name__))
|
||||
raise
|
||||
|
||||
def sortByColumn_desc_(self, column: str, desc: bool):
|
||||
self.model.sort_by(column, desc=desc)
|
||||
|
||||
def valueForColumn_row_(self, column: str, row: int) -> object:
|
||||
return self._getrow(row).get_cell_value(column)
|
||||
|
||||
#--- Python -> Cocoa
|
||||
@dontwrap
|
||||
def show_selected_row(self):
|
||||
self.callback.showSelectedRow()
|
||||
|
||||
@dontwrap
|
||||
def start_editing(self):
|
||||
self.callback.startEditing()
|
||||
|
||||
@dontwrap
|
||||
def stop_editing(self):
|
||||
self.callback.stopEditing()
|
||||
|
||||
@dontwrap
|
||||
def update_selection(self):
|
||||
self.callback.updateSelection()
|
||||
|
||||
class ProgressWindowView(GUIObjectView):
|
||||
def setProgress_(self, progress: int): pass
|
||||
def showWindow(self): pass
|
||||
def closeWindow(self): pass
|
||||
|
||||
class PyProgressWindow(PyGUIObject):
|
||||
def jobdescTextField(self) -> pyref:
|
||||
return self.model.jobdesc_textfield
|
||||
|
||||
def progressdescTextField(self) -> pyref:
|
||||
return self.model.progressdesc_textfield
|
||||
|
||||
def pulse(self):
|
||||
self.model.pulse()
|
||||
|
||||
def cancel(self):
|
||||
self.model.cancel()
|
||||
|
||||
#--- Python -> Cocoa
|
||||
@dontwrap
|
||||
def set_progress(self, last_progress):
|
||||
self.callback.setProgress_(last_progress)
|
||||
|
||||
@dontwrap
|
||||
def show(self):
|
||||
self.callback.showWindow()
|
||||
|
||||
@dontwrap
|
||||
def close(self):
|
||||
self.callback.closeWindow()
|
||||
|
||||
|
||||
class BaseAppView:
|
||||
def showMessage_(self, msg: str): pass
|
||||
|
||||
class PyBaseApp(PyGUIObject):
|
||||
def appName(self) -> str:
|
||||
return self.model.PROMPT_NAME
|
||||
|
||||
def appLongName(self) -> str:
|
||||
return self.model.NAME
|
||||
|
||||
#--- Python --> Cocoa
|
||||
@dontwrap
|
||||
def get_default(self, key_name):
|
||||
return proxy.prefValue_(key_name)
|
||||
|
||||
@dontwrap
|
||||
def set_default(self, key_name, value):
|
||||
proxy.setPrefValue_value_(key_name, value)
|
||||
|
||||
@dontwrap
|
||||
def open_url(self, url):
|
||||
proxy.openURL_(url)
|
||||
|
||||
@dontwrap
|
||||
def show_message(self, msg):
|
||||
self.callback.showMessage_(msg)
|
||||
|
||||
class FairwareView(BaseAppView):
|
||||
def setupAsRegistered(self): pass
|
||||
def showDemoNagWithPrompt_(self, prompt: str): pass
|
||||
|
||||
class PyFairware(PyBaseApp):
|
||||
FOLLOW_PROTOCOLS = ['HSFairwareProtocol']
|
||||
|
||||
def initialRegistrationSetup(self):
|
||||
self.model.initial_registration_setup()
|
||||
|
||||
def isRegistered(self) -> bool:
|
||||
return self.model.registered
|
||||
|
||||
def setRegisteredCode_andEmail_(self, code: str, email: str) -> bool:
|
||||
return self.model.set_registration(code, email, False)
|
||||
|
||||
def contribute(self):
|
||||
self.model.contribute()
|
||||
|
||||
def buy(self):
|
||||
self.model.buy()
|
||||
|
||||
def aboutFairware(self):
|
||||
self.model.about_fairware()
|
||||
|
||||
#--- Python --> Cocoa
|
||||
@dontwrap
|
||||
def setup_as_registered(self):
|
||||
self.callback.setupAsRegistered()
|
||||
|
||||
@dontwrap
|
||||
def show_demo_nag(self, prompt):
|
||||
self.callback.showDemoNagWithPrompt_(prompt)
|
||||
|
||||
38
cocoalib/controllers/HSColumns.h
Normal file
38
cocoalib/controllers/HSColumns.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <Python.h>
|
||||
#import "HSGUIController.h"
|
||||
#import "PyColumns.h"
|
||||
|
||||
/*
|
||||
This structure is to define constants describing table columns (it's easier to maintain in code
|
||||
than in XIB files).
|
||||
*/
|
||||
typedef struct {
|
||||
NSString *attrname;
|
||||
NSUInteger defaultWidth;
|
||||
NSUInteger minWidth;
|
||||
NSUInteger maxWidth;
|
||||
BOOL sortable;
|
||||
Class cellClass;
|
||||
} HSColumnDef;
|
||||
|
||||
@interface HSColumns : HSGUIController {}
|
||||
- (id)initWithPyRef:(PyObject *)aPyRef tableView:(NSTableView *)aTableView;
|
||||
- (PyColumns *)model;
|
||||
- (NSTableView *)view;
|
||||
- (void)connectNotifications;
|
||||
- (void)disconnectNotifications;
|
||||
- (void)initializeColumns:(HSColumnDef *)columns;
|
||||
- (void)initializeColumnsFromModel:(HSColumnDef)columnModel;
|
||||
- (void)setColumnsAsReadOnly;
|
||||
- (void)restoreColumns;
|
||||
- (void)setColumn:(NSString *)colname visible:(BOOL)visible;
|
||||
@end
|
||||
198
cocoalib/controllers/HSColumns.m
Normal file
198
cocoalib/controllers/HSColumns.m
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import "HSColumns.h"
|
||||
#import "Utils.h"
|
||||
#import "HSTableView.h" // To prevent warning on stopEditing
|
||||
|
||||
@implementation HSColumns
|
||||
- (id)initWithPyRef:(PyObject *)aPyRef tableView:(NSTableView *)aTableView
|
||||
{
|
||||
self = [super initWithPyRef:aPyRef wrapperClass:[PyColumns class]
|
||||
callbackClassName:@"ColumnsView" view:aTableView];
|
||||
[self connectNotifications];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self disconnectNotifications];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (PyColumns *)model
|
||||
{
|
||||
return (PyColumns *)model;
|
||||
}
|
||||
|
||||
- (NSTableView *)view
|
||||
{
|
||||
return (NSTableView *)view;
|
||||
}
|
||||
|
||||
- (void)connectNotifications
|
||||
{
|
||||
if ([self view] == nil) {
|
||||
/* This can happen if there something broken somewhere, and even though when that happens,
|
||||
it means that something serious is going on, the fact that we connect to all columnMoved:
|
||||
events messes thigs up even MORE. Don't connect when tableView is nil!
|
||||
*/
|
||||
return;
|
||||
}
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(columnMoved:)
|
||||
name:NSTableViewColumnDidMoveNotification object:[self view]];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(columnMoved:)
|
||||
name:NSOutlineViewColumnDidMoveNotification object:[self view]];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(columnResized:)
|
||||
name:NSTableViewColumnDidResizeNotification object:[self view]];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(columnResized:)
|
||||
name:NSOutlineViewColumnDidResizeNotification object:[self view]];
|
||||
}
|
||||
|
||||
- (void)disconnectNotifications
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
/*
|
||||
It is assumed, when this method is used, that the table/outline is empty *OR* that it is not
|
||||
defined in the column list.
|
||||
|
||||
Special note about NSOutlineView. You can use HSColumns on outline views, but you be aware that
|
||||
the "main" column (the one having the tree disclosure buttons) cannot be removed. Therefore,
|
||||
it has to be defined in the XIB and it must *not* be in column defs.
|
||||
*/
|
||||
- (void)initializeColumns:(HSColumnDef *)columns
|
||||
{
|
||||
/* We don't want default widths to overwrite stored with in the core code */
|
||||
[self disconnectNotifications];
|
||||
/* Translate the title of columns (needed for outlines) present already */
|
||||
for (NSTableColumn *c in [[self view] tableColumns]) {
|
||||
NSString *title = NSLocalizedStringFromTable([[c headerCell] stringValue], @"columns", @"");
|
||||
[[c headerCell] setStringValue:title];
|
||||
}
|
||||
NSUserDefaults *udc = [NSUserDefaultsController sharedUserDefaultsController];
|
||||
HSColumnDef *cdef = columns;
|
||||
while (cdef->attrname != nil) {
|
||||
if ([[self view] tableColumnWithIdentifier:cdef->attrname] != nil) {
|
||||
cdef++;
|
||||
continue;
|
||||
}
|
||||
NSTableColumn *c = [[[NSTableColumn alloc] initWithIdentifier:cdef->attrname] autorelease];
|
||||
[c setResizingMask:NSTableColumnUserResizingMask];
|
||||
/* If the column is not added right away, it causes glitches under 10.5 (minwidths instead of default widths) */
|
||||
[[self view] addTableColumn:c];
|
||||
NSString *title = [[self model] columnDisplay:cdef->attrname];
|
||||
[[c headerCell] setStringValue:title];
|
||||
if (cdef->sortable) {
|
||||
NSSortDescriptor *d = [[[NSSortDescriptor alloc] initWithKey:cdef->attrname ascending:YES] autorelease];
|
||||
[c setSortDescriptorPrototype:d];
|
||||
}
|
||||
[c setWidth:cdef->defaultWidth];
|
||||
[[self model] setColumn:cdef->attrname defaultWidth:cdef->defaultWidth];
|
||||
[c setMinWidth:cdef->minWidth];
|
||||
NSUInteger maxWidth = cdef->maxWidth;
|
||||
if (maxWidth == 0) {
|
||||
maxWidth = 0xffffff;
|
||||
}
|
||||
[c setMaxWidth:maxWidth];
|
||||
if (cdef->cellClass != nil) {
|
||||
id cell = [[[cdef->cellClass alloc] initTextCell:@""] autorelease];
|
||||
[cell setEditable:YES];
|
||||
[c setDataCell:cell];
|
||||
}
|
||||
[c bind:@"fontSize" toObject:udc withKeyPath:@"values.TableFontSize" options:nil];
|
||||
cdef++;
|
||||
}
|
||||
[self connectNotifications];
|
||||
}
|
||||
|
||||
/*
|
||||
Here, instead of having all our column defs, we have one column model, which we use to create
|
||||
our column defs using column names in [[self model] columnNamesInOrder].
|
||||
*/
|
||||
- (void)initializeColumnsFromModel:(HSColumnDef)columnModel
|
||||
{
|
||||
NSArray *colnames = [[self model] columnNamesInOrder];
|
||||
HSColumnDef *defs = (HSColumnDef *)malloc(([colnames count]+1)*sizeof(HSColumnDef));
|
||||
HSColumnDef *def = defs;
|
||||
for (NSString *colname in colnames) {
|
||||
def->attrname = colname;
|
||||
def->defaultWidth = columnModel.defaultWidth;
|
||||
def->minWidth = columnModel.minWidth;
|
||||
def->maxWidth = columnModel.maxWidth;
|
||||
def->sortable = columnModel.sortable;
|
||||
def->cellClass = columnModel.cellClass;
|
||||
def++;
|
||||
}
|
||||
def->attrname = nil; // Sentinel
|
||||
[self initializeColumns:defs];
|
||||
free(defs);
|
||||
}
|
||||
|
||||
- (void)setColumnsAsReadOnly
|
||||
{
|
||||
for (NSTableColumn *col in [[self view] tableColumns]) {
|
||||
[col setEditable:NO];
|
||||
}
|
||||
}
|
||||
|
||||
/* Notifications */
|
||||
- (void)columnMoved:(NSNotification *)notification
|
||||
{
|
||||
/* We only get this call after the move. Although there's "NSOldColumn" and "NSNewColumn",
|
||||
the old index is irrelevant since we have to find the moved column's name.
|
||||
*/
|
||||
NSInteger index = n2i([[notification userInfo] objectForKey:@"NSNewColumn"]);
|
||||
NSTableColumn *c = [[[self view] tableColumns] objectAtIndex:index];
|
||||
NSString *colName = [c identifier];
|
||||
[[self model] moveColumn:colName toIndex:index];
|
||||
}
|
||||
|
||||
- (void)columnResized:(NSNotification *)notification
|
||||
{
|
||||
NSTableColumn *c = [[notification userInfo] objectForKey:@"NSTableColumn"];
|
||||
[[self model] resizeColumn:[c identifier] toWidth:[c width]];
|
||||
}
|
||||
|
||||
/* Python --> Cocoa */
|
||||
- (void)restoreColumns
|
||||
{
|
||||
[self disconnectNotifications];
|
||||
NSArray *columnOrder = [[self model] columnNamesInOrder];
|
||||
for (NSInteger i=0; i<[columnOrder count]; i++) {
|
||||
NSString *colName = [columnOrder objectAtIndex:i];
|
||||
NSInteger index = [[self view] columnWithIdentifier:colName];
|
||||
if ((index != -1) && (index != i)) {
|
||||
[[self view] moveColumn:index toColumn:i];
|
||||
}
|
||||
}
|
||||
for (NSTableColumn *c in [[self view] tableColumns]) {
|
||||
NSInteger width = [[self model] columnWidth:[c identifier]];
|
||||
if (width > 0) {
|
||||
[c setWidth:width];
|
||||
}
|
||||
BOOL isVisible = [[self model] columnIsVisible:[c identifier]];
|
||||
[c setHidden:!isVisible];
|
||||
}
|
||||
[self connectNotifications];
|
||||
}
|
||||
|
||||
- (void)setColumn:(NSString *)colname visible:(BOOL)visible
|
||||
{
|
||||
NSTableColumn *col = [[self view] tableColumnWithIdentifier:colname];
|
||||
if (col == nil)
|
||||
return;
|
||||
if ([col isHidden] == !visible)
|
||||
return;
|
||||
if ([[self view] respondsToSelector:@selector(stopEditing)]) {
|
||||
[(id)[self view] stopEditing];
|
||||
}
|
||||
[col setHidden:!visible];
|
||||
}
|
||||
@end
|
||||
25
cocoalib/controllers/HSComboBox.h
Normal file
25
cocoalib/controllers/HSComboBox.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "HSGUIController.h"
|
||||
#import "PySelectableList.h"
|
||||
|
||||
@interface HSComboBox : HSGUIController <NSComboBoxDataSource>
|
||||
{
|
||||
NSArray *items;
|
||||
}
|
||||
- (id)initWithPyRef:(PyObject *)aPyRef view:(NSComboBox *)aView;
|
||||
- (NSComboBox *)view;
|
||||
- (void)setView:(NSComboBox *)aComboboxView;
|
||||
- (PySelectableList *)model;
|
||||
|
||||
- (void)comboboxViewSelectionChanged;
|
||||
- (void)refresh;
|
||||
- (void)updateSelection;
|
||||
@end
|
||||
119
cocoalib/controllers/HSComboBox.m
Normal file
119
cocoalib/controllers/HSComboBox.m
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import "HSComboBox.h"
|
||||
#import "HSPyUtil.h"
|
||||
|
||||
@implementation HSComboBox
|
||||
- (id)initWithPyRef:(PyObject *)aPyRef view:(NSComboBox *)aView
|
||||
{
|
||||
PySelectableList *m = [[PySelectableList alloc] initWithModel:aPyRef];
|
||||
self = [super initWithModel:m];
|
||||
[m bindCallback:createCallback(@"SelectableListView", self)];
|
||||
[m release];
|
||||
[self setView:aView];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[self view] setTarget:nil];
|
||||
[[self view] setDataSource:nil];
|
||||
[items release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSComboBox *)view
|
||||
{
|
||||
return (NSComboBox *)view;
|
||||
}
|
||||
|
||||
- (void)setView:(NSComboBox *)aComboboxView
|
||||
{
|
||||
if ([self view] != nil) {
|
||||
[[self view] setDataSource:nil];
|
||||
[[self view] setTarget:nil];
|
||||
}
|
||||
[super setView:aComboboxView];
|
||||
if (aComboboxView != nil) {
|
||||
[aComboboxView setUsesDataSource:YES];
|
||||
[aComboboxView setDataSource:self];
|
||||
[aComboboxView setAction:@selector(comboboxViewSelectionChanged)];
|
||||
[aComboboxView setTarget:self];
|
||||
/* This is required for the combobox to send its action whenever it's changed. Normally, it's
|
||||
already set, but then the combobox is created programmatically (xibless), it's not. We
|
||||
make sure it is here.
|
||||
*/
|
||||
[[aComboboxView cell] setSendsActionOnEndEditing:YES];
|
||||
[self refresh];
|
||||
}
|
||||
}
|
||||
|
||||
- (PySelectableList *)model
|
||||
{
|
||||
return (PySelectableList *)model;
|
||||
}
|
||||
|
||||
- (void)comboboxViewSelectionChanged
|
||||
{
|
||||
NSInteger index = [[self view] indexOfSelectedItem];
|
||||
if (index >= 0) {
|
||||
[[self model] selectIndex:index];
|
||||
}
|
||||
}
|
||||
|
||||
/* data source */
|
||||
- (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(NSInteger)index
|
||||
{
|
||||
if (index < 0) {
|
||||
return nil;
|
||||
}
|
||||
return [items objectAtIndex:index];
|
||||
}
|
||||
|
||||
- (NSInteger)numberOfItemsInComboBox:(NSComboBox *)aComboBox
|
||||
{
|
||||
return [items count];
|
||||
}
|
||||
|
||||
- (NSUInteger)comboBox:(NSComboBox *)aComboBox indexOfItemWithStringValue:(NSString *)aString
|
||||
{
|
||||
NSInteger index = [[self model] searchByPrefix:aString];
|
||||
if (index >= 0) {
|
||||
return index;
|
||||
}
|
||||
else {
|
||||
return NSNotFound;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)comboBox:(NSComboBox *)aComboBox completedString:(NSString *)uncompletedString
|
||||
{
|
||||
NSInteger index = [[self model] searchByPrefix:uncompletedString];
|
||||
if (index >= 0) {
|
||||
return [items objectAtIndex:index];
|
||||
}
|
||||
else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
/* model --> view */
|
||||
- (void)refresh
|
||||
{
|
||||
[items release];
|
||||
items = [[[self model] items] retain];
|
||||
[[self view] reloadData];
|
||||
[self updateSelection];
|
||||
}
|
||||
|
||||
- (void)updateSelection
|
||||
{
|
||||
[[self view] selectItemAtIndex:[[self model] selectedIndex]];
|
||||
}
|
||||
@end
|
||||
23
cocoalib/controllers/HSGUIController.h
Normal file
23
cocoalib/controllers/HSGUIController.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "PyGUIObject.h"
|
||||
|
||||
@interface HSGUIController : NSObject
|
||||
{
|
||||
PyGUIObject *model;
|
||||
NSView *view;
|
||||
}
|
||||
- (id)initWithModel:(PyGUIObject *)aPy;
|
||||
- (id)initWithModel:(PyGUIObject *)aPy view:(NSView *)aView;
|
||||
- (id)initWithPyRef:(PyObject *)aPyRef wrapperClass:(Class)aWrapperClass callbackClassName:(NSString *)aCallbackClassName view:(NSView *)aView;
|
||||
- (PyGUIObject *)model;
|
||||
- (NSView *)view;
|
||||
- (void)setView:(NSView *)aView;
|
||||
@end
|
||||
62
cocoalib/controllers/HSGUIController.m
Normal file
62
cocoalib/controllers/HSGUIController.m
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import "HSGUIController.h"
|
||||
#import "HSPyUtil.h"
|
||||
|
||||
@implementation HSGUIController
|
||||
- (id)initWithModel:(PyGUIObject *)aModel
|
||||
{
|
||||
self = [super init];
|
||||
model = [aModel retain];
|
||||
view = nil;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithModel:(PyGUIObject *)aModel view:(NSView *)aView
|
||||
{
|
||||
self = [super init];
|
||||
model = [aModel retain];
|
||||
[self setView:aView];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithPyRef:(PyObject *)aPyRef wrapperClass:(Class)aWrapperClass callbackClassName:(NSString *)aCallbackClassName view:(NSView *)aView
|
||||
{
|
||||
PyGUIObject *m = [[aWrapperClass alloc] initWithModel:aPyRef];
|
||||
self = [self initWithModel:m view:aView];
|
||||
[m bindCallback:createCallback(aCallbackClassName, self)];
|
||||
[m release];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
// NSLog([NSString stringWithFormat:@"%@ dealloc",[[self class] description]]);
|
||||
[self setView:nil];
|
||||
[model free];
|
||||
[model release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (PyGUIObject *)model
|
||||
{
|
||||
return model;
|
||||
}
|
||||
|
||||
- (NSView *)view
|
||||
{
|
||||
return view;
|
||||
}
|
||||
|
||||
- (void)setView:(NSView *)aView
|
||||
{
|
||||
[view release];
|
||||
view = [aView retain];
|
||||
}
|
||||
@end
|
||||
44
cocoalib/controllers/HSOutline.h
Normal file
44
cocoalib/controllers/HSOutline.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "HSGUIController.h"
|
||||
#import "HSOutlineView.h"
|
||||
#import "PyOutline.h"
|
||||
#import "NSIndexPathAdditions.h"
|
||||
|
||||
@interface HSOutline : HSGUIController <HSOutlineViewDelegate, NSOutlineViewDataSource> {
|
||||
NSMutableDictionary *itemData;
|
||||
NSMutableSet *itemRetainer;
|
||||
}
|
||||
- (id)initWithPyRef:(PyObject *)aPyRef wrapperClass:(Class)aWrapperClass callbackClassName:(NSString *)aCallbackClassName view:(HSOutlineView *)aView;
|
||||
- (PyOutline *)model;
|
||||
- (HSOutlineView *)view;
|
||||
|
||||
/* Public */
|
||||
- (void)refresh;
|
||||
- (NSIndexPath *)selectedIndexPath;
|
||||
- (NSArray *)selectedIndexPaths;
|
||||
- (NSString *)dataForCopyToPasteboard;
|
||||
- (void)startEditing;
|
||||
- (void)stopEditing;
|
||||
- (void)updateSelection;
|
||||
- (void)expandItem:(NSIndexPath *)item;
|
||||
- (NSIndexPath *)internalizedPath:(NSIndexPath *)path;
|
||||
|
||||
/* Caching */
|
||||
- (id)property:(NSString *)property valueAtPath:(NSIndexPath *)path;
|
||||
- (void)setProperty:(NSString *)property value:(id)value atPath:(NSIndexPath *)path;
|
||||
- (NSString *)stringProperty:(NSString *)property valueAtPath:(NSIndexPath *)path;
|
||||
- (void)setStringProperty:(NSString *)property value:(NSString *)value atPath:(NSIndexPath *)path;
|
||||
- (BOOL)boolProperty:(NSString *)property valueAtPath:(NSIndexPath *)path;
|
||||
- (void)setBoolProperty:(NSString *)property value:(BOOL)value atPath:(NSIndexPath *)path;
|
||||
- (NSInteger)intProperty:(NSString *)property valueAtPath:(NSIndexPath *)path;
|
||||
- (void)setIntProperty:(NSString *)property value:(int)value atPath:(NSIndexPath *)path;
|
||||
- (void)refreshItemAtPath:(NSIndexPath *)path;
|
||||
@end
|
||||
280
cocoalib/controllers/HSOutline.m
Normal file
280
cocoalib/controllers/HSOutline.m
Normal file
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import "HSOutline.h"
|
||||
#import "Utils.h"
|
||||
|
||||
#define CHILDREN_COUNT_PROPERTY @"children_count"
|
||||
|
||||
@implementation HSOutline
|
||||
- (id)initWithPyRef:(PyObject *)aPyRef wrapperClass:(Class)aWrapperClass callbackClassName:(NSString *)aCallbackClassName view:(HSOutlineView *)aView
|
||||
{
|
||||
self = [super initWithPyRef:aPyRef wrapperClass:aWrapperClass callbackClassName:aCallbackClassName view:aView];
|
||||
itemData = [[NSMutableDictionary dictionary] retain];
|
||||
/* Dictionaries don't retain its keys because it copies them. Our items are NSIndexPath and when
|
||||
an index path has the same value, it's the same instance. Before OS X 10.7, all these instances
|
||||
stayed in memory, so we didn't need to worry about retaining them. Hoever, it seems now that
|
||||
index path instances are sometimes released. Oops. So, we now need to retain our index path
|
||||
instances and that's why we use itemRetainer.
|
||||
|
||||
In fact, it seems that unlike what the doc says, it's not true that two index paths with the
|
||||
same value will always be the same instance.
|
||||
*/
|
||||
itemRetainer = [[NSMutableSet set] retain];
|
||||
if (([[self view] outlineTableColumn] == nil) && ([[[self view] tableColumns] count] > 0)) {
|
||||
[[self view] setOutlineTableColumn:[[[self view] tableColumns] objectAtIndex:0]];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[itemData release];
|
||||
[itemRetainer release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (HSOutlineView *)view
|
||||
{
|
||||
return (HSOutlineView *)view;
|
||||
}
|
||||
|
||||
- (void)setView:(HSOutlineView *)aOutlineView
|
||||
{
|
||||
if ([self view] != nil) {
|
||||
[[self view] setDataSource:nil];
|
||||
[[self view] setDelegate:nil];
|
||||
}
|
||||
[super setView:aOutlineView];
|
||||
if (aOutlineView != nil) {
|
||||
[aOutlineView setDataSource:self];
|
||||
[aOutlineView setDelegate:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (PyOutline *)model
|
||||
{
|
||||
return (PyOutline *)model;
|
||||
}
|
||||
|
||||
/* Private */
|
||||
- (void)setPySelection
|
||||
{
|
||||
NSMutableArray *paths = [NSMutableArray array];
|
||||
NSIndexSet *indexes = [[self view] selectedRowIndexes];
|
||||
NSInteger i = [indexes firstIndex];
|
||||
while (i != NSNotFound) {
|
||||
NSIndexPath *path = [[self view] itemAtRow:i];
|
||||
[paths addObject:p2a(path)];
|
||||
i = [indexes indexGreaterThanIndex:i];
|
||||
}
|
||||
[[self model] setSelectedPaths:paths];
|
||||
}
|
||||
|
||||
- (NSIndexPath *)internalizedPath:(NSIndexPath *)path
|
||||
{
|
||||
/* Because NSIndexPath stopped guaranteeing that the same paths always were represented by the
|
||||
same instances, we have to make sure, when we manipulate paths, that we manipulate the same
|
||||
instances as those that were given by outlineView:child:ofItem:
|
||||
*/
|
||||
NSIndexPath *result = [itemRetainer member:path];
|
||||
if (result == nil) {
|
||||
result = path;
|
||||
[itemData setObject:[NSMutableDictionary dictionary] forKey:result];
|
||||
[itemRetainer addObject:result];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Public */
|
||||
- (void)refresh
|
||||
{
|
||||
[itemData removeAllObjects];
|
||||
// We can't get rid of our instances just yet, we have to wait until after reloadData
|
||||
NSSet *oldRetainer = itemRetainer;
|
||||
itemRetainer = [[NSMutableSet set] retain];
|
||||
[[self view] setDelegate:nil];
|
||||
[[self view] reloadData];
|
||||
[[self view] setDelegate:self];
|
||||
[oldRetainer release];
|
||||
[self updateSelection];
|
||||
}
|
||||
|
||||
- (NSArray *)selectedIndexPaths
|
||||
{
|
||||
NSArray *arrayPaths = [[self model] selectedPaths];
|
||||
NSMutableArray *result = [NSMutableArray array];
|
||||
for (NSArray *arrayPath in arrayPaths) {
|
||||
[result addObject:[self internalizedPath:a2p(arrayPath)]];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)startEditing
|
||||
{
|
||||
[[self view] startEditing];
|
||||
}
|
||||
|
||||
- (void)stopEditing
|
||||
{
|
||||
[[self view] stopEditing];
|
||||
}
|
||||
|
||||
- (void)updateSelection
|
||||
{
|
||||
[[self view] updateSelection];
|
||||
}
|
||||
|
||||
- (void)expandItem:(NSIndexPath *)item
|
||||
{
|
||||
[[self view] ensureExpanded:item];
|
||||
}
|
||||
|
||||
/* Caching */
|
||||
- (id)property:(NSString *)property valueAtPath:(NSIndexPath *)path
|
||||
{
|
||||
NSMutableDictionary *props = [itemData objectForKey:path];
|
||||
id value = [props objectForKey:property];
|
||||
if (value == nil) {
|
||||
value = [[self model] property:property valueAtPath:p2a(path)];
|
||||
if (value == nil) {
|
||||
value = [NSNull null];
|
||||
}
|
||||
[props setObject:value forKey:property];
|
||||
}
|
||||
if (value == [NSNull null]) {
|
||||
value = nil;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
- (void)setProperty:(NSString *)property value:(id)value atPath:(NSIndexPath *)path
|
||||
{
|
||||
NSMutableDictionary *props = [itemData objectForKey:path];
|
||||
[props removeObjectForKey:property];
|
||||
[[self model] setProperty:property value:value atPath:p2a(path)];
|
||||
}
|
||||
|
||||
- (NSString *)stringProperty:(NSString *)property valueAtPath:(NSIndexPath *)path
|
||||
{
|
||||
return [self property:property valueAtPath:path];
|
||||
}
|
||||
|
||||
- (void)setStringProperty:(NSString *)property value:(NSString *)value atPath:(NSIndexPath *)path
|
||||
{
|
||||
[self setProperty:property value:value atPath:path];
|
||||
}
|
||||
|
||||
- (BOOL)boolProperty:(NSString *)property valueAtPath:(NSIndexPath *)path
|
||||
{
|
||||
NSNumber *value = [self property:property valueAtPath:path];
|
||||
return [value boolValue];
|
||||
}
|
||||
|
||||
- (void)setBoolProperty:(NSString *)property value:(BOOL)value atPath:(NSIndexPath *)path
|
||||
{
|
||||
[self setProperty:property value:[NSNumber numberWithBool:value] atPath:path];
|
||||
}
|
||||
|
||||
- (NSInteger)intProperty:(NSString *)property valueAtPath:(NSIndexPath *)path
|
||||
{
|
||||
NSNumber *value = [self property:property valueAtPath:path];
|
||||
return [value intValue];
|
||||
}
|
||||
|
||||
- (void)setIntProperty:(NSString *)property value:(int)value atPath:(NSIndexPath *)path
|
||||
{
|
||||
[self setProperty:property value:[NSNumber numberWithInt:value] atPath:path];
|
||||
}
|
||||
|
||||
- (void)refreshItemAtPath:(NSIndexPath *)path
|
||||
{
|
||||
NSMutableDictionary *props = [itemData objectForKey:path];
|
||||
[props removeAllObjects];
|
||||
}
|
||||
|
||||
/* NSOutlineView data source */
|
||||
|
||||
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
|
||||
{
|
||||
return [self intProperty:CHILDREN_COUNT_PROPERTY valueAtPath:(NSIndexPath *)item];
|
||||
}
|
||||
|
||||
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
|
||||
{
|
||||
NSIndexPath *parent = item;
|
||||
NSIndexPath *path = parent == nil ? [NSIndexPath indexPathWithIndex:index] : [parent indexPathByAddingIndex:index];
|
||||
return [self internalizedPath:path];
|
||||
}
|
||||
|
||||
- (BOOL)outlineView:(NSOutlineView *)theOutlineView isItemExpandable:(id)item
|
||||
{
|
||||
return [self outlineView:[self view] numberOfChildrenOfItem:item] > 0;
|
||||
}
|
||||
|
||||
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)column item:(id)item
|
||||
{
|
||||
return [[self model] canEditProperty:[column identifier] atPath:p2a((NSIndexPath *)item)];
|
||||
}
|
||||
|
||||
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)column byItem:(id)item
|
||||
{
|
||||
return [self property:[column identifier] valueAtPath:(NSIndexPath *)item];
|
||||
}
|
||||
|
||||
- (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)value forTableColumn:(NSTableColumn *)column byItem:(id)item
|
||||
{
|
||||
[self setProperty:[column identifier] value:value atPath:(NSIndexPath *)item];
|
||||
}
|
||||
|
||||
/* We need to change the model selection at both IsChanging and DidChange. We need to set the
|
||||
model selection at IsChanging before of the arrow clicking. The action launched by this little arrow
|
||||
is performed before DidChange. However, when using the arrow to change the selection, IsChanging is
|
||||
never called
|
||||
*/
|
||||
- (void)outlineViewSelectionIsChanging:(NSNotification *)notification
|
||||
{
|
||||
[self setPySelection];
|
||||
}
|
||||
|
||||
- (void)outlineViewSelectionDidChange:(NSNotification *)notification
|
||||
{
|
||||
[self setPySelection];
|
||||
}
|
||||
|
||||
/* HSOutlineView delegate */
|
||||
- (NSIndexPath *)selectedIndexPath
|
||||
{
|
||||
NSArray *paths = [self selectedIndexPaths];
|
||||
if ([paths count] > 0) {
|
||||
return [paths objectAtIndex:0];
|
||||
}
|
||||
else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)dataForCopyToPasteboard
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)outlineViewDidEndEditing:(HSOutlineView *)outlineView
|
||||
{
|
||||
[[self model] saveEdits];
|
||||
}
|
||||
|
||||
- (void)outlineViewWasDoubleClicked:(HSOutlineView *)outlineView
|
||||
{
|
||||
}
|
||||
|
||||
- (void)outlineViewCancelsEdition:(HSOutlineView *)outlineView
|
||||
{
|
||||
[[self model] cancelEdits];
|
||||
}
|
||||
@end
|
||||
23
cocoalib/controllers/HSPopUpList.h
Normal file
23
cocoalib/controllers/HSPopUpList.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <Python.h>
|
||||
#import "HSGUIController.h"
|
||||
#import "PySelectableList.h"
|
||||
|
||||
@interface HSPopUpList : HSGUIController {}
|
||||
- (id)initWithPyRef:(PyObject *)aPyRef popupView:(NSPopUpButton *)aPopupView;
|
||||
- (NSPopUpButton *)view;
|
||||
- (void)setView:(NSPopUpButton *)aPopupView;
|
||||
- (PySelectableList *)model;
|
||||
|
||||
- (void)popupViewSelectionChanged;
|
||||
- (void)refresh;
|
||||
- (void)updateSelection;
|
||||
@end
|
||||
60
cocoalib/controllers/HSPopUpList.m
Normal file
60
cocoalib/controllers/HSPopUpList.m
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import "HSPopUpList.h"
|
||||
#import "Utils.h"
|
||||
|
||||
@implementation HSPopUpList
|
||||
- (id)initWithPyRef:(PyObject *)aPyRef popupView:(NSPopUpButton *)aPopupView
|
||||
{
|
||||
self = [super initWithPyRef:aPyRef wrapperClass:[PySelectableList class]
|
||||
callbackClassName:@"SelectableListView" view:aPopupView];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSPopUpButton *)view
|
||||
{
|
||||
return (NSPopUpButton *)view;
|
||||
}
|
||||
|
||||
- (void)setView:(NSPopUpButton *)aPopupView
|
||||
{
|
||||
if ([self view] != nil) {
|
||||
[[self view] setTarget:nil];
|
||||
}
|
||||
[super setView:aPopupView];
|
||||
if (aPopupView != nil) {
|
||||
[aPopupView setAction:@selector(popupViewSelectionChanged)];
|
||||
[aPopupView setTarget:self];
|
||||
[self refresh];
|
||||
}
|
||||
}
|
||||
|
||||
- (PySelectableList *)model
|
||||
{
|
||||
return (PySelectableList *)model;
|
||||
}
|
||||
|
||||
- (void)popupViewSelectionChanged
|
||||
{
|
||||
[[self model] selectIndex:[[self view] indexOfSelectedItem]];
|
||||
}
|
||||
|
||||
/* model --> view */
|
||||
- (void)refresh
|
||||
{
|
||||
[[self view] removeAllItems];
|
||||
[[self view] addItemsWithTitles:[[self model] items]];
|
||||
[self updateSelection];
|
||||
}
|
||||
|
||||
- (void)updateSelection
|
||||
{
|
||||
[[self view] selectItemAtIndex:[[self model] selectedIndex]];
|
||||
}
|
||||
@end
|
||||
30
cocoalib/controllers/HSProgressWindow.h
Normal file
30
cocoalib/controllers/HSProgressWindow.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Copyright 2013 Hardcoded Software (http://www.hardcoded.net)
|
||||
|
||||
This software is licensed under the "BSD" License as described in the "LICENSE" file,
|
||||
which should be included with this package. The terms are also available at
|
||||
http://www.hardcoded.net/licenses/bsd_license
|
||||
*/
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "HSGUIController.h"
|
||||
#import "HSTextField.h"
|
||||
#import "Worker.h"
|
||||
#import "PyProgressWindow.h"
|
||||
|
||||
@interface HSProgressWindow : HSGUIController <Worker>
|
||||
{
|
||||
NSInteger progress;
|
||||
HSTextField *jobdescTextField;
|
||||
HSTextField *progressdescTextField;
|
||||
NSWindow *parentWindow;
|
||||
}
|
||||
|
||||
- (id)initWithPyRef:(PyObject *)aPyRef view:(NSView *)aView;
|
||||
- (PyProgressWindow *)model;
|
||||
- (void)setParentWindow:(NSWindow *)aParentWindow;
|
||||
|
||||
- (void)setProgress:(NSInteger)aProgress;
|
||||
- (void)showWindow;
|
||||
- (void)closeWindow;
|
||||
@end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user