diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6012a4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +index +com.wincent.buildtools.gitrev.h +*.temp +*.bak + +*.lyx~ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e39a8a8 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "buildtools"] + path = buildtools + url = git://git.wincent.com/buildtools.git +[submodule "WOPublic"] + path = WOPublic + url = git://git.wincent.com/WOPublic.git diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..7631d0b --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright 2002-2012 Wincent Colaiuta. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. 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. + +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 HOLDERS 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. diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..6aca9f8 --- /dev/null +++ b/README.txt @@ -0,0 +1,55 @@ +Synergy +======= + + The lightweight iTunes controller for Mac OS X + https://wincent.com/products/synergy/ + + Control iTunes from any application using hot keys, an always-available global + menu, or attractive, unobtrusive buttons in your menu bar. Get instant + feedback with transparent overlay windows. Enjoy "scrobbling" integration with + last.fm and cover art downloads from amazon.com. + +About the open source release +----------------------------- + +Synergy was originally released in November 2002, and over the years has +received many updates. Due to competing demands on my time, the release rate +slowed down as the years went on, and at the time of writing, the last release +was version 4.5.2, on February 1, 2011. + +In March 2011 I started a new job building the world's largest platform for +collective action at Causes (http://www.causes.com/). + +I've realized that this means that in the immediate future Synergy is unlikely +to get the attention from me that it deserves, yet people still use it and would +like to see development work to continue. + +The simplest way to make that possible is to open source the project. This isn't +just a means to keep the project alive; I strongly believe that open source is +the right way to do software development and in the future it will be the only +way that seriously-taken software is developed. + +The source code is now BSD licensed. The initial source code release is based +off the code that was used to build version 4.5.2, minus the serial number code +and third-party code (such as a local copy of the Growl framework) which I did +not want to distribute. I'm hoping this will be just the first of several such +open source releases that I can make in the near future. + +Synergy will still be available for download and purchase on wincent.com. Maybe +in the future my circumstances will change and I'll be able to fully re-enter +the world of Mac OS X development, in which case I'd like to work on getting +Synergy into Apple's App Store. + +In any case, here's the code, in all its shameful glory. This was the first time +I'd written a piece of software that went beyond a pet project. Looking back at +it now, I truly shudder at some of the ghastly code I wrote while I was learning +C, learning Objective-C, learning Apple's APIs, learning object-oriented +programming, design patterns etc, all at once. Some of this stuff, particularly +the files and methods that were written earlier on, is truly cringe-worthy and +would not look out of place on thedailywtf.com. + +On the other hand, looking back on it makes me realize how much I've grown as a +developer over the last ten years. Its been an amazing ride. + +Wincent Colaiuta +February 25, 2012 diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..604c7a4 --- /dev/null +++ b/Rakefile @@ -0,0 +1,30 @@ +require 'pathname' + +def release_version + return @release_version if @release_version + version_file = Pathname.new('WOSynergy_Version.h').read + version_line = version_file.lines.find do |line| + line =~ /\A#define\s+WO_INFO_PLIST_VERSION\s+(.+)\s*\z/ + end or raise "could not find version number" + @release_version = $~[1] +end + +desc 'create a Git tag for the current build' +task :tag do + if release_version=~ /\+\z/ + raise "refusing to tag intermediate (not official release) version " + + "(version number '#{release_version}' ends in '+')" + else + sh "./tag-release.sh #{release_version}" + end +end + +desc 'upload the current build to Amazon S3' +task :upload do + sh 'aws put ' + + "s3.wincent.com/synergy/releases/synergy-#{release_version}.zip " + + "../../build/Release/synergy-#{release_version}.zip" + sh 'aws put ' + + "s3.wincent.com/synergy/releases/synergy-#{release_version}.zip?acl " + + '--public' +end diff --git a/Synergy.xcodeproj/.gitignore b/Synergy.xcodeproj/.gitignore new file mode 100644 index 0000000..9c66114 --- /dev/null +++ b/Synergy.xcodeproj/.gitignore @@ -0,0 +1,4 @@ +*.mode1* +*.pbxuser +!default.pbxuser + diff --git a/Synergy.xcodeproj/default.pbxuser b/Synergy.xcodeproj/default.pbxuser new file mode 100644 index 0000000..28f8211 --- /dev/null +++ b/Synergy.xcodeproj/default.pbxuser @@ -0,0 +1,5095 @@ +// !$*UTF8*$! +{ + 089C1669FE841209C02AAC07 /* Project object */ = { + activeBuildConfigurationName = Deployment; + activeExecutable = F54E328403A1D9CC01E04078 /* SynergyPref */; + activeTarget = BCBE228A093B34BD00FAD628 /* SynergyPref (Upgraded) */; + addToTargets = ( + ); + breakpoints = ( + ); + codeSenseManager = BCC8716005853E3F00054D38 /* Code sense */; + executables = ( + F54E328403A1D9CC01E04078 /* SynergyPref */, + BCBE2365093B34BD00FAD628 /* SynergyApp (Upgraded) */, + ); + perUserDictionary = { + PBXConfiguration.PBXFileTableDataSource3.PBXBookmarksDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXBookmarksDataSource_NameID; + PBXFileTableDataSourceColumnWidthsKey = ( + 401, + 303.5991, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXBookmarksDataSource_LocationID, + PBXBookmarksDataSource_NameID, + ); + }; + PBXConfiguration.PBXFileTableDataSource3.PBXErrorsWarningsDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXErrorsWarningsDataSource_LocationID; + PBXFileTableDataSourceColumnWidthsKey = ( + 20, + 494, + 187.2085, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXErrorsWarningsDataSource_TypeID, + PBXErrorsWarningsDataSource_MessageID, + PBXErrorsWarningsDataSource_LocationID, + ); + }; + PBXConfiguration.PBXFileTableDataSource3.PBXExecutablesDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXExecutablesDataSource_NameID; + PBXFileTableDataSourceColumnWidthsKey = ( + 22, + 400.7974, + 211.5835, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXExecutablesDataSource_ActiveFlagID, + PBXExecutablesDataSource_NameID, + PBXExecutablesDataSource_CommentsID, + ); + }; + PBXConfiguration.PBXFileTableDataSource3.PBXFileTableDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; + PBXFileTableDataSourceColumnWidthsKey = ( + 20, + 681, + 20, + 147, + 43, + 43, + 20, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXFileDataSource_FiletypeID, + PBXFileDataSource_Filename_ColumnID, + PBXFileDataSource_Built_ColumnID, + PBXFileDataSource_ObjectSize_ColumnID, + PBXFileDataSource_Errors_ColumnID, + PBXFileDataSource_Warnings_ColumnID, + PBXFileDataSource_Target_ColumnID, + ); + }; + PBXConfiguration.PBXFileTableDataSource3.PBXFindDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXFindDataSource_LocationID; + PBXFileTableDataSourceColumnWidthsKey = ( + 392.2974, + 395.2085, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXFindDataSource_MessageID, + PBXFindDataSource_LocationID, + ); + }; + PBXConfiguration.PBXFileTableDataSource3.PBXSymbolsDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXSymbolsDataSource_SymbolNameID; + PBXFileTableDataSourceColumnWidthsKey = ( + 16, + 242.8008, + 272.0356, + 249.2085, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXSymbolsDataSource_SymbolTypeIconID, + PBXSymbolsDataSource_SymbolNameID, + PBXSymbolsDataSource_SymbolTypeID, + PBXSymbolsDataSource_ReferenceNameID, + ); + }; + PBXConfiguration.PBXFileTableDataSource3.XCSCMDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; + PBXFileTableDataSourceColumnWidthsKey = ( + 20, + 20, + 690, + 20, + 82, + 43, + 43, + 20, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXFileDataSource_SCM_ColumnID, + PBXFileDataSource_FiletypeID, + PBXFileDataSource_Filename_ColumnID, + PBXFileDataSource_Built_ColumnID, + PBXFileDataSource_ObjectSize_ColumnID, + PBXFileDataSource_Errors_ColumnID, + PBXFileDataSource_Warnings_ColumnID, + PBXFileDataSource_Target_ColumnID, + ); + }; + PBXConfiguration.PBXTargetDataSource.PBXTargetDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; + PBXFileTableDataSourceColumnWidthsKey = ( + 20, + 705, + 10, + 20, + 148, + 43, + 43, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXFileDataSource_FiletypeID, + PBXFileDataSource_Filename_ColumnID, + PBXTargetDataSource_PrimaryAttribute, + PBXFileDataSource_Built_ColumnID, + PBXFileDataSource_ObjectSize_ColumnID, + PBXFileDataSource_Errors_ColumnID, + PBXFileDataSource_Warnings_ColumnID, + ); + }; + PBXPerProjectTemplateStateSaveDate = 184192409; + PBXPrepackagedSmartGroups_v2 = ( + { + PBXTransientLocationAtTop = bottom; + absolutePathToBundle = ""; + activationKey = OldTargetSmartGroup; + clz = PBXTargetSmartGroup; + description = "Displays all targets of the project."; + globalID = 1C37FABC04509CD000000102; + name = Targets; + preferences = { + image = Targets; + }; + }, + { + PBXTransientLocationAtTop = bottom; + absolutePathToBundle = ""; + clz = PBXTargetSmartGroup2; + description = "Displays all targets of the project as well as nested build phases."; + globalID = 1C37FBAC04509CD000000102; + name = Targets; + preferences = { + image = Targets; + }; + }, + { + PBXTransientLocationAtTop = bottom; + absolutePathToBundle = ""; + clz = PBXExecutablesSmartGroup; + description = "Displays all executables of the project."; + globalID = 1C37FAAC04509CD000000102; + name = Executables; + preferences = { + image = Executable; + }; + }, + { + " PBXTransientLocationAtTop " = bottom; + absolutePathToBundle = ""; + clz = PBXErrorsWarningsSmartGroup; + description = "Displays files with errors or warnings."; + globalID = 1C08E77C0454961000C914BD; + name = "Errors and Warnings"; + preferences = { + fnmatch = ""; + image = WarningsErrors; + recursive = 1; + regex = ""; + root = ""; + }; + }, + { + PBXTransientLocationAtTop = bottom; + absolutePathToBundle = ""; + clz = PBXFilenameSmartGroup; + description = "Filters items in a given group (potentially recursively) based on matching the name with the regular expression of the filter."; + globalID = 1CC0EA4004350EF90044410B; + name = "Implementation Files"; + preferences = { + canSave = 1; + fnmatch = ""; + image = SmartFolder; + isLeaf = 0; + recursive = 1; + regex = "?*\\.[mcMC]"; + root = ""; + }; + }, + { + PBXTransientLocationAtTop = bottom; + absolutePathToBundle = ""; + clz = PBXFilenameSmartGroup; + description = "This group displays Interface Builder NIB Files."; + globalID = 1CC0EA4004350EF90041110B; + name = "NIB Files"; + preferences = { + canSave = 1; + fnmatch = "*.nib"; + image = SmartFolder; + isLeaf = 0; + recursive = 1; + regex = ""; + root = ""; + }; + }, + { + PBXTransientLocationAtTop = no; + absolutePathToBundle = ""; + clz = PBXFindSmartGroup; + description = "Displays Find Results."; + globalID = 1C37FABC05509CD000000102; + name = "Find Results"; + preferences = { + image = spyglass; + }; + }, + { + PBXTransientLocationAtTop = no; + absolutePathToBundle = ""; + clz = PBXBookmarksSmartGroup; + description = "Displays Project Bookmarks."; + globalID = 1C37FABC05539CD112110102; + name = Bookmarks; + preferences = { + image = Bookmarks; + }; + }, + { + PBXTransientLocationAtTop = bottom; + absolutePathToBundle = ""; + clz = XCSCMSmartGroup; + description = "Displays files with interesting SCM status."; + globalID = E2644B35053B69B200211256; + name = SCM; + preferences = { + image = PBXRepository; + isLeaf = 0; + }; + }, + { + PBXTransientLocationAtTop = bottom; + absolutePathToBundle = ""; + clz = PBXSymbolsSmartGroup; + description = "Displays all symbols for the project."; + globalID = 1C37FABC04509CD000100104; + name = "Project Symbols"; + preferences = { + image = ProjectSymbols; + isLeaf = 1; + }; + }, + { + PBXTransientLocationAtTop = bottom; + absolutePathToBundle = ""; + clz = PBXFilenameSmartGroup; + description = "Filters items in a given group (potentially recursively) based on matching the name with the regular expression of the filter."; + globalID = PBXTemplateMarker; + name = "Simple Filter SmartGroup"; + preferences = { + canSave = 1; + fnmatch = "*.nib"; + image = SmartFolder; + isLeaf = 0; + recursive = 1; + regex = ""; + root = ""; + }; + }, + { + PBXTransientLocationAtTop = bottom; + absolutePathToBundle = ""; + clz = PBXFilenameSmartGroup; + description = "Filters items in a given group (potentially recursively) based on matching the name with the regular expression of the filter."; + globalID = PBXTemplateMarker; + name = "Simple Regular Expression SmartGroup"; + preferences = { + canSave = 1; + fnmatch = ""; + image = SmartFolder; + isLeaf = 0; + recursive = 1; + regex = "?*\\.[mcMC]"; + root = ""; + }; + }, + { + PBXTransientLocationAtTop = bottom; + clz = XDDesignSmartGroup; + description = "Displays Xdesign models"; + globalID = 2E4A936305E6979E00701470; + name = Design; + preferences = { + image = Design; + isLeaf = 0; + }; + }, + ); + PBXWorkspaceContents = ( + { + PBXProjectWorkspaceModule_StateKey_Rev39 = { + PBXProjectWorkspaceModule_DEGV_Geometry = { + _collapsingFrameDimension = 0; + _indexOfCollapsedView = 0; + _percentageOfCollapsedView = 0; + isCollapsed = yes; + sizes = ( + "{{0, 0}, {788, 140}}", + "{{0, 140}, {788, 457}}", + ); + }; + PBXProjectWorkspaceModule_DataSourceSelectionKey_Rev6 = { + BoundsStr = "{{0, 0}, {773, 123}}"; + Rows = ( + 0, + ); + VisibleRectStr = "{{0, 0}, {773, 123}}"; + }; + PBXProjectWorkspaceModule_EditorOpen = true; + PBXProjectWorkspaceModule_EmbeddedNavigatorGroup = { + PBXSplitModuleInNavigatorKey = { + Split0 = { + bookmark = BC56E8350653FB0700D9027E; + history = ( + BCC8725C058561BB00054D38, + BCC87261058561BC00054D38, + BCC872970585620F00054D38, + BCB376BE05856BAB00CFDABB, + BCDA94B80587EE4F0089456C, + BCDA94BB0587EE4F0089456C, + BCDA94BC0587EE4F0089456C, + BCDA94BD0587EE4F0089456C, + BCDA94BF0587EE4F0089456C, + BCDA94C10587EE4F0089456C, + BC29FE3E0591377C0049A427, + BCE132E0059150E500764D81, + BCE132E1059150E500764D81, + BCE132E2059150E500764D81, + BCE132E3059150E500764D81, + BCE132E4059150E500764D81, + BCE132E5059150E500764D81, + BCE132E7059150E500764D81, + BC361BE1059167580048A214, + BCF262860591EA35005B79DC, + BC2261BC0594074200AE184B, + BC2261BD0594074200AE184B, + BC2261BE0594074200AE184B, + BC25817805994F4000220DE8, + BC0C427B059A53DA00D5F37D, + BCA5B1FA059A5B36003B2607, + BCA5B1FC059A5B36003B2607, + BCA5B1FD059A5B36003B2607, + BCA5B1FF059A5B36003B2607, + BC72531D05A26BD3000D5DF2, + BC72531E05A26BD3000D5DF2, + BC01FFDB05A503F500A7ED81, + BC31C08305AB94C5003881B1, + BCE4F4B205AEBC6100A5A030, + BC21EA9C05CEAB930080F446, + BC98EB3D05D04B010099F5CA, + BC98EB3F05D04B010099F5CA, + BCCFC9FB05D471F400651BC7, + BCCF25EF05E2976F00E3D1DB, + BC24D3F405E857890021BD1F, + BC24D3F505E857890021BD1F, + BC24D3F605E857890021BD1F, + BC24D3F705E857890021BD1F, + BC13A9DD05F29A2F0024CDF0, + BC536D9705F43E7300F0E668, + BC536D9805F43E7300F0E668, + BC25FA2105FE7F4E00BA8B5D, + BC25FA2305FE7F4E00BA8B5D, + BC25FA2405FE7F4E00BA8B5D, + BCF3AFB8060B6C8100B2B13C, + BCF3AFB9060B6C8100B2B13C, + BCF3AFBE060B6C8100B2B13C, + BCF3AFBF060B6C8100B2B13C, + BCF3AFC0060B6C8100B2B13C, + BCF3AFC1060B6C8100B2B13C, + BCF3AFC2060B6C8100B2B13C, + BCF3AFC3060B6C8100B2B13C, + BCF3AFC4060B6C8100B2B13C, + BCF3AFC5060B6C8100B2B13C, + BCF3AFC6060B6C8100B2B13C, + BCF3AFC7060B6C8100B2B13C, + BCE65DDC060DFA4F007E6BDE, + BCE65DDD060DFA4F007E6BDE, + BCE65DDE060DFA4F007E6BDE, + BC3EB6F6060E6AAE004FF653, + BCFC56D306146876001830F3, + BCFC56D706146876001830F3, + BCFC5D84061473D1001830F3, + BCFC644306147A90001830F3, + BCFC644406147A90001830F3, + BCE03A3E061A691600238E7E, + BCE03A5D061ACCD700238E7E, + BCE03A5E061ACCD700238E7E, + BCE03A5F061ACCD700238E7E, + BCE04144061B2E9F00238E7E, + BCE04E62061B8B3000238E7E, + BC673290061C376700C28481, + BC673291061C376700C28481, + BC673292061C376700C28481, + BC673293061C376700C28481, + BC673294061C376700C28481, + BC673295061C376700C28481, + BC673296061C376700C28481, + BC673297061C376700C28481, + BC673298061C376700C28481, + BC673299061C376700C28481, + BC67329A061C376700C28481, + BC67329B061C376700C28481, + BC67329C061C376700C28481, + BC67329D061C376700C28481, + BC67329E061C376700C28481, + BC67329F061C376700C28481, + BC6732A0061C376700C28481, + BC6732A1061C376700C28481, + BC6732A2061C376700C28481, + BC6732A3061C376700C28481, + BC6732A4061C376700C28481, + BC6732A5061C376700C28481, + BCA7197E05F39694009B5E25, + BC7483270620F81F002E7F0F, + BC56E1620652B33500D9027E, + BC56E82F0653FB0700D9027E, + BC56E8300653FB0700D9027E, + BC56E8310653FB0700D9027E, + ); + prevStack = ( + BCC871AB058556A400054D38, + BCC871FF05855C3200054D38, + BCC872480585618E00054D38, + BCC8724D058561A200054D38, + BCC87252058561B100054D38, + BCC87259058561BB00054D38, + BCC8725E058561BB00054D38, + BCC87263058561BC00054D38, + BCC87272058561C200054D38, + BCB376C305856BAB00CFDABB, + BCB376C405856BAB00CFDABB, + BCDA94C30587EE4F0089456C, + BCDA94C40587EE4F0089456C, + BCDA94C50587EE4F0089456C, + BCDA94C70587EE4F0089456C, + BCDA94C80587EE4F0089456C, + BCDA94C90587EE4F0089456C, + BCDA94CA0587EE4F0089456C, + BCDA94CB0587EE4F0089456C, + BCDA94CD0587EE4F0089456C, + BC29FE420591377C0049A427, + BCE132EA059150E500764D81, + BCE132EB059150E500764D81, + BCE132EC059150E500764D81, + BCE132ED059150E500764D81, + BCE132EE059150E500764D81, + BC361BD405915CAD0048A214, + BC361BE6059167580048A214, + BC361BE8059167580048A214, + BCF262880591EA35005B79DC, + BCF262890591EA35005B79DC, + BC572334059223E9006F18E6, + BC5730AF05938EF9006F18E6, + BC2261C10594074200AE184B, + BC25817A05994F4000220DE8, + BC0C427D059A53DA00D5F37D, + BC0C46C1059A556900D5F37D, + BC0C46C2059A556900D5F37D, + BC0C46C3059A556900D5F37D, + BC0C46C4059A556900D5F37D, + BC0C46C5059A556900D5F37D, + BC0C46C6059A556900D5F37D, + BC0C46C7059A556900D5F37D, + BC0C46C8059A556900D5F37D, + BC0C46C9059A556900D5F37D, + BCA5B20B059A5B36003B2607, + BCA5B212059A5B36003B2607, + BCA5B213059A5B36003B2607, + BCA5B214059A5B36003B2607, + BCA5B219059A5B36003B2607, + BCA5B21C059A5B36003B2607, + BCA5B220059A5B36003B2607, + BCA5B222059A5B36003B2607, + BCA5B236059A5B36003B2607, + BCA5B237059A5B36003B2607, + BCA5B238059A5B36003B2607, + BCA5B239059A5B36003B2607, + BCA5B23B059A5B36003B2607, + BCA5B23D059A5B36003B2607, + BCA061D3059BE18C00341EC7, + BC72533705A26BD3000D5DF2, + BC72534105A26BD3000D5DF2, + BC72534205A26BD3000D5DF2, + BC72534305A26BD3000D5DF2, + BC72534505A26BD3000D5DF2, + BC01FFDF05A503F500A7ED81, + BC31C08805AB94C5003881B1, + BC21EAA405CEAB930080F446, + BCCF260005E2976F00E3D1DB, + BCCF260205E2976F00E3D1DB, + BCCF260305E2976F00E3D1DB, + BC24D3FA05E857890021BD1F, + BC13A9E105F29A2F0024CDF0, + BC536D9E05F43E7300F0E668, + BC536D9F05F43E7300F0E668, + BC7958F605FA81A700B5CFB0, + BC5E662D05FBEBF0000CEEBF, + BC25FA2605FE7F4E00BA8B5D, + BC25FA2A05FE7F4E00BA8B5D, + BCF3AFCA060B6C8100B2B13C, + BCF3AFCB060B6C8100B2B13C, + BCF3AFD7060B6C8100B2B13C, + BCF3AFD8060B6C8100B2B13C, + BCE65DE1060DFA4F007E6BDE, + BC3EB6F9060E6AAE004FF653, + BCFC56DB06146876001830F3, + BCFC56DE06146876001830F3, + BCFC644806147A90001830F3, + BCE03A47061A691600238E7E, + BCE03A62061ACCD700238E7E, + BCE03A63061ACCD700238E7E, + BCE03A64061ACCD700238E7E, + BCE03A65061ACCD700238E7E, + BCE03A66061ACCD700238E7E, + BC7483290620F81F002E7F0F, + BC56E1640652B33500D9027E, + BC56E8320653FB0700D9027E, + BC56E8330653FB0700D9027E, + BC56E8340653FB0700D9027E, + ); + }; + SplitCount = 1; + }; + }; + PBXProjectWorkspaceModule_GeometryKey_Rev15 = { + PBXProjectWorkspaceModule_SGTM_Geometry = { + _collapsingFrameDimension = 0; + _indexOfCollapsedView = 0; + _percentageOfCollapsedView = 0; + sizes = ( + "{{0, 0}, {236, 597}}", + "{{236, 0}, {788, 597}}", + ); + }; + }; + PBXProjectWorkspaceModule_OldDetailFrame = "{{0, 0}, {788, 140}}"; + PBXProjectWorkspaceModule_OldEditorFrame = "{{0, 140}, {788, 457}}"; + PBXProjectWorkspaceModule_OldSuperviewFrame = "{{236, 0}, {788, 597}}"; + PBXProjectWorkspaceModule_SGTM = { + PBXBottomSmartGroupGIDs = ( + 1C37FBAC04509CD000000102, + 1C37FAAC04509CD000000102, + 1C08E77C0454961000C914BD, + 1CC0EA4004350EF90044410B, + 1CC0EA4004350EF90041110B, + 1C37FABC05509CD000000102, + 1C37FABC05539CD112110102, + E2644B35053B69B200211256, + 1C37FABC04509CD000100104, + BCEB7EAB05893C5D005EF634, + ); + PBXSmartGroupTreeModuleColumnData = { + PBXSmartGroupTreeModuleColumnWidthsKey = ( + 22, + 22, + 175, + ); + PBXSmartGroupTreeModuleColumnsKey_v4 = ( + TargetStatusColumn, + SCMStatusColumn, + MainColumn, + ); + }; + PBXSmartGroupTreeModuleOutlineStateKey_v7 = { + PBXSmartGroupTreeModuleOutlineStateExpansionKey = ( + 089C166AFE841209C02AAC07, + F5964509039B4115014C8642, + F596450A039B411C014C8642, + F5CB1D6B0394AF0D01754549, + F5CB21870394B27F01754549, + F5B86F61039931F1010F9052, + F5CB219F0394B36D01754549, + 089C167CFE841241C02AAC07, + F58C12A803991E3601B0D373, + F5FF36EC0399FF2A01EE0299, + BC963A7003E268E700965C5C, + F5FF36ED0399FF3101EE0299, + 1C37FBAC04509CD000000102, + 1C08E77C0454961000C914BD, + 1C37FABC05509CD000000102, + ); + PBXSmartGroupTreeModuleOutlineStateSelectionKey = ( + ( + 24, + 4, + 2, + 0, + ), + ); + PBXSmartGroupTreeModuleOutlineStateVisibleRectKey = "{{0, 298}, {219, 579}}"; + }; + PBXTopSmartGroupGIDs = ( + ); + }; + }; + }, + ); + "PBXWorkspaceContents:PBXConfiguration.PBXModule.PBXBuildResultsModule" = { + }; + "PBXWorkspaceContents:PBXConfiguration.PBXModule.PBXCVSModule" = { + }; + "PBXWorkspaceContents:PBXConfiguration.PBXModule.PBXDebugCLIModule" = { + }; + "PBXWorkspaceContents:PBXConfiguration.PBXModule.PBXNavigatorGroup" = { + PBXSplitModuleInNavigatorKey = { + SplitCount = 1; + }; + }; + "PBXWorkspaceContents:PBXConfiguration.PBXModule.PBXProjectFindModule" = { + }; + "PBXWorkspaceContents:PBXConfiguration.PBXModule.PBXProjectWorkspaceModule" = { + PBXProjectWorkspaceModule_StateKey_Rev39 = { + PBXProjectWorkspaceModule_DEGV_Geometry = { + _collapsingFrameDimension = 0; + _indexOfCollapsedView = 0; + _percentageOfCollapsedView = 0; + isCollapsed = yes; + sizes = ( + "{{0, 0}, {788, 110}}", + "{{0, 110}, {788, 487}}", + ); + }; + PBXProjectWorkspaceModule_DataSourceSelectionKey_Rev6 = { + BoundsStr = "{{0, 0}, {773, 93}}"; + Rows = ( + 0, + ); + VisibleRectStr = "{{0, 0}, {773, 93}}"; + }; + PBXProjectWorkspaceModule_EditorOpen = true; + PBXProjectWorkspaceModule_EmbeddedNavigatorGroup = { + PBXSplitModuleInNavigatorKey = { + Split0 = { + bookmark = BCE03A4E061A691600238E7E; + history = ( + BCC8725C058561BB00054D38, + BCC87261058561BC00054D38, + BCC872970585620F00054D38, + BCB376BE05856BAB00CFDABB, + BCDA94B80587EE4F0089456C, + BCDA94BB0587EE4F0089456C, + BCDA94BC0587EE4F0089456C, + BCDA94BD0587EE4F0089456C, + BCDA94BF0587EE4F0089456C, + BCDA94C10587EE4F0089456C, + BC29FE3E0591377C0049A427, + BCE132E0059150E500764D81, + BCE132E1059150E500764D81, + BCE132E2059150E500764D81, + BCE132E3059150E500764D81, + BCE132E4059150E500764D81, + BCE132E5059150E500764D81, + BCE132E7059150E500764D81, + BC361BE1059167580048A214, + BCF262860591EA35005B79DC, + BC2261BC0594074200AE184B, + BC2261BD0594074200AE184B, + BC2261BE0594074200AE184B, + BC25817805994F4000220DE8, + BC0C427B059A53DA00D5F37D, + BCA5B1FA059A5B36003B2607, + BCA5B1FC059A5B36003B2607, + BCA5B1FD059A5B36003B2607, + BCA5B1FF059A5B36003B2607, + BC72531D05A26BD3000D5DF2, + BC72531E05A26BD3000D5DF2, + BC01FFDB05A503F500A7ED81, + BC31C08305AB94C5003881B1, + BCE4F4B205AEBC6100A5A030, + BC21EA9C05CEAB930080F446, + BC98EB3D05D04B010099F5CA, + BC98EB3F05D04B010099F5CA, + BCCFC9FB05D471F400651BC7, + BCCF25EF05E2976F00E3D1DB, + BC24D3F405E857890021BD1F, + BC24D3F505E857890021BD1F, + BC24D3F605E857890021BD1F, + BC24D3F705E857890021BD1F, + BC13A9DD05F29A2F0024CDF0, + BC536D9705F43E7300F0E668, + BC536D9805F43E7300F0E668, + BC25FA2105FE7F4E00BA8B5D, + BC25FA2305FE7F4E00BA8B5D, + BC25FA2405FE7F4E00BA8B5D, + BC25FA2505FE7F4E00BA8B5D, + BCA7197E05F39694009B5E25, + BCF3AFB8060B6C8100B2B13C, + BCF3AFB9060B6C8100B2B13C, + BCF3AFBD060B6C8100B2B13C, + BCF3AFBE060B6C8100B2B13C, + BCF3AFBF060B6C8100B2B13C, + BCF3AFC0060B6C8100B2B13C, + BCF3AFC1060B6C8100B2B13C, + BCF3AFC2060B6C8100B2B13C, + BCF3AFC3060B6C8100B2B13C, + BCF3AFC4060B6C8100B2B13C, + BCF3AFC5060B6C8100B2B13C, + BCF3AFC6060B6C8100B2B13C, + BCF3AFC7060B6C8100B2B13C, + BCE65DDC060DFA4F007E6BDE, + BCE65DDD060DFA4F007E6BDE, + BCE65DDE060DFA4F007E6BDE, + BC3EB6F6060E6AAE004FF653, + BCFC56D306146876001830F3, + BCFC56D706146876001830F3, + BCFC5D84061473D1001830F3, + BCFC5D87061473D1001830F3, + BCFC5D88061473D1001830F3, + BCFC5D89061473D1001830F3, + BCFC5D8A061473D1001830F3, + BCFC5D8B061473D1001830F3, + BCFC5D8C061473D1001830F3, + BCFC5D8D061473D1001830F3, + BCFC5D8E061473D1001830F3, + BCFC5D8F061473D1001830F3, + BCFC5D90061473D1001830F3, + BCFC5D91061473D1001830F3, + BCFC5D92061473D1001830F3, + BCFC644306147A90001830F3, + BCFC644406147A90001830F3, + BCE03A3E061A691600238E7E, + BCE03A3F061A691600238E7E, + BCE03A40061A691600238E7E, + BCE03A41061A691600238E7E, + BCE03A42061A691600238E7E, + BCE03A43061A691600238E7E, + BCE03A44061A691600238E7E, + BCE03A45061A691600238E7E, + BCFC644206147A90001830F3, + ); + prevStack = ( + BCC871AB058556A400054D38, + BCC871FF05855C3200054D38, + BCC872480585618E00054D38, + BCC8724D058561A200054D38, + BCC87252058561B100054D38, + BCC87259058561BB00054D38, + BCC8725E058561BB00054D38, + BCC87263058561BC00054D38, + BCC87272058561C200054D38, + BCB376C305856BAB00CFDABB, + BCB376C405856BAB00CFDABB, + BCDA94C30587EE4F0089456C, + BCDA94C40587EE4F0089456C, + BCDA94C50587EE4F0089456C, + BCDA94C70587EE4F0089456C, + BCDA94C80587EE4F0089456C, + BCDA94C90587EE4F0089456C, + BCDA94CA0587EE4F0089456C, + BCDA94CB0587EE4F0089456C, + BCDA94CD0587EE4F0089456C, + BC29FE420591377C0049A427, + BCE132EA059150E500764D81, + BCE132EB059150E500764D81, + BCE132EC059150E500764D81, + BCE132ED059150E500764D81, + BCE132EE059150E500764D81, + BC361BD405915CAD0048A214, + BC361BE6059167580048A214, + BC361BE8059167580048A214, + BCF262880591EA35005B79DC, + BCF262890591EA35005B79DC, + BC572334059223E9006F18E6, + BC5730AF05938EF9006F18E6, + BC2261C10594074200AE184B, + BC25817A05994F4000220DE8, + BC0C427D059A53DA00D5F37D, + BC0C46C1059A556900D5F37D, + BC0C46C2059A556900D5F37D, + BC0C46C3059A556900D5F37D, + BC0C46C4059A556900D5F37D, + BC0C46C5059A556900D5F37D, + BC0C46C6059A556900D5F37D, + BC0C46C7059A556900D5F37D, + BC0C46C8059A556900D5F37D, + BC0C46C9059A556900D5F37D, + BCA5B20B059A5B36003B2607, + BCA5B212059A5B36003B2607, + BCA5B213059A5B36003B2607, + BCA5B214059A5B36003B2607, + BCA5B219059A5B36003B2607, + BCA5B21C059A5B36003B2607, + BCA5B220059A5B36003B2607, + BCA5B222059A5B36003B2607, + BCA5B236059A5B36003B2607, + BCA5B237059A5B36003B2607, + BCA5B238059A5B36003B2607, + BCA5B239059A5B36003B2607, + BCA5B23B059A5B36003B2607, + BCA5B23D059A5B36003B2607, + BCA061D3059BE18C00341EC7, + BC72533705A26BD3000D5DF2, + BC72534105A26BD3000D5DF2, + BC72534205A26BD3000D5DF2, + BC72534305A26BD3000D5DF2, + BC72534505A26BD3000D5DF2, + BC01FFDF05A503F500A7ED81, + BC31C08805AB94C5003881B1, + BC21EAA405CEAB930080F446, + BCCF260005E2976F00E3D1DB, + BCCF260205E2976F00E3D1DB, + BCCF260305E2976F00E3D1DB, + BC24D3FA05E857890021BD1F, + BC13A9E105F29A2F0024CDF0, + BC536D9E05F43E7300F0E668, + BC536D9F05F43E7300F0E668, + BC7958F605FA81A700B5CFB0, + BC5E662D05FBEBF0000CEEBF, + BC25FA2605FE7F4E00BA8B5D, + BC25FA2A05FE7F4E00BA8B5D, + BCF3AFCA060B6C8100B2B13C, + BCF3AFCB060B6C8100B2B13C, + BCF3AFD7060B6C8100B2B13C, + BCF3AFD8060B6C8100B2B13C, + BCE65DE1060DFA4F007E6BDE, + BC3EB6F9060E6AAE004FF653, + BCFC56DB06146876001830F3, + BCFC56DE06146876001830F3, + BCFC644806147A90001830F3, + BCE03A46061A691600238E7E, + BCE03A47061A691600238E7E, + BCE03A48061A691600238E7E, + BCE03A49061A691600238E7E, + BCE03A4A061A691600238E7E, + BCE03A4B061A691600238E7E, + BCE03A4C061A691600238E7E, + BCE03A4D061A691600238E7E, + ); + }; + SplitCount = 1; + }; + }; + PBXProjectWorkspaceModule_GeometryKey_Rev15 = { + PBXProjectWorkspaceModule_SGTM_Geometry = { + _collapsingFrameDimension = 0; + _indexOfCollapsedView = 0; + _percentageOfCollapsedView = 0; + sizes = ( + "{{0, 0}, {236, 597}}", + "{{236, 0}, {788, 597}}", + ); + }; + }; + PBXProjectWorkspaceModule_OldDetailFrame = "{{0, 0}, {788, 110}}"; + PBXProjectWorkspaceModule_OldEditorFrame = "{{0, 110}, {788, 487}}"; + PBXProjectWorkspaceModule_OldSuperviewFrame = "{{236, 0}, {788, 597}}"; + PBXProjectWorkspaceModule_SGTM = { + PBXBottomSmartGroupGIDs = ( + 1C37FBAC04509CD000000102, + 1C37FAAC04509CD000000102, + 1C08E77C0454961000C914BD, + 1CC0EA4004350EF90044410B, + 1CC0EA4004350EF90041110B, + 1C37FABC05509CD000000102, + 1C37FABC05539CD112110102, + E2644B35053B69B200211256, + 1C37FABC04509CD000100104, + BCEB7EAB05893C5D005EF634, + ); + PBXSmartGroupTreeModuleColumnData = { + PBXSmartGroupTreeModuleColumnWidthsKey = ( + 22, + 22, + 175, + ); + PBXSmartGroupTreeModuleColumnsKey_v4 = ( + TargetStatusColumn, + SCMStatusColumn, + MainColumn, + ); + }; + PBXSmartGroupTreeModuleOutlineStateKey_v7 = { + PBXSmartGroupTreeModuleOutlineStateExpansionKey = ( + 089C166AFE841209C02AAC07, + F5964509039B4115014C8642, + F596450A039B411C014C8642, + F5CB1D6B0394AF0D01754549, + F5CB21860394B27B01754549, + F5CB219F0394B36D01754549, + 089C167CFE841241C02AAC07, + F58C12A303991D3A01B0D373, + BC689891046AC30F00A80001, + F5FF36EC0399FF2A01EE0299, + BC963A7003E268E700965C5C, + F5FF36ED0399FF3101EE0299, + 1C37FBAC04509CD000000102, + 1C08E77C0454961000C914BD, + 1C37FABC05509CD000000102, + ); + PBXSmartGroupTreeModuleOutlineStateSelectionKey = ( + ( + 92, + 91, + 86, + 84, + 0, + ), + ); + PBXSmartGroupTreeModuleOutlineStateVisibleRectKey = "{{0, 1278}, {219, 579}}"; + }; + PBXTopSmartGroupGIDs = ( + ); + }; + }; + }; + "PBXWorkspaceContents:PBXConfiguration.PBXModule.PBXRunSessionModule" = { + LauncherConfigVersion = 3; + Runner = { + HorizontalSplitView = { + _collapsingFrameDimension = 0; + _indexOfCollapsedView = 0; + _percentageOfCollapsedView = 0; + isCollapsed = yes; + sizes = ( + "{{0, 0}, {493, 167}}", + "{{0, 176}, {493, 267}}", + ); + }; + VerticalSplitView = { + _collapsingFrameDimension = 0; + _indexOfCollapsedView = 0; + _percentageOfCollapsedView = 0; + isCollapsed = yes; + sizes = ( + "{{0, 0}, {405, 443}}", + "{{414, 0}, {514, 443}}", + ); + }; + }; + }; + PBXWorkspaceGeometries = ( + { + Frame = "{{0, 0}, {1024, 597}}"; + PBXProjectWorkspaceModule_GeometryKey_Rev15 = { + }; + RubberWindowFrame = "4 107 1024 639 0 0 1024 746 "; + }, + ); + "PBXWorkspaceGeometries:PBXConfiguration.PBXModule.PBXBuildResultsModule" = { + Frame = "{{0, 0}, {655, 626}}"; + PBXModuleWindowStatusBarHidden = YES; + RubberWindowFrame = "191 99 655 647 0 0 1024 746 "; + }; + "PBXWorkspaceGeometries:PBXConfiguration.PBXModule.PBXCVSModule" = { + Frame = "{{0, 0}, {482, 276}}"; + RubberWindowFrame = "262 214 482 318 0 0 1024 746 "; + }; + "PBXWorkspaceGeometries:PBXConfiguration.PBXModule.PBXDebugCLIModule" = { + Frame = "{{0, 0}, {400, 201}}"; + PBXModuleWindowStatusBarHidden = YES; + RubberWindowFrame = "50 718 400 222 0 0 1024 746 "; + }; + "PBXWorkspaceGeometries:PBXConfiguration.PBXModule.PBXNavigatorGroup" = { + Frame = "{{0, 0}, {750, 481}}"; + PBXModuleWindowStatusBarHidden = YES; + RubberWindowFrame = "15 239 750 502 0 0 1024 746 "; + }; + "PBXWorkspaceGeometries:PBXConfiguration.PBXModule.PBXProjectFindModule" = { + Frame = "{{0, 0}, {665, 371}}"; + RubberWindowFrame = "214 333 665 413 0 0 1024 746 "; + }; + "PBXWorkspaceGeometries:PBXConfiguration.PBXModule.PBXProjectWorkspaceModule" = { + Frame = "{{0, 0}, {1024, 597}}"; + PBXProjectWorkspaceModule_GeometryKey_Rev15 = { + }; + RubberWindowFrame = "4 107 1024 639 0 0 1024 746 "; + }; + "PBXWorkspaceGeometries:PBXConfiguration.PBXModule.PBXRunSessionModule" = { + Frame = "{{0, 0}, {745, 443}}"; + PBXModuleWindowStatusBarHidden = YES; + RubberWindowFrame = "11 277 745 464 0 0 1024 746 "; + }; + PBXWorkspaceStateSaveDate = 184192409; + }; + perUserProjectItems = { + BC0A12C00A3B1A9700B30B7A /* PBXTextBookmark */ = BC0A12C00A3B1A9700B30B7A /* PBXTextBookmark */; + BC0A12C10A3B1A9700B30B7A /* PBXTextBookmark */ = BC0A12C10A3B1A9700B30B7A /* PBXTextBookmark */; + BC0A12C40A3B1A9700B30B7A /* PBXTextBookmark */ = BC0A12C40A3B1A9700B30B7A /* PBXTextBookmark */; + BC0A15E50A3C866900B30B7A /* PBXTextBookmark */ = BC0A15E50A3C866900B30B7A /* PBXTextBookmark */; + BC0A15E60A3C866900B30B7A /* PBXTextBookmark */ = BC0A15E60A3C866900B30B7A /* PBXTextBookmark */; + BC0B53310982BC590033B1A7 /* PBXBookmark */ = BC0B53310982BC590033B1A7 /* PBXBookmark */; + BC0B53830982BC590033B1A7 /* PBXBookmark */ = BC0B53830982BC590033B1A7 /* PBXBookmark */; + BC0B53A70982C7BE0033B1A7 /* PBXBookmark */ = BC0B53A70982C7BE0033B1A7 /* PBXBookmark */; + BC0B53A90982C7BE0033B1A7 /* PBXTextBookmark */ = BC0B53A90982C7BE0033B1A7 /* PBXTextBookmark */; + BC0B53AD0982C7BE0033B1A7 /* PBXBookmark */ = BC0B53AD0982C7BE0033B1A7 /* PBXBookmark */; + BC0B53AE0982C7BE0033B1A7 /* PBXTextBookmark */ = BC0B53AE0982C7BE0033B1A7 /* PBXTextBookmark */; + BC0B53B00982C7BE0033B1A7 /* PBXTextBookmark */ = BC0B53B00982C7BE0033B1A7 /* PBXTextBookmark */; + BC0B54100983F95F0033B1A7 /* PBXTextBookmark */ = BC0B54100983F95F0033B1A7 /* PBXTextBookmark */; + BC0B54110983F95F0033B1A7 /* PBXTextBookmark */ = BC0B54110983F95F0033B1A7 /* PBXTextBookmark */; + BC0B54130983F95F0033B1A7 /* PBXTextBookmark */ = BC0B54130983F95F0033B1A7 /* PBXTextBookmark */; + BC0B54140983F95F0033B1A7 /* PBXTextBookmark */ = BC0B54140983F95F0033B1A7 /* PBXTextBookmark */; + BC0B54250984F9920033B1A7 /* PBXTextBookmark */ = BC0B54250984F9920033B1A7 /* PBXTextBookmark */; + BC0B54260984F9920033B1A7 /* PBXTextBookmark */ = BC0B54260984F9920033B1A7 /* PBXTextBookmark */; + BC0B54280984F9920033B1A7 /* PBXTextBookmark */ = BC0B54280984F9920033B1A7 /* PBXTextBookmark */; + BC0B54290984F9920033B1A7 /* PBXTextBookmark */ = BC0B54290984F9920033B1A7 /* PBXTextBookmark */; + BC0B546509850E2D0033B1A7 /* PBXTextBookmark */ = BC0B546509850E2D0033B1A7 /* PBXTextBookmark */; + BC0B546E09850E2D0033B1A7 /* PBXTextBookmark */ = BC0B546E09850E2D0033B1A7 /* PBXTextBookmark */; + BC0C651B08394D8200D44ACA /* PBXTextBookmark */ = BC0C651B08394D8200D44ACA /* PBXTextBookmark */; + BC0C651D08394D8200D44ACA /* PBXTextBookmark */ = BC0C651D08394D8200D44ACA /* PBXTextBookmark */; + BC0C651F08394D8200D44ACA /* PBXTextBookmark */ = BC0C651F08394D8200D44ACA /* PBXTextBookmark */; + BC0C652208394D8200D44ACA /* PBXTextBookmark */ = BC0C652208394D8200D44ACA /* PBXTextBookmark */; + BC0EF51B09FF8A9B0049BE71 /* PBXTextBookmark */ = BC0EF51B09FF8A9B0049BE71 /* PBXTextBookmark */; + BC0EF51C09FF8A9B0049BE71 /* PBXTextBookmark */ = BC0EF51C09FF8A9B0049BE71 /* PBXTextBookmark */; + BC0EF51D09FF8A9B0049BE71 /* PBXTextBookmark */ = BC0EF51D09FF8A9B0049BE71 /* PBXTextBookmark */; + BC0EF51E09FF8A9B0049BE71 /* PBXTextBookmark */ = BC0EF51E09FF8A9B0049BE71 /* PBXTextBookmark */; + BC0EF51F09FF8A9B0049BE71 /* PBXTextBookmark */ = BC0EF51F09FF8A9B0049BE71 /* PBXTextBookmark */; + BC0EF52009FF8A9B0049BE71 /* PBXTextBookmark */ = BC0EF52009FF8A9B0049BE71 /* PBXTextBookmark */; + BC0EF52A09FF8A9B0049BE71 /* PBXTextBookmark */ = BC0EF52A09FF8A9B0049BE71 /* PBXTextBookmark */; + BC0EF52B09FF8A9B0049BE71 /* PBXTextBookmark */ = BC0EF52B09FF8A9B0049BE71 /* PBXTextBookmark */; + BC0EF52C09FF8A9B0049BE71 /* PBXTextBookmark */ = BC0EF52C09FF8A9B0049BE71 /* PBXTextBookmark */; + BC0EF52F09FF8A9B0049BE71 /* PBXTextBookmark */ = BC0EF52F09FF8A9B0049BE71 /* PBXTextBookmark */; + BC121FAE0881D9CC0062B12B /* PBXTextBookmark */ = BC121FAE0881D9CC0062B12B /* PBXTextBookmark */; + BC121FB00881D9CC0062B12B /* PBXTextBookmark */ = BC121FB00881D9CC0062B12B /* PBXTextBookmark */; + BC121FE90881DA590062B12B /* PBXTextBookmark */ = BC121FE90881DA590062B12B /* PBXTextBookmark */; + BC121FEC0881DA590062B12B /* PBXTextBookmark */ = BC121FEC0881DA590062B12B /* PBXTextBookmark */; + BC236DBA08A2715100F0D6B1 /* PBXTextBookmark */ = BC236DBA08A2715100F0D6B1 /* PBXTextBookmark */; + BC236DE008A2715100F0D6B1 /* PBXTextBookmark */ = BC236DE008A2715100F0D6B1 /* PBXTextBookmark */; + BC236DE308A2715100F0D6B1 /* PBXTextBookmark */ = BC236DE308A2715100F0D6B1 /* PBXTextBookmark */; + BC26ED4208322950003F5E44 /* PBXTextBookmark */ = BC26ED4208322950003F5E44 /* PBXTextBookmark */; + BC26ED4308322950003F5E44 /* PBXTextBookmark */ = BC26ED4308322950003F5E44 /* PBXTextBookmark */; + BC26ED4408322950003F5E44 /* PBXTextBookmark */ = BC26ED4408322950003F5E44 /* PBXTextBookmark */; + BC26ED4508322950003F5E44 /* PBXTextBookmark */ = BC26ED4508322950003F5E44 /* PBXTextBookmark */; + BC26ED4708322950003F5E44 /* PBXTextBookmark */ = BC26ED4708322950003F5E44 /* PBXTextBookmark */; + BC26ED4A08322950003F5E44 /* PBXTextBookmark */ = BC26ED4A08322950003F5E44 /* PBXTextBookmark */; + BC26ED4B08322950003F5E44 /* PBXTextBookmark */ = BC26ED4B08322950003F5E44 /* PBXTextBookmark */; + BC26ED4C08322950003F5E44 /* PBXTextBookmark */ = BC26ED4C08322950003F5E44 /* PBXTextBookmark */; + BC26ED4D08322950003F5E44 /* PBXTextBookmark */ = BC26ED4D08322950003F5E44 /* PBXTextBookmark */; + BC26ED4F08322950003F5E44 /* PBXTextBookmark */ = BC26ED4F08322950003F5E44 /* PBXTextBookmark */; + BC26ED5308322950003F5E44 /* PBXTextBookmark */ = BC26ED5308322950003F5E44 /* PBXTextBookmark */; + BC281F870AF8F6190039EAB8 /* PBXTextBookmark */ = BC281F870AF8F6190039EAB8 /* PBXTextBookmark */; + BC281F890AF8F6190039EAB8 /* PBXTextBookmark */ = BC281F890AF8F6190039EAB8 /* PBXTextBookmark */; + BC2833C0082B80AA00D6CC47 /* PBXTextBookmark */ = BC2833C0082B80AA00D6CC47 /* PBXTextBookmark */; + BC2833C1082B80AA00D6CC47 /* PBXTextBookmark */ = BC2833C1082B80AA00D6CC47 /* PBXTextBookmark */; + BC357D5C0A01290E00A27C1C /* PBXTextBookmark */ = BC357D5C0A01290E00A27C1C /* PBXTextBookmark */; + BC3C722608290C9C00E23B52 /* PBXTextBookmark */ = BC3C722608290C9C00E23B52 /* PBXTextBookmark */; + BC3C722708290C9C00E23B52 /* PBXTextBookmark */ = BC3C722708290C9C00E23B52 /* PBXTextBookmark */; + BC4C59CA0AAEAD6500CD1FB0 /* PBXTextBookmark */ = BC4C59CA0AAEAD6500CD1FB0 /* PBXTextBookmark */; + BC55F4C3083D49A5008C9E55 /* PBXTextBookmark */ = BC55F4C3083D49A5008C9E55 /* PBXTextBookmark */; + BC610865082569BF00596B77 /* PBXTextBookmark */ = BC610865082569BF00596B77 /* PBXTextBookmark */; + BC617FCC0AF8BEA700E1268F /* PBXTextBookmark */ = BC617FCC0AF8BEA700E1268F /* PBXTextBookmark */; + BC617FCD0AF8BEA700E1268F /* PBXTextBookmark */ = BC617FCD0AF8BEA700E1268F /* PBXTextBookmark */; + BC617FCE0AF8BEA700E1268F /* PBXTextBookmark */ = BC617FCE0AF8BEA700E1268F /* PBXTextBookmark */; + BC617FCF0AF8BEA700E1268F /* PBXTextBookmark */ = BC617FCF0AF8BEA700E1268F /* PBXTextBookmark */; + BC617FD20AF8BEA700E1268F /* PBXTextBookmark */ = BC617FD20AF8BEA700E1268F /* PBXTextBookmark */; + BC617FD60AF8BEA700E1268F /* PBXTextBookmark */ = BC617FD60AF8BEA700E1268F /* PBXTextBookmark */; + BC617FD70AF8BEA700E1268F /* PBXTextBookmark */ = BC617FD70AF8BEA700E1268F /* PBXTextBookmark */; + BC617FD80AF8BEA700E1268F /* PBXTextBookmark */ = BC617FD80AF8BEA700E1268F /* PBXTextBookmark */; + BC680CF00834C2A400ABF3B8 /* PBXTextBookmark */ = BC680CF00834C2A400ABF3B8 /* PBXTextBookmark */; + BC680CF60834C2A400ABF3B8 /* PBXTextBookmark */ = BC680CF60834C2A400ABF3B8 /* PBXTextBookmark */; + BC680CF70834C2A400ABF3B8 /* PBXTextBookmark */ = BC680CF70834C2A400ABF3B8 /* PBXTextBookmark */; + BC680CF80834C2A400ABF3B8 /* PBXTextBookmark */ = BC680CF80834C2A400ABF3B8 /* PBXTextBookmark */; + BC680CF90834C2A400ABF3B8 /* PBXTextBookmark */ = BC680CF90834C2A400ABF3B8 /* PBXTextBookmark */; + BC680CFA0834C2A400ABF3B8 /* PBXTextBookmark */ = BC680CFA0834C2A400ABF3B8 /* PBXTextBookmark */; + BC680CFB0834C2A400ABF3B8 /* PBXTextBookmark */ = BC680CFB0834C2A400ABF3B8 /* PBXTextBookmark */; + BC680CFC0834C2A400ABF3B8 /* PBXTextBookmark */ = BC680CFC0834C2A400ABF3B8 /* PBXTextBookmark */; + BC680CFD0834C2A400ABF3B8 /* PBXTextBookmark */ = BC680CFD0834C2A400ABF3B8 /* PBXTextBookmark */; + BC680CFE0834C2A400ABF3B8 /* PBXTextBookmark */ = BC680CFE0834C2A400ABF3B8 /* PBXTextBookmark */; + BC680CFF0834C2A400ABF3B8 /* PBXTextBookmark */ = BC680CFF0834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D000834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D000834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D010834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D010834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D020834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D020834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D030834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D030834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D040834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D040834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D050834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D050834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D060834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D060834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D070834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D070834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D080834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D080834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D090834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D090834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D0A0834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D0A0834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D0B0834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D0B0834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D0C0834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D0C0834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D0D0834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D0D0834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D0E0834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D0E0834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D0F0834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D0F0834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D100834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D100834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D110834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D110834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D120834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D120834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D130834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D130834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D170834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D170834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D180834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D180834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D190834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D190834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D290834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D290834C2A400ABF3B8 /* PBXTextBookmark */; + BC680D2A0834C2A400ABF3B8 /* PBXTextBookmark */ = BC680D2A0834C2A400ABF3B8 /* PBXTextBookmark */; + BC680DDE0834EE2500ABF3B8 /* PBXTextBookmark */ = BC680DDE0834EE2500ABF3B8 /* PBXTextBookmark */; + BC680DE10834EE2500ABF3B8 /* PBXTextBookmark */ = BC680DE10834EE2500ABF3B8 /* PBXTextBookmark */; + BC680DE30834EE2500ABF3B8 /* PBXTextBookmark */ = BC680DE30834EE2500ABF3B8 /* PBXTextBookmark */; + BC680DE40834EE2500ABF3B8 /* PBXTextBookmark */ = BC680DE40834EE2500ABF3B8 /* PBXTextBookmark */; + BC680DE50834EE2500ABF3B8 /* PBXTextBookmark */ = BC680DE50834EE2500ABF3B8 /* PBXTextBookmark */; + BC6AD6E50AF92B7F00D248C7 /* PBXTextBookmark */ = BC6AD6E50AF92B7F00D248C7 /* PBXTextBookmark */; + BC6AD79A0AF9FCC700D248C7 /* PBXTextBookmark */ = BC6AD79A0AF9FCC700D248C7 /* PBXTextBookmark */; + BC6AD79C0AF9FCC700D248C7 /* PBXTextBookmark */ = BC6AD79C0AF9FCC700D248C7 /* PBXTextBookmark */; + BC6BC25B0872834900C3C59C /* PBXTextBookmark */ = BC6BC25B0872834900C3C59C /* PBXTextBookmark */; + BC6BC25C0872834900C3C59C /* PBXTextBookmark */ = BC6BC25C0872834900C3C59C /* PBXTextBookmark */; + BC6BC26B087284F000C3C59C /* PBXTextBookmark */ = BC6BC26B087284F000C3C59C /* PBXTextBookmark */; + BC6BC26C087284F000C3C59C /* PBXTextBookmark */ = BC6BC26C087284F000C3C59C /* PBXTextBookmark */; + BC6BC28E087284F000C3C59C /* PBXTextBookmark */ = BC6BC28E087284F000C3C59C /* PBXTextBookmark */; + BC6BC28F087284F000C3C59C /* PBXTextBookmark */ = BC6BC28F087284F000C3C59C /* PBXTextBookmark */; + BC753A4A0A476E5D00DC2344 /* PBXTextBookmark */ = BC753A4A0A476E5D00DC2344 /* PBXTextBookmark */; + BC753A5E0A485A2200DC2344 /* PBXTextBookmark */ = BC753A5E0A485A2200DC2344 /* PBXTextBookmark */; + BC79A3B509A60E8F008FF8BC /* PBXTextBookmark */ = BC79A3B509A60E8F008FF8BC /* PBXTextBookmark */; + BC79A3B709A60E8F008FF8BC /* PBXTextBookmark */ = BC79A3B709A60E8F008FF8BC /* PBXTextBookmark */; + BC79A3B809A60E8F008FF8BC /* PBXTextBookmark */ = BC79A3B809A60E8F008FF8BC /* PBXTextBookmark */; + BC79A3B909A60E8F008FF8BC /* PBXTextBookmark */ = BC79A3B909A60E8F008FF8BC /* PBXTextBookmark */; + BC79A3BB09A60E8F008FF8BC /* PBXTextBookmark */ = BC79A3BB09A60E8F008FF8BC /* PBXTextBookmark */; + BC79A3BC09A60E8F008FF8BC /* PBXTextBookmark */ = BC79A3BC09A60E8F008FF8BC /* PBXTextBookmark */; + BC79A3BD09A60E8F008FF8BC /* PBXTextBookmark */ = BC79A3BD09A60E8F008FF8BC /* PBXTextBookmark */; + BC79A3BE09A60E8F008FF8BC /* PBXTextBookmark */ = BC79A3BE09A60E8F008FF8BC /* PBXTextBookmark */; + BC79A3C309A60E8F008FF8BC /* PBXTextBookmark */ = BC79A3C309A60E8F008FF8BC /* PBXTextBookmark */; + BC79A3C609A60E8F008FF8BC /* PBXTextBookmark */ = BC79A3C609A60E8F008FF8BC /* PBXTextBookmark */; + BC79A3D309A60E8F008FF8BC /* PBXTextBookmark */ = BC79A3D309A60E8F008FF8BC /* PBXTextBookmark */; + BC79A3F709A60E8F008FF8BC /* PBXTextBookmark */ = BC79A3F709A60E8F008FF8BC /* PBXTextBookmark */; + BC833B560AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B560AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B570AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B570AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B580AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B580AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B590AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B590AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B5A0AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B5A0AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B5B0AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B5B0AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B5C0AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B5C0AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B5D0AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B5D0AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B5E0AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B5E0AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B5F0AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B5F0AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B600AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B600AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B610AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B610AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B620AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B620AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B630AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B630AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B640AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B640AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B650AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B650AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B660AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B660AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B670AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B670AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B680AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B680AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B690AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B690AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B6A0AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B6A0AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B6B0AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B6B0AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B720AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B720AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B8A0AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B8A0AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833B900AAC39D000D8F5F7 /* PBXTextBookmark */ = BC833B900AAC39D000D8F5F7 /* PBXTextBookmark */; + BC833BA30AAC45C600D8F5F7 /* PBXTextBookmark */ = BC833BA30AAC45C600D8F5F7 /* PBXTextBookmark */; + BC833BA40AAC45C600D8F5F7 /* PBXTextBookmark */ = BC833BA40AAC45C600D8F5F7 /* PBXTextBookmark */; + BC833BA80AAC45C600D8F5F7 /* PBXTextBookmark */ = BC833BA80AAC45C600D8F5F7 /* PBXTextBookmark */; + BC833BF00AAC50D600D8F5F7 /* PBXTextBookmark */ = BC833BF00AAC50D600D8F5F7 /* PBXTextBookmark */; + BC833C110AAC566A00D8F5F7 /* PBXTextBookmark */ = BC833C110AAC566A00D8F5F7 /* PBXTextBookmark */; + BC833C1C0AAC5F0300D8F5F7 /* PBXTextBookmark */ = BC833C1C0AAC5F0300D8F5F7 /* PBXTextBookmark */; + BC833C1D0AAC5F0300D8F5F7 /* PBXTextBookmark */ = BC833C1D0AAC5F0300D8F5F7 /* PBXTextBookmark */; + BC833C1E0AAC5F0300D8F5F7 /* PBXTextBookmark */ = BC833C1E0AAC5F0300D8F5F7 /* PBXTextBookmark */; + BC833C5A0AAC915300D8F5F7 /* PBXTextBookmark */ = BC833C5A0AAC915300D8F5F7 /* PBXTextBookmark */; + BC98E7140A39A71A0048ADFF /* PBXTextBookmark */ = BC98E7140A39A71A0048ADFF /* PBXTextBookmark */; + BC98E7160A39A71A0048ADFF /* PBXTextBookmark */ = BC98E7160A39A71A0048ADFF /* PBXTextBookmark */; + BC98E9E20A39F56F0048ADFF /* PBXTextBookmark */ = BC98E9E20A39F56F0048ADFF /* PBXTextBookmark */; + BC98E9E40A39F56F0048ADFF /* PBXTextBookmark */ = BC98E9E40A39F56F0048ADFF /* PBXTextBookmark */; + BC98E9E60A39F56F0048ADFF /* PBXTextBookmark */ = BC98E9E60A39F56F0048ADFF /* PBXTextBookmark */; + BC98E9E80A39F56F0048ADFF /* PBXTextBookmark */ = BC98E9E80A39F56F0048ADFF /* PBXTextBookmark */; + BC98E9EA0A39F56F0048ADFF /* PBXTextBookmark */ = BC98E9EA0A39F56F0048ADFF /* PBXTextBookmark */; + BC98E9EC0A39F56F0048ADFF /* PBXTextBookmark */ = BC98E9EC0A39F56F0048ADFF /* PBXTextBookmark */; + BC98E9EE0A39F56F0048ADFF /* PBXTextBookmark */ = BC98E9EE0A39F56F0048ADFF /* PBXTextBookmark */; + BC98E9EF0A39F56F0048ADFF /* PBXTextBookmark */ = BC98E9EF0A39F56F0048ADFF /* PBXTextBookmark */; + BC98E9F00A39F56F0048ADFF /* PBXTextBookmark */ = BC98E9F00A39F56F0048ADFF /* PBXTextBookmark */; + BC98E9F10A39F56F0048ADFF /* PBXTextBookmark */ = BC98E9F10A39F56F0048ADFF /* PBXTextBookmark */; + BC98E9F20A39F56F0048ADFF /* PBXTextBookmark */ = BC98E9F20A39F56F0048ADFF /* PBXTextBookmark */; + BC98E9F50A39F56F0048ADFF /* PBXTextBookmark */ = BC98E9F50A39F56F0048ADFF /* PBXTextBookmark */; + BC98E9F90A39F56F0048ADFF /* PBXTextBookmark */ = BC98E9F90A39F56F0048ADFF /* PBXTextBookmark */; + BC98E9FC0A39F56F0048ADFF /* PBXTextBookmark */ = BC98E9FC0A39F56F0048ADFF /* PBXTextBookmark */; + BC98EA010A39F56F0048ADFF /* PBXTextBookmark */ = BC98EA010A39F56F0048ADFF /* PBXTextBookmark */; + BC98EA080A39F56F0048ADFF /* PBXTextBookmark */ = BC98EA080A39F56F0048ADFF /* PBXTextBookmark */; + BC98EA090A39F56F0048ADFF /* PBXTextBookmark */ = BC98EA090A39F56F0048ADFF /* PBXTextBookmark */; + BC98EA0B0A39F56F0048ADFF /* PBXTextBookmark */ = BC98EA0B0A39F56F0048ADFF /* PBXTextBookmark */; + BC98EA0C0A39F56F0048ADFF /* PBXTextBookmark */ = BC98EA0C0A39F56F0048ADFF /* PBXTextBookmark */; + BC98EA280A39F7910048ADFF /* PBXTextBookmark */ = BC98EA280A39F7910048ADFF /* PBXTextBookmark */; + BC9A268309261479003E5241 /* PBXTextBookmark */ = BC9A268309261479003E5241 /* PBXTextBookmark */; + BC9A26AF09261479003E5241 /* PBXTextBookmark */ = BC9A26AF09261479003E5241 /* PBXTextBookmark */; + BC9D31EF084121870027F5CB /* PBXTextBookmark */ = BC9D31EF084121870027F5CB /* PBXTextBookmark */; + BCBE23E0093B389D00FAD628 /* PBXTextBookmark */ = BCBE23E0093B389D00FAD628 /* PBXTextBookmark */; + BCBE23E1093B389D00FAD628 /* PBXTextBookmark */ = BCBE23E1093B389D00FAD628 /* PBXTextBookmark */; + BCBE2419093B3A9F00FAD628 /* PBXTextBookmark */ = BCBE2419093B3A9F00FAD628 /* PBXTextBookmark */; + BCBE26FB093B3CC100FAD628 /* PBXTextBookmark */ = BCBE26FB093B3CC100FAD628 /* PBXTextBookmark */; + BCC7DCE10844C00B003A8CF0 /* PBXTextBookmark */ = BCC7DCE10844C00B003A8CF0 /* PBXTextBookmark */; + BCDA4649083A652E009B5FA8 /* PBXTextBookmark */ = BCDA4649083A652E009B5FA8 /* PBXTextBookmark */; + BCEB8EB8093DF01400949543 /* PBXTextBookmark */ = BCEB8EB8093DF01400949543 /* PBXTextBookmark */; + BCEDABE00AFA8EF90036B190 /* PBXTextBookmark */ = BCEDABE00AFA8EF90036B190 /* PBXTextBookmark */; + BCEDABE40AFA8F9B0036B190 /* PBXTextBookmark */ = BCEDABE40AFA8F9B0036B190 /* PBXTextBookmark */; + BCF539400AB5F748007E56E5 /* PBXTextBookmark */ = BCF539400AB5F748007E56E5 /* PBXTextBookmark */; + BCF539410AB5F748007E56E5 /* PBXTextBookmark */ = BCF539410AB5F748007E56E5 /* PBXTextBookmark */; + BCF539420AB5F748007E56E5 /* PBXTextBookmark */ = BCF539420AB5F748007E56E5 /* PBXTextBookmark */; + BCF5408B0AB6A399007E56E5 /* PBXTextBookmark */ = BCF5408B0AB6A399007E56E5 /* PBXTextBookmark */; + }; + sourceControlManager = BCC8715F05853E3F00054D38 /* Source Control */; + userBookmarkGroup = F5626BAD03AECF750185C75A /* PBXBookmarkGroup */; + userBuildSettings = { + OBJROOT = "$(SRCROOT)/../../build-intermediates"; + SYMROOT = "$(SRCROOT)/../../build"; + }; + }; + BC037D100430328600A80001 /* WOControlButtons.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {972, 4368}}"; + sepNavSelRange = "{3134, 0}"; + sepNavVisRect = "{{0, 272}, {972, 653}}"; + sepNavWindowFrame = "{{130, 78}, {750, 558}}"; + }; + }; + BC037D110430328600A80001 /* WOControlButtons.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {972, 17906}}"; + sepNavSelRange = "{27820, 7}"; + sepNavVisRect = "{{0, 12386}, {972, 649}}"; + }; + }; + BC040E40041F3C180052CE41 /* WOButtonState.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {683, 812}}"; + sepNavSelRange = "{901, 0}"; + sepNavVisRect = "{{0, 99}, {683, 713}}"; + }; + }; + BC040E41041F3C180052CE41 /* WOButtonState.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {683, 2394}}"; + sepNavSelRange = "{3823, 0}"; + sepNavVisRect = "{{0, 1484}, {683, 713}}"; + }; + }; + BC040E44041F67100052CE41 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC040E40041F3C180052CE41 /* WOButtonState.h */; + name = "WOButtonState.h: 32"; + rLen = 0; + rLoc = 1391; + rType = 0; + vrLen = 769; + vrLoc = 0; + }; + BC04126F041F684E0052CE41 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC040E40041F3C180052CE41 /* WOButtonState.h */; + name = "WOButtonState.h: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 588; + vrLoc = 0; + }; + BC08FB9D0404F1E00099965D /* WOSysctl.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {683, 1862}}"; + sepNavSelRange = "{37, 6}"; + sepNavVisRect = "{{0, 1149}, {683, 713}}"; + }; + }; + BC08FB9E0404F1E00099965D /* WOSysctl.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 3794}}"; + sepNavSelRange = "{13498, 0}"; + sepNavVisRect = "{{0, 3290}, {766, 504}}"; + }; + }; + BC0A12C00A3B1A9700B30B7A /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F54314790394BAF401AC36F3 /* HotkeyCapableApplication.m */; + name = "HotkeyCapableApplication.m: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 978; + vrLoc = 0; + }; + BC0A12C10A3B1A9700B30B7A /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F57E7B5303AD61D401A8AA3E /* defaults.plist */; + name = "defaults.plist: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 753; + vrLoc = 0; + }; + BC0A12C40A3B1A9700B30B7A /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F57E7B5303AD61D401A8AA3E /* defaults.plist */; + name = "defaults.plist: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 753; + vrLoc = 0; + }; + BC0A15E50A3C866900B30B7A /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC98E7CC0A39F0EF0048ADFF /* NSAppleScript+WOAdditions.m */; + name = "NSAppleScript+WOAdditions.m: 32"; + rLen = 0; + rLoc = 1653; + rType = 0; + vrLen = 1784; + vrLoc = 0; + }; + BC0A15E60A3C866900B30B7A /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC7B628003D4613B00B1B2E1 /* WOSynergyFloaterController.m */; + name = "- (void)setAlbumImagePath:(NSString *)path"; + rLen = 43; + rLoc = 31069; + rType = 0; + vrLen = 892; + vrLoc = 30856; + }; + BC0B53310982BC590033B1A7 /* PBXBookmark */ = { + isa = PBXBookmark; + fRef = BCDC340803E41D2800A59DD4 /* SynergyLarge.png */; + }; + BC0B53830982BC590033B1A7 /* PBXBookmark */ = { + isa = PBXBookmark; + fRef = BCDC340803E41D2800A59DD4 /* SynergyLarge.png */; + }; + BC0B53A70982C7BE0033B1A7 /* PBXBookmark */ = { + isa = PBXBookmark; + fRef = BCAC73AF07E5FE9300FDA956 /* NoCoverArt.png */; + }; + BC0B53A90982C7BE0033B1A7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC911C1303C31CBC000D6AD2 /* WOKeyCaptureView.h */; + name = Create; + rLen = 6; + rLoc = 45; + rType = 0; + vrLen = 2222; + vrLoc = 0; + }; + BC0B53AD0982C7BE0033B1A7 /* PBXBookmark */ = { + isa = PBXBookmark; + fRef = BCAC73AF07E5FE9300FDA956 /* NoCoverArt.png */; + }; + BC0B53AE0982C7BE0033B1A7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F54314780394BAF401AC36F3 /* HotkeyCapableApplication.h */; + name = "WOSynergyPreferences *"; + rLen = 22; + rLoc = 436; + rType = 0; + vrLen = 1452; + vrLoc = 483; + }; + BC0B53B00982C7BE0033B1A7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC911C1303C31CBC000D6AD2 /* WOKeyCaptureView.h */; + name = Create; + rLen = 6; + rLoc = 45; + rType = 0; + vrLen = 2186; + vrLoc = 526; + }; + BC0B54040983F1DA0033B1A7 /* NSEvent.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = NSEvent.h; + path = /Developer/SDKs/MacOSX10.2.8.sdk/System/Library/Frameworks/AppKit.framework/Versions/C/Headers/NSEvent.h; + sourceTree = ""; + }; + BC0B540B0983F9360033B1A7 /* NSString.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = NSString.h; + path = /Developer/SDKs/MacOSX10.2.8.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSString.h; + sourceTree = ""; + }; + BC0B54100983F95F0033B1A7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC588016046F9F9C00A80001 /* German */; + name = "German: 34"; + rLen = 0; + rLoc = 774; + rType = 0; + vrLen = 774; + vrLoc = 0; + }; + BC0B54110983F95F0033B1A7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC689895046AC32100A80001 /* French */; + name = "French: 34"; + rLen = 0; + rLoc = 672; + rType = 0; + vrLen = 673; + vrLoc = 0; + }; + BC0B54130983F95F0033B1A7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC0B540B0983F9360033B1A7 /* NSString.h */; + name = unichar; + rLen = 7; + rLoc = 96; + rType = 0; + vrLen = 1945; + vrLoc = 0; + }; + BC0B54140983F95F0033B1A7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC0B54040983F1DA0033B1A7 /* NSEvent.h */; + name = "NSAlternateKeyMask =\t\t1 << 19,"; + rLen = 32; + rLoc = 2091; + rType = 0; + vrLen = 1891; + vrLoc = 908; + }; + BC0B54250984F9920033B1A7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC911C1403C31CBC000D6AD2 /* WOKeyCaptureView.m */; + name = "WOKeyCaptureView.m: 244"; + rLen = 0; + rLoc = 7594; + rType = 0; + vrLen = 1896; + vrLoc = 6636; + }; + BC0B54260984F9920033B1A7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F5626BC903AF15A80185C75A /* WOSynergyHelp.h */; + name = "WOSynergyHelp.h: 14"; + rLen = 0; + rLoc = 318; + rType = 0; + vrLen = 345; + vrLoc = 0; + }; + BC0B54280984F9920033B1A7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC911C1403C31CBC000D6AD2 /* WOKeyCaptureView.m */; + name = "WOKeyCaptureView.m: 244"; + rLen = 0; + rLoc = 7594; + rType = 0; + vrLen = 1896; + vrLoc = 6636; + }; + BC0B54290984F9920033B1A7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F5626BC903AF15A80185C75A /* WOSynergyHelp.h */; + name = "WOSynergyHelp.h: 14"; + rLen = 0; + rLoc = 318; + rType = 0; + vrLen = 345; + vrLoc = 0; + }; + BC0B546509850E2D0033B1A7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F55DFBBE03A84D64010663F9 /* WODebug.h */; + name = NSLog; + rLen = 5; + rLoc = 769; + rType = 0; + vrLen = 2092; + vrLoc = 0; + }; + BC0B546E09850E2D0033B1A7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F55DFBBE03A84D64010663F9 /* WODebug.h */; + name = NSLog; + rLen = 5; + rLoc = 769; + rType = 0; + vrLen = 2092; + vrLoc = 0; + }; + BC0B61BA03C9D2580035855F /* WOCommon.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1125, 941}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {1125, 941}}"; + }; + }; + BC0C651B08394D8200D44ACA /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC680E320836364400ABF3B8 /* mach_init.h */; + name = task_self; + rLen = 9; + rLoc = 2483; + rType = 0; + vrLen = 1003; + vrLoc = 2027; + }; + BC0C651D08394D8200D44ACA /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC08FB9D0404F1E00099965D /* WOSysctl.h */; + name = Create; + rLen = 6; + rLoc = 37; + rType = 0; + vrLen = 2588; + vrLoc = 4128; + }; + BC0C651F08394D8200D44ACA /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC680E320836364400ABF3B8 /* mach_init.h */; + name = task_self; + rLen = 9; + rLoc = 2483; + rType = 0; + vrLen = 1003; + vrLoc = 2027; + }; + BC0C652208394D8200D44ACA /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC08FB9D0404F1E00099965D /* WOSysctl.h */; + name = Create; + rLen = 6; + rLoc = 37; + rType = 0; + vrLen = 2588; + vrLoc = 4128; + }; + BC0EF51B09FF8A9B0049BE71 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCC06707045CF19700A80001 /* French */; + name = 2.8; + rLen = 3; + rLoc = 164; + rType = 0; + vrLen = 278; + vrLoc = 0; + }; + BC0EF51C09FF8A9B0049BE71 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCA7F4A404837A0C00A80001 /* Dutch */; + name = 2.8; + rLen = 3; + rLoc = 162; + rType = 0; + vrLen = 277; + vrLoc = 0; + }; + BC0EF51D09FF8A9B0049BE71 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC5D0AC7042FDFFF00A80001 /* WOExceptions.h */; + name = "#define WO_INTERNET_VERSION_CHECK_EXCEPTION"; + rLen = 48; + rLoc = 966; + rType = 0; + vrLen = 1049; + vrLoc = 0; + }; + BC0EF51E09FF8A9B0049BE71 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC26C14C041C82F500A80001 /* WONSScreenExtensions.m */; + name = "retain]"; + rLen = 7; + rLoc = 2030; + rType = 0; + vrLen = 1030; + vrLoc = 2583; + }; + BC0EF51F09FF8A9B0049BE71 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F5964517039B448A014C8642 /* WOSynergyPreferences.h */; + name = "WOSynergyPreferences.h: 12"; + rLen = 0; + rLoc = 346; + rType = 0; + vrLen = 1183; + vrLoc = 0; + }; + BC0EF52009FF8A9B0049BE71 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F5964518039B448A014C8642 /* WOSynergyPreferences.m */; + name = "WOSynergyPreferences.m: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 553; + vrLoc = 0; + }; + BC0EF52A09FF8A9B0049BE71 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC26C14C041C82F500A80001 /* WONSScreenExtensions.m */; + name = "retain]"; + rLen = 7; + rLoc = 2030; + rType = 0; + vrLen = 1030; + vrLoc = 2583; + }; + BC0EF52B09FF8A9B0049BE71 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F5964518039B448A014C8642 /* WOSynergyPreferences.m */; + name = "WOSynergyPreferences.m: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 553; + vrLoc = 0; + }; + BC0EF52C09FF8A9B0049BE71 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F5964517039B448A014C8642 /* WOSynergyPreferences.h */; + name = "WOSynergyPreferences.h: 12"; + rLen = 0; + rLoc = 346; + rType = 0; + vrLen = 1183; + vrLoc = 0; + }; + BC0EF52F09FF8A9B0049BE71 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC7B627C03D460B300B1B2E1 /* WOSynergyFloaterWindow.m */; + name = "WOSynergyFloaterWindow.m: 132"; + rLen = 0; + rLoc = 4644; + rType = 0; + vrLen = 1264; + vrLoc = 0; + }; + BC121FAE0881D9CC0062B12B /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F56702DD03A4BC7201842AA0 /* WOSynergyView.m */; + name = "WOSynergyView.m: 252"; + rLen = 223; + rLoc = 6850; + rType = 0; + vrLen = 2260; + vrLoc = 30654; + }; + BC121FB00881D9CC0062B12B /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F56702DD03A4BC7201842AA0 /* WOSynergyView.m */; + name = "WOSynergyView.m: 252"; + rLen = 223; + rLoc = 6850; + rType = 0; + vrLen = 2260; + vrLoc = 30654; + }; + BC121FE90881DA590062B12B /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC7E248003DB8FEE002EDF57 /* WOProcessManager.h */; + name = Create; + rLen = 6; + rLoc = 45; + rType = 0; + vrLen = 1022; + vrLoc = 0; + }; + BC121FEC0881DA590062B12B /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC7E248003DB8FEE002EDF57 /* WOProcessManager.h */; + name = Create; + rLen = 6; + rLoc = 45; + rType = 0; + vrLen = 1022; + vrLoc = 0; + }; + BC17717A044386A900A80001 /* NSString+WOExtensions.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {673, 321}}"; + sepNavSelRange = "{50, 6}"; + sepNavVisRect = "{{0, 0}, {673, 258}}"; + }; + }; + BC17717B044386A900A80001 /* NSString+WOExtensions.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {990, 1413}}"; + sepNavSelRange = "{2376, 6}"; + sepNavVisRect = "{{0, 935}, {990, 329}}"; + }; + }; + BC19436A03C1483700EFA70C /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F543147A0394BAF401AC36F3 /* SynergyController.h */; + name = "SynergyController.h: 59"; + rLen = 0; + rLoc = 1636; + rType = 0; + vrLen = 1525; + vrLoc = 575; + }; + BC236DBA08A2715100F0D6B1 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC7E248103DB8FEE002EDF57 /* WOProcessManager.m */; + name = ProcessSerialNumber; + rLen = 19; + rLoc = 397; + rType = 0; + vrLen = 1511; + vrLoc = 0; + }; + BC236DE008A2715100F0D6B1 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC7E248103DB8FEE002EDF57 /* WOProcessManager.m */; + name = ProcessSerialNumber; + rLen = 19; + rLoc = 397; + rType = 0; + vrLen = 1511; + vrLoc = 0; + }; + BC236DE308A2715100F0D6B1 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC30616403FA8FDD007C4E56 /* WOSynergyGlobal.h */; + name = "WOSynergyGlobal.h: WO_SYNERGY_VERSION_STRING"; + rLen = 0; + rLoc = 349; + rType = 0; + vrLen = 2591; + vrLoc = 0; + }; + BC26252403C10FFB00C4C6C7 /* WOHotKey.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {705, 843}}"; + sepNavSelRange = "{89, 4}"; + sepNavVisRect = "{{0, 0}, {705, 843}}"; + }; + }; + BC26252503C10FFB00C4C6C7 /* WOHotKey.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 953}}"; + sepNavSelRange = "{89, 4}"; + sepNavVisRect = "{{0, 0}, {766, 953}}"; + }; + }; + BC26252A03C1101D00C4C6C7 /* WOSynergyHotKey.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {705, 843}}"; + sepNavSelRange = "{96, 4}"; + sepNavVisRect = "{{0, 0}, {705, 843}}"; + }; + }; + BC26252B03C1101D00C4C6C7 /* WOSynergyHotKey.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {683, 713}}"; + sepNavSelRange = "{96, 4}"; + sepNavVisRect = "{{0, 0}, {683, 713}}"; + }; + }; + BC26253103C1208B00C4C6C7 /* WOGestalt.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {683, 713}}"; + sepNavSelRange = "{377, 7}"; + sepNavVisRect = "{{0, 0}, {683, 713}}"; + }; + }; + BC26253203C1208B00C4C6C7 /* WOGestalt.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {898, 756}}"; + sepNavSelRange = "{1047, 0}"; + sepNavVisRect = "{{0, 0}, {898, 193}}"; + }; + }; + BC26C14B041C82F500A80001 /* WONSScreenExtensions.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {826, 475}}"; + sepNavSelRange = "{49, 6}"; + sepNavVisRect = "{{0, 0}, {826, 353}}"; + }; + }; + BC26C14C041C82F500A80001 /* WONSScreenExtensions.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 1554}}"; + sepNavSelRange = "{2030, 7}"; + sepNavVisRect = "{{0, 1090}, {940, 464}}"; + }; + }; + BC26ED4208322950003F5E44 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC040E41041F3C180052CE41 /* WOButtonState.m */; + name = "WOButtonState.m: 127"; + rLen = 0; + rLoc = 3823; + rType = 0; + vrLen = 1368; + vrLoc = 3210; + }; + BC26ED4308322950003F5E44 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDC33E903E279D900A59DD4 /* WOFeedbackWindow.m */; + name = "void *ref = [result windowRef];"; + rLen = 31; + rLoc = 994; + rType = 0; + vrLen = 1816; + vrLoc = 293; + }; + BC26ED4408322950003F5E44 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDC33E503E279CB00A59DD4 /* WOFeedbackController.m */; + name = "WOFeedbackController.m: 29"; + rLen = 0; + rLoc = 854; + rType = 0; + vrLen = 1805; + vrLoc = 0; + }; + BC26ED4508322950003F5E44 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDC33E403E279CB00A59DD4 /* WOFeedbackController.h */; + name = Create; + rLen = 6; + rLoc = 49; + rType = 0; + vrLen = 1183; + vrLoc = 2677; + }; + BC26ED4708322950003F5E44 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCE15EA803E459B6005E6BE2 /* WOFeedbackDefaults.h */; + name = "WOFeedbackDefaults.h: FEEDBACK_BAR_LATERAL_OFFSET_FROM_MIDDLE"; + rLen = 176; + rLoc = 988; + rType = 0; + vrLen = 1723; + vrLoc = 3; + }; + BC26ED4A08322950003F5E44 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCE15EA803E459B6005E6BE2 /* WOFeedbackDefaults.h */; + name = Create; + rLen = 6; + rLoc = 47; + rType = 0; + vrLen = 1689; + vrLoc = 0; + }; + BC26ED4B08322950003F5E44 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDC33E903E279D900A59DD4 /* WOFeedbackWindow.m */; + name = "void *ref = [result windowRef];"; + rLen = 31; + rLoc = 994; + rType = 0; + vrLen = 1816; + vrLoc = 293; + }; + BC26ED4C08322950003F5E44 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDC33E403E279CB00A59DD4 /* WOFeedbackController.h */; + name = Create; + rLen = 6; + rLoc = 49; + rType = 0; + vrLen = 1368; + vrLoc = 0; + }; + BC26ED4D08322950003F5E44 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDC33E503E279CB00A59DD4 /* WOFeedbackController.m */; + name = delayedFadeTimer; + rLen = 16; + rLoc = 467; + rType = 0; + vrLen = 1805; + vrLoc = 0; + }; + BC26ED4F08322950003F5E44 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDC33E103E2799000A59DD4 /* WOFeedbackView.m */; + name = "WOFeedbackView.m: 172"; + rLen = 0; + rLoc = 5348; + rType = 0; + vrLen = 2112; + vrLoc = 0; + }; + BC26ED5308322950003F5E44 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDC33E003E2799000A59DD4 /* WOFeedbackView.h */; + name = Create; + rLen = 6; + rLoc = 43; + rType = 0; + vrLen = 1277; + vrLoc = 0; + }; + BC27E64A042DEF5300A80001 /* WOSongInfo.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1393, 1106}}"; + sepNavSelRange = "{1644, 5}"; + sepNavVisRect = "{{0, 717}, {1393, 389}}"; + }; + }; + BC27E64B042DEF5300A80001 /* WOSongInfo.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1393, 7700}}"; + sepNavSelRange = "{4668, 5}"; + sepNavVisRect = "{{0, 2922}, {1393, 389}}"; + }; + }; + BC27E64E042EE1A200A80001 /* SLA.r */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {743, 25787}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {743, 517}}"; + }; + }; + BC281F870AF8F6190039EAB8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC281F880AF8F6190039EAB8 /* WOEnumerate.h */; + name = "(null): 34"; + rLen = 0; + rLoc = 1660; + rType = 0; + vrLen = 2855; + vrLoc = 234; + }; + BC281F880AF8F6190039EAB8 /* WOEnumerate.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = WOEnumerate.h; + path = /Users/wincent/trabajo/jaguar/Synergy/panther/WOCommon/WOEnumerate.h; + sourceTree = ""; + }; + BC281F890AF8F6190039EAB8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC281F8A0AF8F6190039EAB8 /* WOObfuscation.h */; + name = "#define firstObject weepeefiaquobeic"; + rLen = 73; + rLoc = 7354; + rType = 0; + vrLen = 2298; + vrLoc = 6298; + }; + BC281F8A0AF8F6190039EAB8 /* WOObfuscation.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = WOObfuscation.h; + path = /Users/wincent/trabajo/jaguar/Synergy/panther/WOCommon/WOObfuscation.h; + sourceTree = ""; + }; + BC2833C0082B80AA00D6CC47 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC040E40041F3C180052CE41 /* WOButtonState.h */; + name = "WOButtonState.h: 33"; + rLen = 0; + rLoc = 901; + rType = 0; + vrLen = 1297; + vrLoc = 210; + }; + BC2833C1082B80AA00D6CC47 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC040E40041F3C180052CE41 /* WOButtonState.h */; + name = Create; + rLen = 6; + rLoc = 42; + rType = 0; + vrLen = 1355; + vrLoc = 152; + }; + BC2B328804574EF800A80001 /* French */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 1609}}"; + sepNavSelRange = "{1807, 0}"; + sepNavVisRect = "{{0, 495}, {766, 859}}"; + }; + }; + BC2B328A0457FF1F00A80001 /* French */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1922, 2197}}"; + sepNavSelRange = "{6327, 0}"; + sepNavVisRect = "{{0, 4}, {743, 388}}"; + }; + }; + BC2C26AA0482824B00A80001 /* Dutch */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 953}}"; + sepNavSelRange = "{666, 0}"; + sepNavVisRect = "{{0, 0}, {766, 953}}"; + }; + }; + BC2C26AB0482825B00A80001 /* Dutch */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {2258, 2197}}"; + sepNavSelRange = "{6256, 0}"; + sepNavVisRect = "{{0, 4}, {743, 388}}"; + }; + }; + BC2D22EB072E9D6D007E3CBD /* Norwegian */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 464}}"; + sepNavSelRange = "{117, 3}"; + sepNavVisRect = "{{0, 0}, {940, 464}}"; + }; + }; + BC30615503FA757C007C4E56 /* NSMenuExtra.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {826, 1595}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {826, 209}}"; + }; + }; + BC30616403FA8FDD007C4E56 /* WOSynergyGlobal.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {972, 1820}}"; + sepNavSelRange = "{451, 0}"; + sepNavVisRect = "{{0, 1013}, {972, 649}}"; + sepNavWindowFrame = "{{108, 169}, {750, 558}}"; + }; + }; + BC357D5C0A01290E00A27C1C /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC7B627703D4602200B1B2E1 /* WOSynergyFloaterView.h */; + name = "WOSynergyFloaterView.h: MIN_ALPHA_FOR_FLOATER_WINDOW"; + rLen = 0; + rLoc = 563; + rType = 0; + vrLen = 1069; + vrLoc = 30; + }; + BC3C3168080C9E8D00DF9C20 /* zh_CN */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 646}}"; + sepNavSelRange = "{150, 3}"; + sepNavVisRect = "{{0, 0}, {940, 646}}"; + }; + }; + BC3C3169080C9E8D00DF9C20 /* zh_CN */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 1693}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {766, 953}}"; + }; + }; + BC3C3172080C9ECD00DF9C20 /* zh_CN */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1393, 897}}"; + sepNavSelRange = "{11, 0}"; + sepNavVisRect = "{{0, 0}, {1393, 389}}"; + }; + }; + BC3C3173080C9ECD00DF9C20 /* zh_CN */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 834}}"; + sepNavSelRange = "{150, 3}"; + sepNavVisRect = "{{0, 0}, {940, 834}}"; + }; + }; + BC3C3174080C9ECD00DF9C20 /* zh_CN */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 2269}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {766, 953}}"; + }; + }; + BC3C3177080C9ED500DF9C20 /* zh_CN */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 953}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {766, 953}}"; + }; + }; + BC3C722608290C9C00E23B52 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC040E41041F3C180052CE41 /* WOButtonState.m */; + name = "WOButtonState.m: 127"; + rLen = 0; + rLoc = 3823; + rType = 0; + vrLen = 1072; + vrLoc = 3686; + }; + BC3C722708290C9C00E23B52 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F54314790394BAF401AC36F3 /* HotkeyCapableApplication.m */; + name = "#import \"WOGestalt.h\""; + rLen = 21; + rLoc = 1031; + rType = 0; + vrLen = 2244; + vrLoc = 12773; + }; + BC3E83FF045F354900A80001 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F54314790394BAF401AC36F3 /* HotkeyCapableApplication.m */; + name = "HotkeyCapableApplication.m: a"; + rLen = 0; + rLoc = 7020; + rType = 0; + vrLen = 2317; + vrLoc = 5315; + }; + BC4C59CA0AAEAD6500CD1FB0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F54FD5E70393F70D010D9943 /* RELEASE PROCEDURE */; + name = "RELEASE PROCEDURE: 52"; + rLen = 0; + rLoc = 1676; + rType = 0; + vrLen = 1510; + vrLoc = 166; + }; + BC542869040520370052F570 /* WOCoverDownloader.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 954}}"; + sepNavSelRange = "{866, 0}"; + sepNavVisRect = "{{0, 0}, {766, 954}}"; + }; + }; + BC54286A040520370052F570 /* WOCoverDownloader.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1393, 17192}}"; + sepNavSelRange = "{10685, 436}"; + sepNavVisRect = "{{0, 4584}, {1393, 389}}"; + sepNavWindowFrame = "{{15, 59}, {780, 682}}"; + }; + }; + BC55F4C3083D49A5008C9E55 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F56E9BFD0394A55F010CA6FF /* OrgWincentSynergyPref.m */; + name = WO_CREDITS_SCROLL_TIME_VALUE; + rLen = 28; + rLoc = 210095; + rType = 0; + vrLen = 1653; + vrLoc = 206511; + }; + BC56F21803DC506600BD6ADA /* WOSynergyAnchorWindow.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {826, 237}}"; + sepNavSelRange = "{50, 6}"; + sepNavVisRect = "{{0, 0}, {826, 209}}"; + }; + }; + BC56F21903DC506600BD6ADA /* WOSynergyAnchorWindow.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1016, 980}}"; + sepNavSelRange = "{2306, 3}"; + sepNavVisRect = "{{0, 582}, {1016, 398}}"; + }; + }; + BC56F21C03DC508200BD6ADA /* WOSynergyAnchorView.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {826, 783}}"; + sepNavSelRange = "{48, 6}"; + sepNavVisRect = "{{0, 0}, {826, 209}}"; + }; + }; + BC56F21D03DC508200BD6ADA /* WOSynergyAnchorView.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {826, 2085}}"; + sepNavSelRange = "{973, 15}"; + sepNavVisRect = "{{0, 490}, {826, 353}}"; + }; + }; + BC56F22303DC5A1800BD6ADA /* WOSynergyAnchorController.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {826, 1413}}"; + sepNavSelRange = "{54, 6}"; + sepNavVisRect = "{{0, 0}, {826, 209}}"; + }; + }; + BC56F22403DC5A1800BD6ADA /* WOSynergyAnchorController.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {826, 6229}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {826, 353}}"; + }; + }; + BC588013046F9F6F00A80001 /* German */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 464}}"; + sepNavSelRange = "{117, 3}"; + sepNavVisRect = "{{0, 0}, {940, 464}}"; + }; + }; + BC588014046F9F8B00A80001 /* German */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {741, 2393}}"; + sepNavSelRange = "{2825, 1}"; + sepNavVisRect = "{{0, 1027}, {741, 424}}"; + }; + }; + BC588016046F9F9C00A80001 /* German */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 834}}"; + sepNavSelRange = "{774, 0}"; + sepNavVisRect = "{{0, 0}, {940, 834}}"; + }; + }; + BC588017046FA03100A80001 /* German */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {770, 1623}}"; + sepNavSelRange = "{1835, 0}"; + sepNavVisRect = "{{0, 696}, {770, 526}}"; + }; + }; + BC5BAB4E03C1B27100799024 /* WOCarbonWrappers.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {826, 265}}"; + sepNavSelRange = "{2, 0}"; + sepNavVisRect = "{{0, 0}, {826, 209}}"; + }; + }; + BC5BAB4F03C1B27100799024 /* WOCarbonWrappers.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {826, 615}}"; + sepNavSelRange = "{665, 15}"; + sepNavVisRect = "{{0, 323}, {826, 209}}"; + }; + }; + BC5CC3260409B4E2001CCC4A /* WOButtonSet.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1393, 980}}"; + sepNavSelRange = "{848, 0}"; + sepNavVisRect = "{{0, 387}, {1393, 389}}"; + }; + }; + BC5CC3270409B4E2001CCC4A /* WOButtonSet.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1393, 6622}}"; + sepNavSelRange = "{3350, 0}"; + sepNavVisRect = "{{0, 0}, {1393, 389}}"; + }; + }; + BC5D0AC3042FCFFE00A80001 /* WONSFileManagerExtensions.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 654}}"; + sepNavSelRange = "{422, 0}"; + sepNavVisRect = "{{0, 0}, {940, 654}}"; + }; + }; + BC5D0AC4042FCFFE00A80001 /* WONSFileManagerExtensions.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 798}}"; + sepNavSelRange = "{1221, 6}"; + sepNavVisRect = "{{0, 55}, {940, 654}}"; + }; + }; + BC5D0AC7042FDFFF00A80001 /* WOExceptions.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 504}}"; + sepNavSelRange = "{966, 48}"; + sepNavVisRect = "{{0, 12}, {940, 464}}"; + }; + }; + BC610865082569BF00596B77 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F543147A0394BAF401AC36F3 /* SynergyController.h */; + name = "SynergyController.h: 385"; + rLen = 120; + rLoc = 11261; + rType = 0; + vrLen = 1648; + vrLoc = 1589; + }; + BC617DFF0AF7CC9D00E1268F /* WOAudioscrobbler.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {972, 1162}}"; + sepNavSelRange = "{1480, 0}"; + sepNavVisRect = "{{0, 125}, {972, 646}}"; + }; + }; + BC617E000AF7CC9D00E1268F /* WOAudioscrobbler.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {972, 7028}}"; + sepNavSelRange = "{9001, 0}"; + sepNavVisRect = "{{0, 4774}, {972, 646}}"; + }; + }; + BC617FCC0AF8BEA700E1268F /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F543147B0394BAF401AC36F3 /* SynergyController.m */; + name = NSNotification; + rLen = 14; + rLoc = 240022; + rType = 0; + vrLen = 1411; + vrLoc = 239476; + }; + BC617FCD0AF8BEA700E1268F /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F594B71D039CC99C01E61CB8 /* WOSingleton.h */; + name = "WOSingleton.h: 75"; + rLen = 0; + rLoc = 2386; + rType = 0; + vrLen = 1300; + vrLoc = 1942; + }; + BC617FCE0AF8BEA700E1268F /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F594B71E039CC99C01E61CB8 /* WOSingleton.m */; + name = alloc; + rLen = 5; + rLoc = 692; + rType = 0; + vrLen = 1267; + vrLoc = 2054; + }; + BC617FCF0AF8BEA700E1268F /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC617FD00AF8BEA700E1268F /* NSURLRequest.h */; + name = "@interface NSURLRequest : NSObject "; + rLen = 75; + rLoc = 5294; + rType = 0; + vrLen = 1805; + vrLoc = 15905; + }; + BC617FD00AF8BEA700E1268F /* NSURLRequest.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = NSURLRequest.h; + path = /Developer/SDKs/MacOSX10.2.8.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSURLRequest.h; + sourceTree = ""; + }; + BC617FD20AF8BEA700E1268F /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC617FD30AF8BEA700E1268F /* WOConvenienceMacros.h */; + name = "#define WO_STRING(...) [NSString stringWithFormat:__VA_ARGS__]"; + rLen = 63; + rLoc = 6042; + rType = 0; + vrLen = 3142; + vrLoc = 0; + }; + BC617FD30AF8BEA700E1268F /* WOConvenienceMacros.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = WOConvenienceMacros.h; + path = /Users/wincent/trabajo/jaguar/Synergy/panther/WOCommon/WOConvenienceMacros.h; + sourceTree = ""; + }; + BC617FD60AF8BEA700E1268F /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F594B71D039CC99C01E61CB8 /* WOSingleton.h */; + name = "WOSingleton.h: 75"; + rLen = 0; + rLoc = 2386; + rType = 0; + vrLen = 1300; + vrLoc = 1942; + }; + BC617FD70AF8BEA700E1268F /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F594B71E039CC99C01E61CB8 /* WOSingleton.m */; + name = alloc; + rLen = 5; + rLoc = 692; + rType = 0; + vrLen = 1267; + vrLoc = 2054; + }; + BC617FD80AF8BEA700E1268F /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC617DFF0AF7CC9D00E1268F /* WOAudioscrobbler.h */; + name = "WOAudioscrobbler.h: 11"; + rLen = 0; + rLoc = 383; + rType = 0; + vrLen = 211; + vrLoc = 0; + }; + BC680CF00834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC26253103C1208B00C4C6C7 /* WOGestalt.h */; + name = Panther; + rLen = 7; + rLoc = 377; + rType = 0; + vrLen = 430; + vrLoc = 0; + }; + BC680CF60834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC037D100430328600A80001 /* WOControlButtons.h */; + name = "WOControlButtons.h: 37"; + rLen = 0; + rLoc = 2509; + rType = 0; + vrLen = 1877; + vrLoc = 2885; + }; + BC680CF70834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F5B86F62039931F1010F9052 /* English */; + name = 1.9; + rLen = 0; + rLoc = 164; + rType = 0; + vrLen = 277; + vrLoc = 0; + }; + BC680CF80834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F5B86F6A0399320C010F9052 /* Spanish */; + name = "Spanish: 5"; + rLen = 0; + rLoc = 167; + rType = 0; + vrLen = 277; + vrLoc = 0; + }; + BC680CF90834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCBB9A370470727600A80001 /* Dutch */; + name = 1.9; + rLen = 0; + rLoc = 162; + rType = 0; + vrLen = 275; + vrLoc = 0; + }; + BC680CFA0834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCF3AFB1060B3D2A00B2B13C /* ru */; + name = 1.9; + rLen = 0; + rLoc = 108; + rType = 0; + vrLen = 230; + vrLoc = 0; + }; + BC680CFB0834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCD86CA507669B3600FAA179 /* zh_TW */; + name = 1.9; + rLen = 0; + rLoc = 112; + rType = 0; + vrLen = 255; + vrLoc = 0; + }; + BC680CFC0834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCD8714007669DC400FAA179 /* it */; + name = 1.9; + rLen = 0; + rLoc = 166; + rType = 0; + vrLen = 263; + vrLoc = 0; + }; + BC680CFD0834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC3C3168080C9E8D00DF9C20 /* zh_CN */; + name = 1.9; + rLen = 0; + rLoc = 110; + rType = 0; + vrLen = 263; + vrLoc = 0; + }; + BC680CFE0834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F58C12A903991E3601B0D373 /* English */; + name = 1.9; + rLen = 3; + rLoc = 164; + rType = 0; + vrLen = 279; + vrLoc = 0; + }; + BC680CFF0834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F58C12B103991E4E01B0D373 /* Spanish */; + name = 1.9; + rLen = 0; + rLoc = 117; + rType = 0; + vrLen = 279; + vrLoc = 0; + }; + BC680D000834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCC06707045CF19700A80001 /* French */; + name = 1.9; + rLen = 0; + rLoc = 164; + rType = 0; + vrLen = 278; + vrLoc = 0; + }; + BC680D010834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC689898046BE0FC00A80001 /* Japanese */; + name = 1.9; + rLen = 0; + rLoc = 164; + rType = 0; + vrLen = 278; + vrLoc = 0; + }; + BC680D020834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC588013046F9F6F00A80001 /* German */; + name = 1.9; + rLen = 0; + rLoc = 164; + rType = 0; + vrLen = 279; + vrLoc = 0; + }; + BC680D030834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCA7F4A3048379BC00A80001 /* Swedish */; + name = 1.9; + rLen = 0; + rLoc = 164; + rType = 0; + vrLen = 279; + vrLoc = 0; + }; + BC680D040834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCA7F4A404837A0C00A80001 /* Dutch */; + name = 1.9; + rLen = 0; + rLoc = 162; + rType = 0; + vrLen = 277; + vrLoc = 0; + }; + BC680D050834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCF3AFB0060B3CAF00B2B13C /* ru */; + name = 1.9; + rLen = 0; + rLoc = 162; + rType = 0; + vrLen = 234; + vrLoc = 0; + }; + BC680D060834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC2D22EB072E9D6D007E3CBD /* Norwegian */; + name = 1.9; + rLen = 0; + rLoc = 164; + rType = 0; + vrLen = 287; + vrLoc = 0; + }; + BC680D070834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCD8710B07669C4A00FAA179 /* zh_TW */; + name = 1.9; + rLen = 0; + rLoc = 154; + rType = 0; + vrLen = 257; + vrLoc = 0; + }; + BC680D080834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCD8712807669D4300FAA179 /* it */; + name = 1.9; + rLen = 0; + rLoc = 166; + rType = 0; + vrLen = 264; + vrLoc = 0; + }; + BC680D090834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC3C3173080C9ECD00DF9C20 /* zh_CN */; + name = 1.9; + rLen = 0; + rLoc = 150; + rType = 0; + vrLen = 264; + vrLoc = 0; + }; + BC680D0A0834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDF68280753B6A100EF20AF /* Japanese */; + name = 1.9; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 595; + vrLoc = 0; + }; + BC680D0B0834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDF68290753B6A100EF20AF /* French */; + name = 1.9; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 738; + vrLoc = 0; + }; + BC680D0C0834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDF682A0753B6A100EF20AF /* ru */; + name = 1.9; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 735; + vrLoc = 0; + }; + BC680D0D0834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDF682B0753B6A100EF20AF /* Swedish */; + name = 1.9; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 720; + vrLoc = 0; + }; + BC680D0E0834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDF682C0753B6A100EF20AF /* Norwegian */; + name = 1.9; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 717; + vrLoc = 0; + }; + BC680D0F0834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDF682D0753B6A100EF20AF /* English */; + name = 1.9; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 725; + vrLoc = 0; + }; + BC680D100834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDF682E0753B6A100EF20AF /* Dutch */; + name = 1.9; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 733; + vrLoc = 0; + }; + BC680D110834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDF682F0753B6A100EF20AF /* German */; + name = 1.9; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 773; + vrLoc = 0; + }; + BC680D120834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDF68300753B6A100EF20AF /* Spanish */; + name = 1.9; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 743; + vrLoc = 0; + }; + BC680D130834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCD8711207669C8900FAA179 /* zh_TW */; + name = 1.8.1; + rLen = 0; + rLoc = 636; + rType = 0; + vrLen = 510; + vrLoc = 126; + }; + BC680D170834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCD8712707669D4300FAA179 /* it */; + name = 1.9; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 763; + vrLoc = 0; + }; + BC680D180834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCB5C0E107D7DF4A00D08CA9 /* tr */; + name = 1.9; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 725; + vrLoc = 0; + }; + BC680D190834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC3C3172080C9ECD00DF9C20 /* zh_CN */; + name = 1.9; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 725; + vrLoc = 0; + }; + BC680D290834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC26253103C1208B00C4C6C7 /* WOGestalt.h */; + name = Panther; + rLen = 7; + rLoc = 377; + rType = 0; + vrLen = 430; + vrLoc = 0; + }; + BC680D2A0834C2A400ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC7B627803D4602200B1B2E1 /* WOSynergyFloaterView.m */; + name = "WOSynergyFloaterView.m: 607"; + rLen = 0; + rLoc = 21156; + rType = 0; + vrLen = 1717; + vrLoc = 20173; + }; + BC680DDE0834EE2500ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC5CC3260409B4E2001CCC4A /* WOButtonSet.h */; + name = "WOButtonSet.h: 44"; + rLen = 111; + rLoc = 946; + rType = 0; + vrLen = 1303; + vrLoc = 0; + }; + BC680DE10834EE2500ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC08FB9E0404F1E00099965D /* WOSysctl.c */; + name = "error = sysctl(mib, 3, NULL, &sizeOfBufferRequired, NULL, NULL);"; + rLen = 70; + rLoc = 5414; + rType = 0; + vrLen = 2949; + vrLoc = 3553; + }; + BC680DE30834EE2500ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC5CC3270409B4E2001CCC4A /* WOButtonSet.m */; + name = "WOButtonSet.m: 84"; + rLen = 0; + rLoc = 3134; + rType = 0; + vrLen = 2035; + vrLoc = 2053; + }; + BC680DE40834EE2500ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC7B628003D4613B00B1B2E1 /* WOSynergyFloaterController.m */; + name = Panther; + rLen = 7; + rLoc = 25146; + rType = 0; + vrLen = 2315; + vrLoc = 23822; + }; + BC680DE50834EE2500ABF3B8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC5CC3260409B4E2001CCC4A /* WOButtonSet.h */; + name = "WOButtonSet.h: 44"; + rLen = 111; + rLoc = 946; + rType = 0; + vrLen = 1303; + vrLoc = 0; + }; + BC680E320836364400ABF3B8 /* mach_init.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = mach_init.h; + path = /usr/include/mach/mach_init.h; + sourceTree = ""; + }; + BC68988904691BF000A80001 /* Japanese */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1603, 2248}}"; + sepNavSelRange = "{5118, 0}"; + sepNavVisRect = "{{0, 13}, {743, 388}}"; + }; + }; + BC68988B04691C0700A80001 /* Japanese */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {770, 1604}}"; + sepNavSelRange = "{1588, 1}"; + sepNavVisRect = "{{0, 719}, {770, 526}}"; + }; + }; + BC689892046AC30F00A80001 /* English */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 834}}"; + sepNavSelRange = "{652, 0}"; + sepNavVisRect = "{{0, 0}, {940, 834}}"; + }; + }; + BC689895046AC32100A80001 /* French */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 834}}"; + sepNavSelRange = "{672, 0}"; + sepNavVisRect = "{{0, 0}, {940, 834}}"; + }; + }; + BC689896046AC32E00A80001 /* Spanish */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 953}}"; + sepNavSelRange = "{654, 0}"; + sepNavVisRect = "{{0, 0}, {766, 953}}"; + }; + }; + BC689898046BE0FC00A80001 /* Japanese */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 464}}"; + sepNavSelRange = "{117, 3}"; + sepNavVisRect = "{{0, 0}, {940, 464}}"; + }; + }; + BC6AD6E50AF92B7F00D248C7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC617DFF0AF7CC9D00E1268F /* WOAudioscrobbler.h */; + name = "WOAudioscrobbler.h: _submissionURL"; + rLen = 0; + rLoc = 1480; + rType = 0; + vrLen = 1681; + vrLoc = 140; + }; + BC6AD79A0AF9FCC700D248C7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC6AD79B0AF9FCC700D248C7 /* NSObjCRuntime.h */; + name = "#define FOUNDATION_EXPORT extern"; + rLen = 37; + rLoc = 559; + rType = 0; + vrLen = 1077; + vrLoc = 0; + }; + BC6AD79B0AF9FCC700D248C7 /* NSObjCRuntime.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = NSObjCRuntime.h; + path = /Developer/SDKs/MacOSX10.2.8.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSObjCRuntime.h; + sourceTree = ""; + }; + BC6AD79C0AF9FCC700D248C7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC6AD79D0AF9FCC700D248C7 /* NSRange.h */; + name = FOUNDATION_EXPORT; + rLen = 17; + rLoc = 816; + rType = 0; + vrLen = 1213; + vrLoc = 0; + }; + BC6AD79D0AF9FCC700D248C7 /* NSRange.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = NSRange.h; + path = /Developer/SDKs/MacOSX10.2.8.sdk/System/Library/Frameworks/Foundation.framework/Versions/C/Headers/NSRange.h; + sourceTree = ""; + }; + BC6BC25B0872834900C3C59C /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCF3AFAF060B3C9E00B2B13C /* ru */; + name = "ru: 2"; + rLen = 0; + rLoc = 42; + rType = 0; + vrLen = 3520; + vrLoc = 0; + }; + BC6BC25C0872834900C3C59C /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCF3AFAF060B3C9E00B2B13C /* ru */; + name = "ru: 2"; + rLen = 0; + rLoc = 42; + rType = 0; + vrLen = 3520; + vrLoc = 0; + }; + BC6BC26B087284F000C3C59C /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCB5C0F107D7DF5900D08CA9 /* tr */; + name = "tr: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 1988; + vrLoc = 0; + }; + BC6BC26C087284F000C3C59C /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC3C3174080C9ECD00DF9C20 /* zh_CN */; + name = "zh_CN: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 1689; + vrLoc = 0; + }; + BC6BC28E087284F000C3C59C /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCB5C0F107D7DF5900D08CA9 /* tr */; + name = "tr: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 1988; + vrLoc = 0; + }; + BC6BC28F087284F000C3C59C /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC3C3174080C9ECD00DF9C20 /* zh_CN */; + name = "zh_CN: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 1689; + vrLoc = 0; + }; + BC753A4A0A476E5D00DC2344 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F5CAFBDB039C8FC1019C532D /* Development notes.txt */; + name = Icon; + rLen = 4; + rLoc = 350; + rType = 0; + vrLen = 1513; + vrLoc = 0; + }; + BC753A5E0A485A2200DC2344 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC3C3173080C9ECD00DF9C20 /* zh_CN */; + name = 3.0; + rLen = 3; + rLoc = 150; + rType = 0; + vrLen = 264; + vrLoc = 0; + }; + BC79A3B509A60E8F008FF8BC /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F58C12B103991E4E01B0D373 /* Spanish */; + name = 2.5; + rLen = 3; + rLoc = 164; + rType = 0; + vrLen = 279; + vrLoc = 0; + }; + BC79A3B709A60E8F008FF8BC /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC689898046BE0FC00A80001 /* Japanese */; + name = 2.5; + rLen = 3; + rLoc = 164; + rType = 0; + vrLen = 278; + vrLoc = 0; + }; + BC79A3B809A60E8F008FF8BC /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC588013046F9F6F00A80001 /* German */; + name = 2.5; + rLen = 3; + rLoc = 164; + rType = 0; + vrLen = 279; + vrLoc = 0; + }; + BC79A3B909A60E8F008FF8BC /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCA7F4A3048379BC00A80001 /* Swedish */; + name = 2.5; + rLen = 3; + rLoc = 164; + rType = 0; + vrLen = 279; + vrLoc = 0; + }; + BC79A3BB09A60E8F008FF8BC /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCF3AFB0060B3CAF00B2B13C /* ru */; + name = 2.5; + rLen = 3; + rLoc = 162; + rType = 0; + vrLen = 234; + vrLoc = 0; + }; + BC79A3BC09A60E8F008FF8BC /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC2D22EB072E9D6D007E3CBD /* Norwegian */; + name = 2.5; + rLen = 3; + rLoc = 164; + rType = 0; + vrLen = 287; + vrLoc = 0; + }; + BC79A3BD09A60E8F008FF8BC /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCD8710B07669C4A00FAA179 /* zh_TW */; + name = 2.5; + rLen = 3; + rLoc = 154; + rType = 0; + vrLen = 257; + vrLoc = 0; + }; + BC79A3BE09A60E8F008FF8BC /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCD8712807669D4300FAA179 /* it */; + name = 2.5; + rLen = 3; + rLoc = 166; + rType = 0; + vrLen = 264; + vrLoc = 0; + }; + BC79A3C309A60E8F008FF8BC /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDF682B0753B6A100EF20AF /* Swedish */; + name = 2.5; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 850; + vrLoc = 0; + }; + BC79A3C609A60E8F008FF8BC /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDF682E0753B6A100EF20AF /* Dutch */; + name = 2.5; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 878; + vrLoc = 0; + }; + BC79A3D309A60E8F008FF8BC /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F5CAFBDB039C8FC1019C532D /* Development notes.txt */; + name = "Development notes.txt: 186"; + rLen = 0; + rLoc = 5061; + rType = 0; + vrLen = 1503; + vrLoc = 4106; + }; + BC79A3F709A60E8F008FF8BC /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC037D110430328600A80001 /* WOControlButtons.m */; + name = "SYN-100"; + rLen = 7; + rLoc = 27820; + rType = 0; + vrLen = 2314; + vrLoc = 26736; + }; + BC7B627303D45F7D00B1B2E1 /* WORoundedRect.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {705, 843}}"; + sepNavSelRange = "{251, 165}"; + sepNavVisRect = "{{0, 0}, {705, 843}}"; + }; + }; + BC7B627403D45F7D00B1B2E1 /* WORoundedRect.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {673, 671}}"; + sepNavSelRange = "{254, 1235}"; + sepNavVisRect = "{{0, 126}, {673, 533}}"; + }; + }; + BC7B627703D4602200B1B2E1 /* WOSynergyFloaterView.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1016, 3024}}"; + sepNavSelRange = "{493, 24}"; + sepNavVisRect = "{{0, 0}, {1016, 398}}"; + }; + }; + BC7B627803D4602200B1B2E1 /* WOSynergyFloaterView.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {972, 20510}}"; + sepNavSelRange = "{45743, 24}"; + sepNavVisRect = "{{0, 19841}, {972, 652}}"; + }; + }; + BC7B627B03D460B300B1B2E1 /* WOSynergyFloaterWindow.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 646}}"; + sepNavSelRange = "{874, 0}"; + sepNavVisRect = "{{0, 0}, {940, 646}}"; + }; + }; + BC7B627C03D460B300B1B2E1 /* WOSynergyFloaterWindow.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 2366}}"; + sepNavSelRange = "{5960, 0}"; + sepNavVisRect = "{{0, 0}, {940, 646}}"; + sepNavWindowFrame = "{{15, 183}, {750, 558}}"; + }; + }; + BC7B627F03D4613B00B1B2E1 /* WOSynergyFloaterController.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1393, 3976}}"; + sepNavSelRange = "{5628, 6}"; + sepNavVisRect = "{{0, 2291}, {1393, 389}}"; + }; + }; + BC7B628003D4613B00B1B2E1 /* WOSynergyFloaterController.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1393, 14546}}"; + sepNavSelRange = "{26550, 6}"; + sepNavVisRect = "{{0, 10957}, {1393, 389}}"; + sepNavWindowFrame = "{{194, 180}, {750, 558}}"; + }; + }; + BC7DA10D042193D000A80001 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCE8D3E90420BEAA00A80001 /* WOButtonWithTrackingRect.h */; + name = "WOButtonWithTrackingRect.h: setMouseEnteredAction:"; + rLen = 88; + rLoc = 678; + rType = 0; + vrLen = 789; + vrLoc = 46; + }; + BC7E248003DB8FEE002EDF57 /* WOProcessManager.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 889}}"; + sepNavSelRange = "{45, 6}"; + sepNavVisRect = "{{0, 0}, {940, 889}}"; + }; + }; + BC7E248103DB8FEE002EDF57 /* WOProcessManager.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 2464}}"; + sepNavSelRange = "{397, 19}"; + sepNavVisRect = "{{0, 0}, {940, 889}}"; + }; + }; + BC833B560AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC9272FE040B7B6F0099CFAC /* TODO.rtfd */; + name = "TODO.rtfd: 601"; + rLen = 0; + rLoc = 33989; + rType = 0; + vrLen = 1563; + vrLoc = 31345; + }; + BC833B570AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F5B86F6A0399320C010F9052 /* Spanish */; + name = 3.0; + rLen = 3; + rLoc = 164; + rType = 0; + vrLen = 277; + vrLoc = 0; + }; + BC833B580AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC3C3168080C9E8D00DF9C20 /* zh_CN */; + name = 3.0; + rLen = 3; + rLoc = 150; + rType = 0; + vrLen = 263; + vrLoc = 0; + }; + BC833B590AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCD8714007669DC400FAA179 /* it */; + name = 3.0; + rLen = 3; + rLoc = 166; + rType = 0; + vrLen = 263; + vrLoc = 0; + }; + BC833B5A0AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCD86CA507669B3600FAA179 /* zh_TW */; + name = 3.0; + rLen = 3; + rLoc = 154; + rType = 0; + vrLen = 255; + vrLoc = 0; + }; + BC833B5B0AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCF3AFB1060B3D2A00B2B13C /* ru */; + name = 3.0; + rLen = 3; + rLoc = 108; + rType = 0; + vrLen = 230; + vrLoc = 0; + }; + BC833B5C0AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCBB9A370470727600A80001 /* Dutch */; + name = 3.0; + rLen = 3; + rLoc = 162; + rType = 0; + vrLen = 275; + vrLoc = 0; + }; + BC833B5D0AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F5B86F62039931F1010F9052 /* English */; + name = "English: 5"; + rLen = 0; + rLoc = 167; + rType = 0; + vrLen = 277; + vrLoc = 0; + }; + BC833B5E0AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCBE2360093B34BD00FAD628 /* Info-SynergyApp__Upgraded_.plist */; + name = "Info-SynergyApp__Upgraded_.plist: 49"; + rLen = 0; + rLoc = 1425; + rType = 0; + vrLen = 1291; + vrLoc = 333; + }; + BC833B5F0AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDF68280753B6A100EF20AF /* Japanese */; + name = 3.0; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 531; + vrLoc = 43; + }; + BC833B600AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDF68290753B6A100EF20AF /* French */; + name = 3.0; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 629; + vrLoc = 0; + }; + BC833B610AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDF682A0753B6A100EF20AF /* ru */; + name = 3.0; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 640; + vrLoc = 0; + }; + BC833B620AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDF682C0753B6A100EF20AF /* Norwegian */; + name = 3.0; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 620; + vrLoc = 0; + }; + BC833B630AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDF682F0753B6A100EF20AF /* German */; + name = 3.0; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 676; + vrLoc = 0; + }; + BC833B640AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDF68300753B6A100EF20AF /* Spanish */; + name = 3.0; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 643; + vrLoc = 0; + }; + BC833B650AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC689892046AC30F00A80001 /* English */; + name = "English: 34"; + rLen = 0; + rLoc = 652; + rType = 0; + vrLen = 652; + vrLoc = 0; + }; + BC833B660AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC3C3172080C9ECD00DF9C20 /* zh_CN */; + name = "zh_CN: 1"; + rLen = 0; + rLoc = 11; + rType = 0; + vrLen = 631; + vrLoc = 0; + }; + BC833B670AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCB5C0E107D7DF4A00D08CA9 /* tr */; + name = 3.0; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 631; + vrLoc = 0; + }; + BC833B680AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCD8712707669D4300FAA179 /* it */; + name = 3.0; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 671; + vrLoc = 0; + }; + BC833B690AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCD8711207669C8900FAA179 /* zh_TW */; + name = 3.0; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 469; + vrLoc = 0; + }; + BC833B6A0AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F58C12A903991E3601B0D373 /* English */; + name = 3.0; + rLen = 3; + rLoc = 164; + rType = 0; + vrLen = 279; + vrLoc = 0; + }; + BC833B6B0AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F58C12AB03991E3601B0D373 /* English */; + name = "English: 70"; + rLen = 45; + rLoc = 2255; + rType = 0; + vrLen = 1301; + vrLoc = 1666; + }; + BC833B720AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC9272FE040B7B6F0099CFAC /* TODO.rtfd */; + name = "TODO.rtfd: 601"; + rLen = 0; + rLoc = 33989; + rType = 0; + vrLen = 1563; + vrLoc = 31345; + }; + BC833B8A0AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC689892046AC30F00A80001 /* English */; + name = "English: 34"; + rLen = 0; + rLoc = 652; + rType = 0; + vrLen = 652; + vrLoc = 0; + }; + BC833B900AAC39D000D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F58C12AB03991E3601B0D373 /* English */; + name = "English: 70"; + rLen = 45; + rLoc = 2255; + rType = 0; + vrLen = 1301; + vrLoc = 1666; + }; + BC833BA30AAC45C600D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC7B627B03D460B300B1B2E1 /* WOSynergyFloaterWindow.h */; + name = "WOSynergyFloaterWindow.h: 27"; + rLen = 0; + rLoc = 874; + rType = 0; + vrLen = 1115; + vrLoc = 0; + }; + BC833BA40AAC45C600D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC7B627C03D460B300B1B2E1 /* WOSynergyFloaterWindow.m */; + name = "WOSynergyFloaterWindow.m: 146"; + rLen = 0; + rLoc = 5960; + rType = 0; + vrLen = 1982; + vrLoc = 0; + }; + BC833BA80AAC45C600D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC7B627B03D460B300B1B2E1 /* WOSynergyFloaterWindow.h */; + name = "WOSynergyFloaterWindow.h: 27"; + rLen = 0; + rLoc = 874; + rType = 0; + vrLen = 1115; + vrLoc = 0; + }; + BC833BF00AAC50D600D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F543147A0394BAF401AC36F3 /* SynergyController.h */; + name = "SynergyController.h: 6"; + rLen = 0; + rLoc = 116; + rType = 0; + vrLen = 1131; + vrLoc = 0; + }; + BC833C110AAC566A00D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDC33E003E2799000A59DD4 /* WOFeedbackView.h */; + name = "WOFeedbackView.h: 81"; + rLen = 0; + rLoc = 1962; + rType = 0; + vrLen = 660; + vrLoc = 24; + }; + BC833C1C0AAC5F0300D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDC33E103E2799000A59DD4 /* WOFeedbackView.m */; + name = "WOFeedbackView.m: 289"; + rLen = 0; + rLoc = 8420; + rType = 0; + vrLen = 1872; + vrLoc = 0; + }; + BC833C1D0AAC5F0300D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC7B627803D4602200B1B2E1 /* WOSynergyFloaterView.m */; + name = "- (NSImage *)coverImage"; + rLen = 24; + rLoc = 45743; + rType = 0; + vrLen = 1186; + vrLoc = 44787; + }; + BC833C1E0AAC5F0300D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F56E9BFC0394A55F010CA6FF /* OrgWincentSynergyPref.h */; + name = WO_CREDITS_SCROLL_TIME_VALUE; + rLen = 28; + rLoc = 821; + rType = 0; + vrLen = 2075; + vrLoc = 9849; + }; + BC833C5A0AAC915300D8F5F7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F56E9BFD0394A55F010CA6FF /* OrgWincentSynergyPref.m */; + name = "OrgWincentSynergyPref.m: 5835"; + rLen = 0; + rLoc = 216373; + rType = 0; + vrLen = 1663; + vrLoc = 190046; + }; + BC880E7407399368009ADB84 /* main.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 350}}"; + sepNavSelRange = "{25, 0}"; + sepNavVisRect = "{{0, 0}, {940, 313}}"; + }; + }; + BC8E6DAF0792F0E100E16274 /* WOPluginCollection.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 2337}}"; + sepNavSelRange = "{3749, 0}"; + sepNavVisRect = "{{0, 0}, {766, 954}}"; + }; + }; + BC8E6DB00792F0E100E16274 /* WOPluginCollection.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 954}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {766, 954}}"; + }; + }; + BC8E6DC20792F1A200E16274 /* WOPlugin.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 954}}"; + sepNavSelRange = "{195, 0}"; + sepNavVisRect = "{{0, 0}, {766, 954}}"; + }; + }; + BC8E6DC30792F1A200E16274 /* WOPlugin.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 954}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {766, 954}}"; + }; + }; + BC911C1303C31CBC000D6AD2 /* WOKeyCaptureView.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 1106}}"; + sepNavSelRange = "{45, 6}"; + sepNavVisRect = "{{0, 0}, {940, 834}}"; + }; + }; + BC911C1403C31CBC000D6AD2 /* WOKeyCaptureView.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 5404}}"; + sepNavSelRange = "{7594, 0}"; + sepNavVisRect = "{{0, 3024}, {940, 834}}"; + }; + }; + BC9272FE040B7B6F0099CFAC /* TODO.rtfd */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 9091}}"; + sepNavSelRange = "{33989, 0}"; + sepNavVisRect = "{{0, 8123}, {766, 954}}"; + }; + }; + BC92735A040D192A0099CFAC /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F543147B0394BAF401AC36F3 /* SynergyController.m */; + name = "SynergyController.m: tellITunesToggleShuffle"; + rLen = 0; + rLoc = 217574; + rType = 0; + vrLen = 535; + vrLoc = 110636; + }; + BC9576F106B29759006F9C43 /* create-branch.sh */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {734, 476}}"; + sepNavSelRange = "{611, 0}"; + sepNavVisRect = "{{0, 0}, {734, 476}}"; + }; + }; + BC9576F206B29759006F9C43 /* tag-release.sh */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {734, 476}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {734, 476}}"; + }; + }; + BC98E7140A39A71A0048ADFF /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCBE22E5093B34BD00FAD628 /* Info-SynergyPref__Upgraded_.plist */; + name = 2.8; + rLen = 3; + rLoc = 352; + rType = 0; + vrLen = 1097; + vrLoc = 0; + }; + BC98E7160A39A71A0048ADFF /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F54314780394BAF401AC36F3 /* HotkeyCapableApplication.h */; + name = "WOSynergyPreferences *"; + rLen = 22; + rLoc = 436; + rType = 0; + vrLen = 1302; + vrLoc = 483; + }; + BC98E7560A39D8C60048ADFF /* NSImage+WOAdditions.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 654}}"; + sepNavSelRange = "{196, 0}"; + sepNavVisRect = "{{0, 0}, {940, 654}}"; + }; + }; + BC98E7570A39D8C60048ADFF /* NSImage+WOAdditions.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1393, 798}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {1393, 323}}"; + }; + }; + BC98E7CB0A39F0EF0048ADFF /* NSAppleScript+WOAdditions.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 654}}"; + sepNavSelRange = "{265, 105}"; + sepNavVisRect = "{{0, 0}, {940, 654}}"; + }; + }; + BC98E7CC0A39F0EF0048ADFF /* NSAppleScript+WOAdditions.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 646}}"; + sepNavSelRange = "{1653, 0}"; + sepNavVisRect = "{{0, 0}, {940, 646}}"; + }; + }; + BC98E9E20A39F56F0048ADFF /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC98E9E30A39F56F0048ADFF /* CoverView.m */; + name = "(null): 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 1812; + vrLoc = 0; + }; + BC98E9E30A39F56F0048ADFF /* CoverView.m */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.objc; + name = CoverView.m; + path = "/Users/wincent/Desktop/Synergy Classic, llegando/Clutter/Source/CoverView.m"; + sourceTree = ""; + }; + BC98E9E40A39F56F0048ADFF /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC98E9E50A39F56F0048ADFF /* NSBitmapImageRep.h */; + name = "- (int)bytesPerRow;"; + rLen = 20; + rLoc = 3681; + rType = 0; + vrLen = 2113; + vrLoc = 2921; + }; + BC98E9E50A39F56F0048ADFF /* NSBitmapImageRep.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = NSBitmapImageRep.h; + path = /Developer/SDKs/MacOSX10.2.8.sdk/System/Library/Frameworks/AppKit.framework/Versions/C/Headers/NSBitmapImageRep.h; + sourceTree = ""; + }; + BC98E9E60A39F56F0048ADFF /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC98E9E70A39F56F0048ADFF /* Quickdraw.h */; + name = "typedef GrafPtr CGrafPtr;"; + rLen = 50; + rLoc = 52006; + rType = 0; + vrLen = 2181; + vrLoc = 50958; + }; + BC98E9E70A39F56F0048ADFF /* Quickdraw.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = Quickdraw.h; + path = /Developer/SDKs/MacOSX10.2.8.sdk/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/QD.framework/Versions/A/Headers/Quickdraw.h; + sourceTree = ""; + }; + BC98E9E80A39F56F0048ADFF /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC98E9E90A39F56F0048ADFF /* ImageCompression.h */; + name = "GraphicsImporterComponentType = 'grip'"; + rLen = 41; + rLoc = 130575; + rType = 0; + vrLen = 1547; + vrLoc = 139964; + }; + BC98E9E90A39F56F0048ADFF /* ImageCompression.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = ImageCompression.h; + path = /Developer/SDKs/MacOSX10.2.8.sdk/System/Library/Frameworks/QuickTime.framework/Versions/A/Headers/ImageCompression.h; + sourceTree = ""; + }; + BC98E9EA0A39F56F0048ADFF /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC98E9EB0A39F56F0048ADFF /* MacMemory.h */; + name = "PtrToHand("; + rLen = 11; + rLoc = 40028; + rType = 0; + vrLen = 1239; + vrLoc = 39380; + }; + BC98E9EB0A39F56F0048ADFF /* MacMemory.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = MacMemory.h; + path = /Developer/SDKs/MacOSX10.2.8.sdk/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/Headers/MacMemory.h; + sourceTree = ""; + }; + BC98E9EC0A39F56F0048ADFF /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC98E9ED0A39F56F0048ADFF /* MacTypes.h */; + name = char; + rLen = 4; + rLoc = 6438; + rType = 0; + vrLen = 2253; + vrLoc = 5708; + }; + BC98E9ED0A39F56F0048ADFF /* MacTypes.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = MacTypes.h; + path = /Developer/SDKs/MacOSX10.2.8.sdk/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/Headers/MacTypes.h; + sourceTree = ""; + }; + BC98E9EE0A39F56F0048ADFF /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC98E7570A39D8C60048ADFF /* NSImage+WOAdditions.m */; + name = "NSImage+WOAdditions.m: 14"; + rLen = 0; + rLoc = 240; + rType = 0; + vrLen = 1633; + vrLoc = 126; + }; + BC98E9EF0A39F56F0048ADFF /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC98E7560A39D8C60048ADFF /* NSImage+WOAdditions.h */; + name = "NSImage+WOAdditions.h: PICTRepresentation"; + rLen = 0; + rLoc = 196; + rType = 0; + vrLen = 234; + vrLoc = 0; + }; + BC98E9F00A39F56F0048ADFF /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC5D0AC3042FCFFE00A80001 /* WONSFileManagerExtensions.h */; + name = "WONSFileManagerExtensions.h: 15"; + rLen = 0; + rLoc = 422; + rType = 0; + vrLen = 482; + vrLoc = 0; + }; + BC98E9F10A39F56F0048ADFF /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC5D0AC4042FCFFE00A80001 /* WONSFileManagerExtensions.m */; + name = Create; + rLen = 6; + rLoc = 1221; + rType = 0; + vrLen = 1378; + vrLoc = 47; + }; + BC98E9F20A39F56F0048ADFF /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC98E7CB0A39F0EF0048ADFF /* NSAppleScript+WOAdditions.h */; + name = "- (NSAppleEventDescriptor *)executeWithParameters:(NSArray *)parameters error:(NSDictionary **)errorInfo;"; + rLen = 105; + rLoc = 265; + rType = 0; + vrLen = 377; + vrLoc = 0; + }; + BC98E9F50A39F56F0048ADFF /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC98E9F60A39F56F0048ADFF /* QuickTimeComponents.h */; + name = JFIF; + rLen = 4; + rLoc = 43997; + rType = 0; + vrLen = 2004; + vrLoc = 42946; + }; + BC98E9F60A39F56F0048ADFF /* QuickTimeComponents.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = QuickTimeComponents.h; + path = /Developer/SDKs/MacOSX10.2.8.sdk/System/Library/Frameworks/QuickTime.framework/Versions/A/Headers/QuickTimeComponents.h; + sourceTree = ""; + }; + BC98E9F90A39F56F0048ADFF /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC98E7560A39D8C60048ADFF /* NSImage+WOAdditions.h */; + name = "NSImage (WOAdditions)"; + rLen = 22; + rLoc = 172; + rType = 0; + vrLen = 201; + vrLoc = 0; + }; + BC98E9FC0A39F56F0048ADFF /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC98E9FD0A39F56F0048ADFF /* NSBitmapImageRep.h */; + name = isPlanar; + rLen = 8; + rLoc = 3626; + rType = 0; + vrLen = 2434; + vrLoc = 160; + }; + BC98E9FD0A39F56F0048ADFF /* NSBitmapImageRep.h */ = { + isa = PBXFileReference; + lastKnownFileType = sourcecode.c.h; + name = NSBitmapImageRep.h; + path = /Developer/SDKs/MacOSX10.2.8.sdk/System/Library/Frameworks/AppKit.framework/Versions/C/Headers/NSBitmapImageRep.h; + sourceTree = ""; + }; + BC98EA010A39F56F0048ADFF /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC98E7570A39D8C60048ADFF /* NSImage+WOAdditions.m */; + name = "NSImage+WOAdditions.m: 26"; + rLen = 0; + rLoc = 240; + rType = 0; + vrLen = 1541; + vrLoc = 281; + }; + BC98EA080A39F56F0048ADFF /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC5D0AC4042FCFFE00A80001 /* WONSFileManagerExtensions.m */; + name = Create; + rLen = 6; + rLoc = 1221; + rType = 0; + vrLen = 1300; + vrLoc = 0; + }; + BC98EA090A39F56F0048ADFF /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC5D0AC3042FCFFE00A80001 /* WONSFileManagerExtensions.h */; + name = "WONSFileManagerExtensions.h: 15"; + rLen = 0; + rLoc = 422; + rType = 0; + vrLen = 482; + vrLoc = 0; + }; + BC98EA0B0A39F56F0048ADFF /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC98E7CB0A39F0EF0048ADFF /* NSAppleScript+WOAdditions.h */; + name = "NSAppleScript (WOAdditions)"; + rLen = 27; + rLoc = 178; + rType = 0; + vrLen = 213; + vrLoc = 0; + }; + BC98EA0C0A39F56F0048ADFF /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC98E7CC0A39F0EF0048ADFF /* NSAppleScript+WOAdditions.m */; + name = "- (NSAppleEventDescriptor *)executeWithParameters:(NSArray *)parameters error:(NSDictionary **)errorInfo"; + rLen = 104; + rLoc = 226; + rType = 0; + vrLen = 342; + vrLoc = 0; + }; + BC98EA280A39F7910048ADFF /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F543147B0394BAF401AC36F3 /* SynergyController.m */; + name = "SynergyController.m: 6229"; + rLen = 0; + rLoc = 232383; + rType = 0; + vrLen = 1846; + vrLoc = 232400; + }; + BC9A268309261479003E5241 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCD86C87076696EA00FAA179 /* pt */; + name = "pt: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 2106; + vrLoc = 0; + }; + BC9A26AF09261479003E5241 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCD86C87076696EA00FAA179 /* pt */; + name = "pt: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 2106; + vrLoc = 0; + }; + BC9A6D390757EB39007BD7AC /* InfoPlist.strings */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {705, 836}}"; + sepNavSelRange = "{280, 0}"; + sepNavVisRect = "{{0, 0}, {705, 836}}"; + }; + }; + BC9D31EF084121870027F5CB /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F56E9BFC0394A55F010CA6FF /* OrgWincentSynergyPref.h */; + name = WO_CREDITS_SCROLL_TIME_VALUE; + rLen = 28; + rLoc = 821; + rType = 0; + vrLen = 1880; + vrLoc = 0; + }; + BCA7F4A3048379BC00A80001 /* Swedish */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 464}}"; + sepNavSelRange = "{117, 3}"; + sepNavVisRect = "{{0, 0}, {940, 464}}"; + }; + }; + BCA7F4A404837A0C00A80001 /* Dutch */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 464}}"; + sepNavSelRange = "{116, 3}"; + sepNavVisRect = "{{0, 0}, {940, 464}}"; + }; + }; + BCA7F4A504837A4100A80001 /* Swedish */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {770, 2365}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {770, 526}}"; + }; + }; + BCA7F4A704837A9300A80001 /* Swedish */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {705, 843}}"; + sepNavSelRange = "{1, 0}"; + sepNavVisRect = "{{0, 0}, {705, 843}}"; + }; + }; + BCA7F4A804837B7300A80001 /* Swedish */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {990, 1679}}"; + sepNavSelRange = "{1914, 0}"; + sepNavVisRect = "{{0, 809}, {990, 329}}"; + }; + }; + BCAF0AC7073730530043CFC7 /* Info.plist */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {705, 843}}"; + sepNavSelRange = "{699, 0}"; + sepNavVisRect = "{{0, 0}, {705, 843}}"; + }; + }; + BCB0BD8A0682231300FFC46C /* Norwegian */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1016, 2282}}"; + sepNavSelRange = "{2275, 7}"; + sepNavVisRect = "{{0, 802}, {1016, 398}}"; + }; + }; + BCB0D18403DD36FE005CFB0B /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC56F22403DC5A1800BD6ADA /* WOSynergyAnchorController.m */; + name = "WOSynergyAnchorController.m: newRect"; + rLen = 0; + rLoc = 8700; + rType = 0; + vrLen = 1449; + vrLoc = 3062; + }; + BCB0D18503DD3702005CFB0B /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC56F22303DC5A1800BD6ADA /* WOSynergyAnchorController.h */; + name = "WOSynergyAnchorController.h: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 1348; + vrLoc = 0; + }; + BCB5C0E107D7DF4A00D08CA9 /* tr */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 897}}"; + sepNavSelRange = "{8, 3}"; + sepNavVisRect = "{{0, 0}, {940, 646}}"; + }; + }; + BCB5C0F107D7DF5900D08CA9 /* tr */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {683, 2450}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {683, 910}}"; + }; + }; + BCB5C0F807D7DF7B00D08CA9 /* tr */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 1735}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {766, 999}}"; + }; + }; + BCBB9A360470726C00A80001 /* Dutch */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {770, 1623}}"; + sepNavSelRange = "{1822, 0}"; + sepNavVisRect = "{{0, 696}, {770, 526}}"; + }; + }; + BCBB9A370470727600A80001 /* Dutch */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 646}}"; + sepNavSelRange = "{162, 3}"; + sepNavVisRect = "{{0, 0}, {940, 646}}"; + }; + }; + BCBE228A093B34BD00FAD628 /* SynergyPref (Upgraded) */ = { + activeExec = 0; + }; + BCBE22E5093B34BD00FAD628 /* Info-SynergyPref__Upgraded_.plist */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 490}}"; + sepNavSelRange = "{352, 3}"; + sepNavVisRect = "{{0, 0}, {940, 464}}"; + }; + }; + BCBE22EA093B34BD00FAD628 /* SynergyApp (Upgraded) */ = { + activeExec = 0; + executables = ( + BCBE2365093B34BD00FAD628 /* SynergyApp (Upgraded) */, + ); + }; + BCBE2360093B34BD00FAD628 /* Info-SynergyApp__Upgraded_.plist */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1016, 812}}"; + sepNavSelRange = "{1425, 0}"; + sepNavVisRect = "{{0, 376}, {1016, 405}}"; + }; + }; + BCBE2365093B34BD00FAD628 /* SynergyApp (Upgraded) */ = { + isa = PBXExecutable; + activeArgIndex = 2147483647; + activeArgIndices = ( + ); + argumentStrings = ( + ); + autoAttachOnCrash = 1; + configStateDict = { + }; + customDataFormattersEnabled = 1; + debuggerPlugin = GDBDebugging; + disassemblyDisplayState = 0; + enableDebugStr = 1; + environmentEntries = ( + ); + executableSystemSymbolLevel = 0; + executableUserSymbolLevel = 0; + libgmallocEnabled = 0; + name = "SynergyApp (Upgraded)"; + sourceDirectories = ( + ); + }; + BCBE23E0093B389D00FAD628 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCBE22E5093B34BD00FAD628 /* Info-SynergyPref__Upgraded_.plist */; + name = "Info-SynergyPref__Upgraded_.plist: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 1097; + vrLoc = 0; + }; + BCBE23E1093B389D00FAD628 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCBE2360093B34BD00FAD628 /* Info-SynergyApp__Upgraded_.plist */; + name = "Info-SynergyApp__Upgraded_.plist: 49"; + rLen = 0; + rLoc = 1425; + rType = 0; + vrLen = 1624; + vrLoc = 0; + }; + BCBE2419093B3A9F00FAD628 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F54FD5E70393F70D010D9943 /* RELEASE PROCEDURE */; + name = "RELEASE PROCEDURE: 44"; + rLen = 0; + rLoc = 1326; + rType = 0; + vrLen = 905; + vrLoc = 771; + }; + BCBE26FB093B3CC100FAD628 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC5D0AC7042FDFFF00A80001 /* WOExceptions.h */; + name = "#define WO_INTERNET_VERSION_CHECK_EXCEPTION\t\t\t\\"; + rLen = 48; + rLoc = 966; + rType = 0; + vrLen = 1048; + vrLoc = 0; + }; + BCC066EF045BF68A00A80001 /* WOPopUpButton.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {826, 545}}"; + sepNavSelRange = "{42, 6}"; + sepNavVisRect = "{{0, 0}, {826, 209}}"; + }; + }; + BCC066F0045BF68A00A80001 /* WOPopUpButton.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {996, 5417}}"; + sepNavSelRange = "{3741, 24}"; + sepNavVisRect = "{{0, 1253}, {996, 645}}"; + }; + }; + BCC06707045CF19700A80001 /* French */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 464}}"; + sepNavSelRange = "{164, 3}"; + sepNavVisRect = "{{0, 0}, {940, 464}}"; + }; + }; + BCC06708045D06BE00A80001 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCC066EF045BF68A00A80001 /* WOPopUpButton.h */; + name = "WOPopUpButton.h: 18"; + rLen = 0; + rLoc = 431; + rType = 0; + vrLen = 842; + vrLoc = 0; + }; + BCC06709045D179D00A80001 /* WOButtonCellWithTrackingRect.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {826, 531}}"; + sepNavSelRange = "{57, 6}"; + sepNavVisRect = "{{0, 0}, {826, 209}}"; + }; + }; + BCC0670C045D1B8400A80001 /* WOButtonCellWithTrackingRect.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {990, 7602}}"; + sepNavSelRange = "{9816, 0}"; + sepNavVisRect = "{{0, 1614}, {990, 329}}"; + }; + }; + BCC2DD090445BC6B00A80001 /* WOSynergyFloater.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {996, 645}}"; + sepNavSelRange = "{301, 11}"; + sepNavVisRect = "{{0, 0}, {996, 645}}"; + }; + }; + BCC7DCE10844C00B003A8CF0 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC7B627703D4602200B1B2E1 /* WOSynergyFloaterView.h */; + name = setAlbumImagePath; + rLen = 17; + rLoc = 5917; + rType = 0; + vrLen = 1287; + vrLoc = 4825; + }; + BCC8715F05853E3F00054D38 /* Source Control */ = { + isa = PBXSourceControlManager; + fallbackIsa = XCSourceControlManager; + isSCMEnabled = 1; + scmConfiguration = { + }; + scmType = scm.subversion; + }; + BCC8716005853E3F00054D38 /* Code sense */ = { + isa = PBXCodeSenseManager; + indexTemplatePath = ""; + }; + BCC8716105853E6200054D38 /* LoginItemAPI.c */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {990, 8441}}"; + sepNavSelRange = "{518, 6}"; + sepNavVisRect = "{{0, 2236}, {990, 329}}"; + }; + }; + BCC8716205853E6200054D38 /* LoginItemAPI.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {904, 853}}"; + sepNavSelRange = "{2885, 0}"; + sepNavVisRect = "{{0, 545}, {904, 308}}"; + sepNavWindowFrame = "{{38, 162}, {750, 558}}"; + }; + }; + BCCC9AF603CD9A5400B03CAD /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F577917C0395498401EE990F /* WODistributedNotification.m */; + name = "WODistributedNotification.m: makeAppObserver:selector:"; + rLen = 0; + rLoc = 11597; + rType = 0; + vrLen = 1453; + vrLoc = 506; + }; + BCCC9AF703CD9A5900B03CAD /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F577917B0395498401EE990F /* WODistributedNotification.h */; + name = "WODistributedNotification.h: 153"; + rLen = 0; + rLoc = 4997; + rType = 0; + vrLen = 382; + vrLoc = 4293; + }; + BCCFC98503D1A3050006CDE7 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F56E9BFC0394A55F010CA6FF /* OrgWincentSynergyPref.h */; + name = "OrgWincentSynergyPref.h: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 1285; + vrLoc = 0; + }; + BCD86C680766964800FAA179 /* pt */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {705, 1763}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {705, 843}}"; + }; + }; + BCD86C87076696EA00FAA179 /* pt */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 2338}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {940, 889}}"; + }; + }; + BCD86CA507669B3600FAA179 /* zh_TW */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 646}}"; + sepNavSelRange = "{154, 3}"; + sepNavVisRect = "{{0, 0}, {940, 646}}"; + }; + }; + BCD86CA607669B3600FAA179 /* zh_TW */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 1694}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {766, 953}}"; + }; + }; + BCD8710B07669C4A00FAA179 /* zh_TW */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 464}}"; + sepNavSelRange = "{112, 3}"; + sepNavVisRect = "{{0, 0}, {940, 464}}"; + }; + }; + BCD8710C07669C4A00FAA179 /* zh_TW */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 2305}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {766, 953}}"; + }; + }; + BCD8710E07669C4A00FAA179 /* zh_TW */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 953}}"; + sepNavSelRange = "{651, 0}"; + sepNavVisRect = "{{0, 0}, {766, 953}}"; + }; + }; + BCD8711207669C8900FAA179 /* zh_TW */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 821}}"; + sepNavSelRange = "{8, 3}"; + sepNavVisRect = "{{0, 0}, {940, 646}}"; + sepNavWindowFrame = "{{15, 615}, {750, 558}}"; + }; + }; + BCD8712707669D4300FAA179 /* it */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 939}}"; + sepNavSelRange = "{8, 3}"; + sepNavVisRect = "{{0, 0}, {940, 646}}"; + }; + }; + BCD8712807669D4300FAA179 /* it */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 464}}"; + sepNavSelRange = "{118, 3}"; + sepNavVisRect = "{{0, 0}, {940, 464}}"; + }; + }; + BCD8712B07669D4300FAA179 /* it */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 953}}"; + sepNavSelRange = "{651, 0}"; + sepNavVisRect = "{{0, 0}, {766, 953}}"; + }; + }; + BCD8714007669DC400FAA179 /* it */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 646}}"; + sepNavSelRange = "{166, 3}"; + sepNavVisRect = "{{0, 0}, {940, 646}}"; + }; + }; + BCD8714107669DC400FAA179 /* it */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 1749}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {766, 999}}"; + }; + }; + BCDA4649083A652E009B5FA8 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC08FB9E0404F1E00099965D /* WOSysctl.c */; + name = "error = sysctl(mib, 3, NULL, &sizeOfBufferRequired, NULL, NULL);"; + rLen = 70; + rLoc = 5414; + rType = 0; + vrLen = 1627; + vrLoc = 0; + }; + BCDC33E003E2799000A59DD4 /* WOFeedbackView.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {972, 1512}}"; + sepNavSelRange = "{1962, 0}"; + sepNavVisRect = "{{0, 34}, {972, 315}}"; + }; + }; + BCDC33E103E2799000A59DD4 /* WOFeedbackView.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1393, 7210}}"; + sepNavSelRange = "{12641, 7}"; + sepNavVisRect = "{{0, 5045}, {1393, 389}}"; + }; + }; + BCDC33E403E279CB00A59DD4 /* WOFeedbackController.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {683, 2212}}"; + sepNavSelRange = "{49, 6}"; + sepNavVisRect = "{{0, 1485}, {683, 713}}"; + }; + }; + BCDC33E503E279CB00A59DD4 /* WOFeedbackController.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {683, 5362}}"; + sepNavSelRange = "{854, 0}"; + sepNavVisRect = "{{0, 0}, {683, 713}}"; + }; + }; + BCDC33E803E279D900A59DD4 /* WOFeedbackWindow.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {705, 843}}"; + sepNavSelRange = "{45, 6}"; + sepNavVisRect = "{{0, 0}, {705, 843}}"; + }; + }; + BCDC33E903E279D900A59DD4 /* WOFeedbackWindow.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {683, 882}}"; + sepNavSelRange = "{994, 31}"; + sepNavVisRect = "{{0, 169}, {683, 713}}"; + }; + }; + BCDDDF360431F0C600A80001 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC037D110430328600A80001 /* WOControlButtons.m */; + name = "WOControlButtons.m: 663"; + rLen = 0; + rLoc = 28767; + rType = 0; + vrLen = 943; + vrLoc = 22677; + }; + BCDF68280753B6A100EF20AF /* Japanese */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 919}}"; + sepNavSelRange = "{8, 3}"; + sepNavVisRect = "{{0, 77}, {940, 646}}"; + }; + }; + BCDF68290753B6A100EF20AF /* French */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 897}}"; + sepNavSelRange = "{8, 3}"; + sepNavVisRect = "{{0, 0}, {940, 646}}"; + }; + }; + BCDF682A0753B6A100EF20AF /* ru */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 925}}"; + sepNavSelRange = "{8, 3}"; + sepNavVisRect = "{{0, 0}, {940, 646}}"; + }; + }; + BCDF682B0753B6A100EF20AF /* Swedish */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 897}}"; + sepNavSelRange = "{8, 3}"; + sepNavVisRect = "{{0, 0}, {940, 464}}"; + }; + }; + BCDF682C0753B6A100EF20AF /* Norwegian */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 897}}"; + sepNavSelRange = "{8, 3}"; + sepNavVisRect = "{{0, 0}, {940, 646}}"; + }; + }; + BCDF682D0753B6A100EF20AF /* English */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {972, 897}}"; + sepNavSelRange = "{8, 3}"; + sepNavVisRect = "{{0, 0}, {972, 649}}"; + }; + }; + BCDF682E0753B6A100EF20AF /* Dutch */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 897}}"; + sepNavSelRange = "{8, 3}"; + sepNavVisRect = "{{0, 0}, {940, 464}}"; + }; + }; + BCDF682F0753B6A100EF20AF /* German */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 897}}"; + sepNavSelRange = "{8, 3}"; + sepNavVisRect = "{{0, 0}, {940, 646}}"; + }; + }; + BCDF68300753B6A100EF20AF /* Spanish */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 897}}"; + sepNavSelRange = "{8, 3}"; + sepNavVisRect = "{{0, 0}, {940, 646}}"; + }; + }; + BCE03A5A061ACC1A00238E7E /* ru */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 953}}"; + sepNavSelRange = "{638, 0}"; + sepNavVisRect = "{{0, 0}, {766, 953}}"; + }; + }; + BCE15EA803E459B6005E6BE2 /* WOFeedbackDefaults.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1016, 840}}"; + sepNavSelRange = "{1548, 3}"; + sepNavVisRect = "{{0, 249}, {1016, 398}}"; + }; + }; + BCE1E95004131268006F5C0D /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC7B627F03D4613B00B1B2E1 /* WOSynergyFloaterController.h */; + name = "WOSynergyFloaterController.h: 123"; + rLen = 0; + rLoc = 4273; + rType = 0; + vrLen = 1469; + vrLoc = 3330; + }; + BCE4635F03CAB6BF00CB9B3F /* WONSStringExtensions.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 953}}"; + sepNavSelRange = "{335, 57}"; + sepNavVisRect = "{{0, 0}, {766, 953}}"; + }; + }; + BCE4636003CAB6BF00CB9B3F /* WONSStringExtensions.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1016, 2562}}"; + sepNavSelRange = "{1140, 5}"; + sepNavVisRect = "{{0, 368}, {1016, 398}}"; + }; + }; + BCE8D3E90420BEAA00A80001 /* WOButtonWithTrackingRect.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {826, 573}}"; + sepNavSelRange = "{53, 6}"; + sepNavVisRect = "{{0, 0}, {826, 209}}"; + }; + }; + BCE8D3EA0420BEAA00A80001 /* WOButtonWithTrackingRect.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 2841}}"; + sepNavSelRange = "{153, 0}"; + sepNavVisRect = "{{0, 998}, {766, 999}}"; + }; + }; + BCE8D3ED0420BEBB00A80001 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCE8D3EA0420BEAA00A80001 /* WOButtonWithTrackingRect.m */; + name = "WOButtonWithTrackingRect.m: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 259; + vrLoc = 0; + }; + BCE8D3EE0420BEBB00A80001 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCE8D3EA0420BEAA00A80001 /* WOButtonWithTrackingRect.m */; + name = "WOButtonWithTrackingRect.m: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 259; + vrLoc = 0; + }; + BCE8D3EF0420C0F100A80001 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F56702DC03A4BC7201842AA0 /* WOSynergyView.h */; + name = "WOSynergyView.h: 1"; + rLen = 0; + rLoc = 0; + rType = 0; + vrLen = 853; + vrLoc = 0; + }; + BCE8D3F00420C52E00A80001 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCE8D3EA0420BEAA00A80001 /* WOButtonWithTrackingRect.m */; + name = "WOButtonWithTrackingRect.m: 37"; + rLen = 0; + rLoc = 1012; + rType = 0; + vrLen = 533; + vrLoc = 845; + }; + BCEB8EB8093DF01400949543 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC5CC3270409B4E2001CCC4A /* WOButtonSet.m */; + name = "WOButtonSet.m: 84"; + rLen = 0; + rLoc = 3134; + rType = 0; + vrLen = 2353; + vrLoc = 0; + }; + BCEDABE00AFA8EF90036B190 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC617E000AF7CC9D00E1268F /* WOAudioscrobbler.m */; + name = "WOAudioscrobbler.m: challengeResponse"; + rLen = 0; + rLoc = 9001; + rType = 0; + vrLen = 1703; + vrLoc = 13041; + }; + BCEDABE40AFA8F9B0036B190 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC617E000AF7CC9D00E1268F /* WOAudioscrobbler.m */; + name = "WOAudioscrobbler.m: challengeResponse"; + rLen = 0; + rLoc = 9001; + rType = 0; + vrLen = 1703; + vrLoc = 13041; + }; + BCEDBF7F073752D300A1585C /* iTunesNotifications.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 954}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {766, 954}}"; + }; + }; + BCEDBF80073752D300A1585C /* iTunesNotifications.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 2757}}"; + sepNavSelRange = "{370, 6016}"; + sepNavVisRect = "{{0, 1791}, {766, 954}}"; + }; + }; + BCEDBF81073752D300A1585C /* WOAppDelegate.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 954}}"; + sepNavSelRange = "{298, 0}"; + sepNavVisRect = "{{0, 0}, {766, 954}}"; + }; + }; + BCEDBF82073752D300A1585C /* WOAppDelegate.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 954}}"; + sepNavSelRange = "{369, 0}"; + sepNavVisRect = "{{0, 0}, {766, 954}}"; + }; + }; + BCEDBF83073752D300A1585C /* WOFloater.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {591, 913}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {591, 913}}"; + }; + }; + BCEDBF84073752D300A1585C /* WOFloater.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {705, 2561}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 1718}, {705, 843}}"; + }; + }; + BCEDBF85073752D300A1585C /* WOFloaterView.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 4479}}"; + sepNavSelRange = "{8204, 0}"; + sepNavVisRect = "{{0, 3429}, {766, 913}}"; + }; + }; + BCEDBF86073752D300A1585C /* WOFloaterView.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {705, 15371}}"; + sepNavSelRange = "{20609, 0}"; + sepNavVisRect = "{{0, 14308}, {705, 843}}"; + }; + }; + BCEDBF87073752D300A1585C /* WOFloaterWindow.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 913}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {766, 913}}"; + }; + }; + BCEDBF88073752D300A1585C /* WOFloaterWindow.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {705, 1427}}"; + sepNavSelRange = "{2799, 58}"; + sepNavVisRect = "{{0, 584}, {705, 843}}"; + }; + }; + BCEDC0680737591300A1585C /* SynergyAdvancePreferencesMacros.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 840}}"; + sepNavSelRange = "{1318, 0}"; + sepNavVisRect = "{{0, 0}, {766, 445}}"; + }; + }; + BCF3AFAF060B3C9E00B2B13C /* ru */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {683, 1092}}"; + sepNavSelRange = "{42, 0}"; + sepNavVisRect = "{{0, 0}, {683, 910}}"; + }; + }; + BCF3AFB0060B3CAF00B2B13C /* ru */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 464}}"; + sepNavSelRange = "{115, 3}"; + sepNavVisRect = "{{0, 0}, {940, 464}}"; + }; + }; + BCF3AFB1060B3D2A00B2B13C /* ru */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 646}}"; + sepNavSelRange = "{108, 3}"; + sepNavVisRect = "{{0, 0}, {940, 646}}"; + }; + }; + BCF539400AB5F748007E56E5 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC30616403FA8FDD007C4E56 /* WOSynergyGlobal.h */; + name = "WOSynergyGlobal.h: 14"; + rLen = 0; + rLoc = 451; + rType = 0; + vrLen = 1172; + vrLoc = 3045; + }; + BCF539410AB5F748007E56E5 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC037D110430328600A80001 /* WOControlButtons.m */; + name = "SYN-100"; + rLen = 7; + rLoc = 27820; + rType = 0; + vrLen = 2172; + vrLoc = 30364; + }; + BCF539420AB5F748007E56E5 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BC037D100430328600A80001 /* WOControlButtons.h */; + name = "WOControlButtons.h: 49"; + rLen = 0; + rLoc = 3322; + rType = 0; + vrLen = 3653; + vrLoc = 609; + }; + BCF5408B0AB6A399007E56E5 /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = BCDF682D0753B6A100EF20AF /* English */; + name = 3.1; + rLen = 3; + rLoc = 8; + rType = 0; + vrLen = 632; + vrLoc = 0; + }; + F5277179039F7A0B013BBD0D /* WOPreferences.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {826, 1805}}"; + sepNavSelRange = "{123, 4}"; + sepNavVisRect = "{{0, 0}, {826, 209}}"; + }; + }; + F527717A039F7A0B013BBD0D /* WOPreferences.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1016, 5236}}"; + sepNavSelRange = "{1011, 5}"; + sepNavVisRect = "{{0, 368}, {1016, 398}}"; + }; + }; + F54314780394BAF401AC36F3 /* HotkeyCapableApplication.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 1092}}"; + sepNavSelRange = "{436, 22}"; + sepNavVisRect = "{{0, 252}, {940, 717}}"; + }; + }; + F54314790394BAF401AC36F3 /* HotkeyCapableApplication.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1393, 15218}}"; + sepNavSelRange = "{1309, 41}"; + sepNavVisRect = "{{0, 350}, {1393, 323}}"; + }; + }; + F543147A0394BAF401AC36F3 /* SynergyController.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1393, 5628}}"; + sepNavSelRange = "{3708, 25}"; + sepNavVisRect = "{{0, 1775}, {1393, 389}}"; + }; + }; + F543147B0394BAF401AC36F3 /* SynergyController.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1393, 95452}}"; + sepNavSelRange = "{6500, 25}"; + sepNavVisRect = "{{0, 2445}, {1393, 389}}"; + sepNavWindowFrame = "{{203, 188}, {750, 558}}"; + }; + }; + F543148F0394BB0E01AC36F3 /* main.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {587, 497}}"; + sepNavSelRange = "{33, 6}"; + sepNavVisRect = "{{0, 0}, {587, 497}}"; + }; + }; + F54314910394BB2101AC36F3 /* getSongInfo.applescript */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1125, 941}}"; + sepNavSelRange = "{1552, 0}"; + sepNavVisRect = "{{0, 0}, {1125, 941}}"; + }; + }; + F54E328403A1D9CC01E04078 /* SynergyPref */ = { + isa = PBXExecutable; + activeArgIndex = 2147483647; + activeArgIndices = ( + NO, + NO, + NO, + NO, + NO, + NO, + NO, + ); + argumentStrings = ( + "-AppleLanguages \"(English)\"", + "-AppleLanguages \"(Spanish)\"", + "-AppleLanguages \"(French)\"", + "-AppleLanguages \"(Japanese)\"", + "-AppleLanguages \"(German)\"", + "-AppleLanguages \"(Italian)\"", + "-AppleLanguages \"(Dutch)\"", + ); + autoAttachOnCrash = 1; + configStateDict = { + "PBXLSLaunchAction-1" = { + PBXLSLaunchAction = 1; + PBXLSLaunchStartAction = 1; + PBXLSLaunchStdioStyle = 2; + PBXLSLaunchStyle = 0; + class = PBXGDB_LaunchConfig; + identifier = com.apple.ProjectBuilder.launch.GDBMI_Config; + remoteHostInfo = ""; + startActionInfo = ""; + }; + }; + customDataFormattersEnabled = 1; + debuggerPlugin = GDBDebugging; + disassemblyDisplayState = 0; + dylibVariantSuffix = ""; + enableDebugStr = 1; + environmentEntries = ( + ); + executableSystemSymbolLevel = 0; + executableUserSymbolLevel = 0; + launchableReference = F54E328503A1D9CC01E04078 /* System Preferences.app */; + libgmallocEnabled = 0; + name = SynergyPref; + sourceDirectories = ( + ); + }; + F54E328503A1D9CC01E04078 /* System Preferences.app */ = { + isa = PBXFileReference; + explicitFileType = wrapper.application; + name = "System Preferences.app"; + path = "/Applications/System Preferences.app"; + sourceTree = ""; + }; + F54FD5E60393F6B0010D9943 /* build.sh */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {734, 1147}}"; + sepNavSelRange = "{89, 0}"; + sepNavVisRect = "{{0, 30}, {734, 476}}"; + }; + }; + F54FD5E70393F70D010D9943 /* RELEASE PROCEDURE */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {972, 728}}"; + sepNavSelRange = "{1676, 0}"; + sepNavVisRect = "{{0, 79}, {972, 649}}"; + }; + }; + F55DFBBE03A84D64010663F9 /* WODebug.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1016, 2506}}"; + sepNavSelRange = "{1347, 5}"; + sepNavVisRect = "{{0, 396}, {1016, 398}}"; + }; + }; + F5626BAD03AECF750185C75A /* PBXBookmarkGroup */ = { + isa = PBXBookmarkGroup; + children = ( + F5626BC803AF0E3B0185C75A /* PBXTextBookmark */, + F5E471CC03AF7D8401FB4C6E /* PBXTextBookmark */, + BC19436A03C1483700EFA70C /* PBXTextBookmark */, + BCCC9AF603CD9A5400B03CAD /* PBXTextBookmark */, + BCCC9AF703CD9A5900B03CAD /* PBXTextBookmark */, + BCCFC98503D1A3050006CDE7 /* PBXTextBookmark */, + BCB0D18403DD36FE005CFB0B /* PBXTextBookmark */, + BCB0D18503DD3702005CFB0B /* PBXTextBookmark */, + BC92735A040D192A0099CFAC /* PBXTextBookmark */, + BCE1E95004131268006F5C0D /* PBXTextBookmark */, + BC040E44041F67100052CE41 /* PBXTextBookmark */, + BC04126F041F684E0052CE41 /* PBXTextBookmark */, + BCE8D3ED0420BEBB00A80001 /* PBXTextBookmark */, + BCE8D3EE0420BEBB00A80001 /* PBXTextBookmark */, + BCE8D3EF0420C0F100A80001 /* PBXTextBookmark */, + BCE8D3F00420C52E00A80001 /* PBXTextBookmark */, + BC7DA10D042193D000A80001 /* PBXTextBookmark */, + BCDDDF360431F0C600A80001 /* PBXTextBookmark */, + BCC06708045D06BE00A80001 /* PBXTextBookmark */, + BC3E83FF045F354900A80001 /* PBXTextBookmark */, + ); + name = Root; + }; + F5626BC803AF0E3B0185C75A /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F5ECD2D103A5F4D7010DC0E7 /* WOHelp.h */; + name = "WOHelp.h: registerHelpBook:"; + rLen = 49; + rLoc = 659; + rType = 0; + vrLen = 394; + vrLoc = 0; + }; + F5626BC903AF15A80185C75A /* WOSynergyHelp.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 834}}"; + sepNavSelRange = "{318, 0}"; + sepNavVisRect = "{{0, 0}, {940, 834}}"; + }; + }; + F5626BCA03AF15A80185C75A /* WOSynergyHelp.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {826, 503}}"; + sepNavSelRange = "{330, 6}"; + sepNavVisRect = "{{0, 0}, {826, 353}}"; + }; + }; + F56702DC03A4BC7201842AA0 /* WOSynergyView.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 1889}}"; + sepNavSelRange = "{3686, 0}"; + sepNavVisRect = "{{0, 1020}, {766, 859}}"; + }; + }; + F56702DD03A4BC7201842AA0 /* WOSynergyView.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1016, 16268}}"; + sepNavSelRange = "{3317, 5}"; + sepNavVisRect = "{{0, 2547}, {1016, 398}}"; + sepNavWindowFrame = "{{61, 141}, {750, 558}}"; + }; + }; + F56E9BFC0394A55F010CA6FF /* OrgWincentSynergyPref.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1393, 6720}}"; + sepNavSelRange = "{7327, 25}"; + sepNavVisRect = "{{0, 2347}, {1393, 389}}"; + }; + }; + F56E9BFD0394A55F010CA6FF /* OrgWincentSynergyPref.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1393, 81886}}"; + sepNavSelRange = "{12740, 25}"; + sepNavVisRect = "{{0, 4279}, {1393, 389}}"; + sepNavWindowFrame = "{{133, 188}, {750, 558}}"; + }; + }; + F577917B0395498401EE990F /* WODistributedNotification.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1393, 1918}}"; + sepNavSelRange = "{4888, 25}"; + sepNavVisRect = "{{0, 1451}, {1393, 389}}"; + }; + }; + F577917C0395498401EE990F /* WODistributedNotification.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1393, 4396}}"; + sepNavSelRange = "{9188, 25}"; + sepNavVisRect = "{{0, 0}, {1393, 389}}"; + }; + }; + F58C12A903991E3601B0D373 /* English */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 646}}"; + sepNavSelRange = "{164, 3}"; + sepNavVisRect = "{{0, 0}, {940, 646}}"; + }; + }; + F58C12AB03991E3601B0D373 /* English */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1016, 2254}}"; + sepNavSelRange = "{2255, 45}"; + sepNavVisRect = "{{0, 802}, {1016, 398}}"; + }; + }; + F58C12B103991E4E01B0D373 /* Spanish */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 464}}"; + sepNavSelRange = "{117, 3}"; + sepNavVisRect = "{{0, 0}, {940, 464}}"; + }; + }; + F58C12B203991E6001B0D373 /* Spanish */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {741, 424}}"; + sepNavSelRange = "{608, 0}"; + sepNavVisRect = "{{0, 0}, {741, 424}}"; + }; + }; + F594B71D039CC99C01E61CB8 /* WOSingleton.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {972, 1400}}"; + sepNavSelRange = "{2386, 0}"; + sepNavVisRect = "{{0, 752}, {972, 648}}"; + }; + }; + F594B71E039CC99C01E61CB8 /* WOSingleton.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {972, 1554}}"; + sepNavSelRange = "{692, 5}"; + sepNavVisRect = "{{0, 906}, {972, 648}}"; + }; + }; + F5964517039B448A014C8642 /* WOSynergyPreferences.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1393, 5922}}"; + sepNavSelRange = "{9835, 27}"; + sepNavVisRect = "{{0, 4699}, {1393, 389}}"; + }; + }; + F5964518039B448A014C8642 /* WOSynergyPreferences.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 464}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {940, 464}}"; + }; + }; + F5B86F62039931F1010F9052 /* English */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 646}}"; + sepNavSelRange = "{167, 0}"; + sepNavVisRect = "{{0, 0}, {940, 646}}"; + }; + }; + F5B86F64039931F1010F9052 /* English */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 1735}}"; + sepNavSelRange = "{4638, 0}"; + sepNavVisRect = "{{0, 0}, {766, 953}}"; + }; + }; + F5B86F6A0399320C010F9052 /* Spanish */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 646}}"; + sepNavSelRange = "{164, 3}"; + sepNavVisRect = "{{0, 0}, {940, 646}}"; + }; + }; + F5B86F6B03993218010F9052 /* Spanish */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {770, 1582}}"; + sepNavSelRange = "{1821, 0}"; + sepNavVisRect = "{{0, 696}, {770, 526}}"; + }; + }; + F5B86F6D039932B1010F9052 /* build.sh */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {872, 438}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {743, 438}}"; + }; + }; + F5CAFBDB039C8FC1019C532D /* Development notes.txt */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {940, 2604}}"; + sepNavSelRange = "{350, 4}"; + sepNavVisRect = "{{0, 0}, {940, 646}}"; + }; + }; + F5CAFBDE039C9165019C532D /* Icon.r */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1393, 60914}}"; + sepNavSelRange = "{27, 4}"; + sepNavVisRect = "{{0, 4203}, {1393, 389}}"; + }; + }; + F5CB1D5B0394AF0401754549 /* quotes */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {766, 1204}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 290}, {766, 914}}"; + }; + }; + F5E1D2510399F66E01669DE8 /* diskImage.sh */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1112, 4032}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {743, 359}}"; + }; + }; + F5E471CC03AF7D8401FB4C6E /* PBXTextBookmark */ = { + isa = PBXTextBookmark; + fRef = F5626BCA03AF15A80185C75A /* WOSynergyHelp.m */; + name = "WOSynergyHelp.m: __WINT_TYPE__"; + rLen = 0; + rLoc = 217; + rType = 0; + vrLen = 956; + vrLoc = 0; + }; + F5ECD2D103A5F4D7010DC0E7 /* WOHelp.h */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {673, 489}}"; + sepNavSelRange = "{0, 0}"; + sepNavVisRect = "{{0, 0}, {673, 405}}"; + }; + }; + F5ECD2D203A5F4D7010DC0E7 /* WOHelp.m */ = { + uiCtxt = { + sepNavIntBoundsRect = "{{0, 0}, {1016, 4522}}"; + sepNavSelRange = "{2041, 5}"; + sepNavVisRect = "{{0, 522}, {1016, 398}}"; + }; + }; +} diff --git a/Synergy.xcodeproj/project.pbxproj b/Synergy.xcodeproj/project.pbxproj new file mode 100644 index 0000000..a89f254 --- /dev/null +++ b/Synergy.xcodeproj/project.pbxproj @@ -0,0 +1,1962 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXAggregateTarget section */ + BC1567C00D186568001991C5 /* Synergy Prefs + SynergyPref */ = { + isa = PBXAggregateTarget; + buildConfigurationList = BC1567DE0D1865B1001991C5 /* Build configuration list for PBXAggregateTarget "Synergy Prefs + SynergyPref" */; + buildPhases = ( + BC1567BF0D186568001991C5 /* CopyFiles */, + ); + dependencies = ( + BC1567C40D18656E001991C5 /* PBXTargetDependency */, + BC1567C60D186570001991C5 /* PBXTargetDependency */, + ); + name = "Synergy Prefs + SynergyPref"; + productName = "Synergy Prefs + SynergyPref"; + }; + BC881F3E0D1AD08C00D969B3 /* Update build number */ = { + isa = PBXAggregateTarget; + buildConfigurationList = BC881F500D1AD0B200D969B3 /* Build configuration list for PBXAggregateTarget "Update build number" */; + buildPhases = ( + BC881F3D0D1AD08C00D969B3 /* ShellScript */, + ); + dependencies = ( + ); + name = "Update build number"; + productName = "Update build number"; + }; + BC8B9A730D22EC7F00A11CB9 /* Distribution */ = { + isa = PBXAggregateTarget; + buildConfigurationList = BC8B9A860D22ECE600A11CB9 /* Build configuration list for PBXAggregateTarget "Distribution" */; + buildPhases = ( + BC8B9A720D22EC7F00A11CB9 /* ShellScript */, + ); + dependencies = ( + BC8B9A800D22ECC000A11CB9 /* PBXTargetDependency */, + BC8B9A820D22ECC400A11CB9 /* PBXTargetDependency */, + ); + name = Distribution; + productName = Distribution; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + BC024B17104ADB1F001A9488 /* NSMutableString+WOEditingUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = BC024B14104ADB1F001A9488 /* NSMutableString+WOEditingUtilities.m */; }; + BC024B18104ADB1F001A9488 /* NSString+WOCreation.m in Sources */ = {isa = PBXBuildFile; fileRef = BC024B16104ADB1F001A9488 /* NSString+WOCreation.m */; }; + BC0A12B90A3B1A6100B30B7A /* Synergy.app in Copy Files (Synergy.app) */ = {isa = PBXBuildFile; fileRef = BCBE2364093B34BD00FAD628 /* Synergy.app */; }; + BC0B53930982BCD00033B1A7 /* MacOSX_Universal_60px.gif in Resources */ = {isa = PBXBuildFile; fileRef = BC0B53920982BCD00033B1A7 /* MacOSX_Universal_60px.gif */; }; + BC0B9D7D0FF409A7007AE543 /* OrgWincentSynergyPref.m in Sources */ = {isa = PBXBuildFile; fileRef = F56E9BFD0394A55F010CA6FF /* OrgWincentSynergyPref.m */; }; + BC0B9D7E0FF409A7007AE543 /* WOAudioscrobblerController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC3C8C410B00A89D0066E6D7 /* WOAudioscrobblerController.m */; }; + BC0B9D7F0FF409A7007AE543 /* WOButtonCellWithTrackingRect.m in Sources */ = {isa = PBXBuildFile; fileRef = BCC0670C045D1B8400A80001 /* WOButtonCellWithTrackingRect.m */; }; + BC0B9D800FF409A7007AE543 /* WOButtonSet.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5CC3270409B4E2001CCC4A /* WOButtonSet.m */; }; + BC0B9D810FF409A7007AE543 /* WOButtonState.m in Sources */ = {isa = PBXBuildFile; fileRef = BC040E41041F3C180052CE41 /* WOButtonState.m */; }; + BC0B9D820FF409A7007AE543 /* WOButtonWithTrackingRect.m in Sources */ = {isa = PBXBuildFile; fileRef = BCE8D3EA0420BEAA00A80001 /* WOButtonWithTrackingRect.m */; }; + BC0B9D830FF409A7007AE543 /* WOControlButtons.m in Sources */ = {isa = PBXBuildFile; fileRef = BC037D110430328600A80001 /* WOControlButtons.m */; }; + BC0B9D840FF409A7007AE543 /* WODistributedNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = F577917C0395498401EE990F /* WODistributedNotification.m */; }; + BC0B9D860FF409A7007AE543 /* WOKeyCaptureView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC911C1403C31CBC000D6AD2 /* WOKeyCaptureView.m */; }; + BC0B9D870FF409A7007AE543 /* WONSFileManagerExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5D0AC4042FCFFE00A80001 /* WONSFileManagerExtensions.m */; }; + BC0B9D880FF409A7007AE543 /* WONSScreenExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = BC26C14C041C82F500A80001 /* WONSScreenExtensions.m */; }; + BC0B9D890FF409A7007AE543 /* WOPopUpButton.m in Sources */ = {isa = PBXBuildFile; fileRef = BCC066F0045BF68A00A80001 /* WOPopUpButton.m */; }; + BC0B9D8A0FF409A7007AE543 /* WOPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = F527717A039F7A0B013BBD0D /* WOPreferences.m */; }; + BC0B9D8B0FF409A7007AE543 /* WOProcessManager.m in Sources */ = {isa = PBXBuildFile; fileRef = BC7E248103DB8FEE002EDF57 /* WOProcessManager.m */; }; + BC0B9D8C0FF409A7007AE543 /* WORoundedRect.m in Sources */ = {isa = PBXBuildFile; fileRef = BC7B627403D45F7D00B1B2E1 /* WORoundedRect.m */; }; + BC0B9D8D0FF409A7007AE543 /* WOSynergyAnchorController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC56F22403DC5A1800BD6ADA /* WOSynergyAnchorController.m */; }; + BC0B9D8E0FF409A7007AE543 /* WOSynergyAnchorView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC56F21D03DC508200BD6ADA /* WOSynergyAnchorView.m */; }; + BC0B9D8F0FF409A7007AE543 /* WOSynergyAnchorWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = BC56F21903DC506600BD6ADA /* WOSynergyAnchorWindow.m */; }; + BC0B9D900FF409A7007AE543 /* WOSynergyFloaterController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC7B628003D4613B00B1B2E1 /* WOSynergyFloaterController.m */; }; + BC0B9D910FF409A7007AE543 /* WOSynergyFloaterView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC7B627803D4602200B1B2E1 /* WOSynergyFloaterView.m */; }; + BC0B9D920FF409A7007AE543 /* WOSynergyFloaterWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = BC7B627C03D460B300B1B2E1 /* WOSynergyFloaterWindow.m */; }; + BC0B9D930FF409A7007AE543 /* WOSynergyView.m in Sources */ = {isa = PBXBuildFile; fileRef = F56702DD03A4BC7201842AA0 /* WOSynergyView.m */; }; + BC0B9DD00FF40AC4007AE543 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = BC6A2A5B0D14A1210080E56B /* main.m */; }; + BC0B9DD10FF40AC4007AE543 /* NSDictionary+WOCreation.m in Sources */ = {isa = PBXBuildFile; fileRef = BC0B9DBF0FF40A80007AE543 /* NSDictionary+WOCreation.m */; }; + BC0B9DD20FF40AC4007AE543 /* NSMutableString+WOEditingUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = BC0B9DA60FF40A1F007AE543 /* NSMutableString+WOEditingUtilities.m */; }; + BC0B9DD30FF40AC4007AE543 /* NSString+WOCreation.m in Sources */ = {isa = PBXBuildFile; fileRef = BC0B9DA80FF40A1F007AE543 /* NSString+WOCreation.m */; }; + BC0B9DD40FF40AC4007AE543 /* NSString+WOFileUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = BC0B9DAA0FF40A1F007AE543 /* NSString+WOFileUtilities.m */; }; + BC0B9DD50FF40AC4007AE543 /* WOLoginItem.m in Sources */ = {isa = PBXBuildFile; fileRef = BC0B9DB00FF40A3E007AE543 /* WOLoginItem.m */; }; + BC0B9DD60FF40AC4007AE543 /* WOLoginItemList.m in Sources */ = {isa = PBXBuildFile; fileRef = BC0B9DB20FF40A3E007AE543 /* WOLoginItemList.m */; }; + BC0B9DD70FF40AC4007AE543 /* WOLogManager.m in Sources */ = {isa = PBXBuildFile; fileRef = BC0B9DAE0FF40A3E007AE543 /* WOLogManager.m */; }; + BC0B9DD80FF40AC4007AE543 /* WOObject.m in Sources */ = {isa = PBXBuildFile; fileRef = BC0B9DB40FF40A3E007AE543 /* WOObject.m */; }; + BC0B9DD90FF40AC4007AE543 /* WOPreferencePane.m in Sources */ = {isa = PBXBuildFile; fileRef = BC1567AB0D185F5C001991C5 /* WOPreferencePane.m */; }; + BC0B9DDA0FF40AC4007AE543 /* WOPreferenceWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = BC9885CE0D19226800B00823 /* WOPreferenceWindow.m */; }; + BC0B9DDB0FF40AC4007AE543 /* WOSynergyPreferencesController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC9885F90D1927FF00B00823 /* WOSynergyPreferencesController.m */; }; + BC0B9E270FF40C56007AE543 /* HotkeyCapableApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = F54314790394BAF401AC36F3 /* HotkeyCapableApplication.m */; }; + BC0B9E280FF40C56007AE543 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F543148F0394BB0E01AC36F3 /* main.m */; }; + BC0B9E290FF40C56007AE543 /* NSAppleScript+WOAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = BC98E7CC0A39F0EF0048ADFF /* NSAppleScript+WOAdditions.m */; }; + BC0B9E2A0FF40C56007AE543 /* NSImage+WOAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = BC98E7570A39D8C60048ADFF /* NSImage+WOAdditions.m */; }; + BC0B9E2B0FF40C56007AE543 /* NSString+WOExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = BC17717B044386A900A80001 /* NSString+WOExtensions.m */; }; + BC0B9E2C0FF40C56007AE543 /* NSTimer+WOPausable.m in Sources */ = {isa = PBXBuildFile; fileRef = BC3C95FD0B0108A00066E6D7 /* NSTimer+WOPausable.m */; }; + BC0B9E2D0FF40C56007AE543 /* SynergyController+WOAudioscrobbler.m in Sources */ = {isa = PBXBuildFile; fileRef = BC3C8B000AFF9FC50066E6D7 /* SynergyController+WOAudioscrobbler.m */; }; + BC0B9E2E0FF40C56007AE543 /* SynergyController.m in Sources */ = {isa = PBXBuildFile; fileRef = F543147B0394BAF401AC36F3 /* SynergyController.m */; }; + BC0B9E2F0FF40C56007AE543 /* WOAudioscrobbler.m in Sources */ = {isa = PBXBuildFile; fileRef = BC617E000AF7CC9D00E1268F /* WOAudioscrobbler.m */; }; + BC0B9E300FF40C56007AE543 /* WOAudioscrobblerController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC3C8C410B00A89D0066E6D7 /* WOAudioscrobblerController.m */; }; + BC0B9E310FF40C56007AE543 /* WOButtonCellWithTrackingRect.m in Sources */ = {isa = PBXBuildFile; fileRef = BCC0670C045D1B8400A80001 /* WOButtonCellWithTrackingRect.m */; }; + BC0B9E320FF40C56007AE543 /* WOButtonSet.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5CC3270409B4E2001CCC4A /* WOButtonSet.m */; }; + BC0B9E330FF40C56007AE543 /* WOButtonState.m in Sources */ = {isa = PBXBuildFile; fileRef = BC040E41041F3C180052CE41 /* WOButtonState.m */; }; + BC0B9E340FF40C56007AE543 /* WOButtonWithTrackingRect.m in Sources */ = {isa = PBXBuildFile; fileRef = BCE8D3EA0420BEAA00A80001 /* WOButtonWithTrackingRect.m */; }; + BC0B9E350FF40C56007AE543 /* WOCarbonWrappers.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5BAB4F03C1B27100799024 /* WOCarbonWrappers.m */; }; + BC0B9E360FF40C56007AE543 /* WOControlButtons.m in Sources */ = {isa = PBXBuildFile; fileRef = BC037D110430328600A80001 /* WOControlButtons.m */; }; + BC0B9E370FF40C56007AE543 /* WOCoverDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = BC54286A040520370052F570 /* WOCoverDownloader.m */; }; + BC0B9E380FF40C56007AE543 /* WODistributedNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = F577917C0395498401EE990F /* WODistributedNotification.m */; }; + BC0B9E390FF40C56007AE543 /* WOFeedbackController.m in Sources */ = {isa = PBXBuildFile; fileRef = BCDC33E503E279CB00A59DD4 /* WOFeedbackController.m */; }; + BC0B9E3A0FF40C56007AE543 /* WOFeedbackView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCDC33E103E2799000A59DD4 /* WOFeedbackView.m */; }; + BC0B9E3B0FF40C56007AE543 /* WOFeedbackWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = BCDC33E903E279D900A59DD4 /* WOFeedbackWindow.m */; }; + BC0B9E3D0FF40C56007AE543 /* WONSFileManagerExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5D0AC4042FCFFE00A80001 /* WONSFileManagerExtensions.m */; }; + BC0B9E3E0FF40C56007AE543 /* WONSScreenExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = BC26C14C041C82F500A80001 /* WONSScreenExtensions.m */; }; + BC0B9E3F0FF40C56007AE543 /* WONSStringExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = BCE4636003CAB6BF00CB9B3F /* WONSStringExtensions.m */; }; + BC0B9E400FF40C56007AE543 /* WOPopUpButton.m in Sources */ = {isa = PBXBuildFile; fileRef = BCC066F0045BF68A00A80001 /* WOPopUpButton.m */; }; + BC0B9E410FF40C56007AE543 /* WOPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = F527717A039F7A0B013BBD0D /* WOPreferences.m */; }; + BC0B9E420FF40C56007AE543 /* WOProcessManager.m in Sources */ = {isa = PBXBuildFile; fileRef = BC7E248103DB8FEE002EDF57 /* WOProcessManager.m */; }; + BC0B9E430FF40C56007AE543 /* WORoundedRect.m in Sources */ = {isa = PBXBuildFile; fileRef = BC7B627403D45F7D00B1B2E1 /* WORoundedRect.m */; }; + BC0B9E440FF40C56007AE543 /* WOSongInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = BC27E64B042DEF5300A80001 /* WOSongInfo.m */; }; + BC0B9E450FF40C56007AE543 /* WOSynergyFloaterController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC7B628003D4613B00B1B2E1 /* WOSynergyFloaterController.m */; }; + BC0B9E460FF40C56007AE543 /* WOSynergyFloaterView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC7B627803D4602200B1B2E1 /* WOSynergyFloaterView.m */; }; + BC0B9E470FF40C56007AE543 /* WOSynergyFloaterWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = BC7B627C03D460B300B1B2E1 /* WOSynergyFloaterWindow.m */; }; + BC0B9E480FF40C56007AE543 /* WOSynergyView.m in Sources */ = {isa = PBXBuildFile; fileRef = F56702DD03A4BC7201842AA0 /* WOSynergyView.m */; }; + BC0B9E490FF40C56007AE543 /* WOSysctl.c in Sources */ = {isa = PBXBuildFile; fileRef = BC08FB9E0404F1E00099965D /* WOSysctl.c */; }; + BC3C8CB80B00B8B80066E6D7 /* audioscrobbler.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC3C8CB20B00B8B80066E6D7 /* audioscrobbler.xib */; }; + BC3C93470B00DCBF0066E6D7 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC3C93120B00DCBE0066E6D7 /* Security.framework */; }; + BC3C93480B00DCBF0066E6D7 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC3C93120B00DCBE0066E6D7 /* Security.framework */; }; + BC55A13B103AA1FF00B5AB83 /* NSDictionary+WOCreation.m in Sources */ = {isa = PBXBuildFile; fileRef = BC55A13A103AA1FF00B5AB83 /* NSDictionary+WOCreation.m */; }; + BC55A1A6103ABA9000B5AB83 /* NSDictionary+WOCreation.m in Sources */ = {isa = PBXBuildFile; fileRef = BC55A1A5103ABA9000B5AB83 /* NSDictionary+WOCreation.m */; }; + BC6A2A5A0D14A1000080E56B /* SynergyPreferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC6A2A580D14A1000080E56B /* SynergyPreferences.xib */; }; + BC6A2A650D14A18C0080E56B /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5CB1D7B0394B24501754549 /* Cocoa.framework */; }; + BC8814840D193B7200D969B3 /* Synergy.prefPane in CopyFiles */ = {isa = PBXBuildFile; fileRef = BCFE77750B0211FE005B3442 /* Synergy.prefPane */; }; + BC8814EB0D19F5EB00D969B3 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC3C93120B00DCBE0066E6D7 /* Security.framework */; }; + BC8815B20D1AAA9900D969B3 /* Synergy.icns in Resources */ = {isa = PBXBuildFile; fileRef = BC8815B10D1AAA9900D969B3 /* Synergy.icns */; }; + BC8815D00D1AC7E100D969B3 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = BC8815CE0D1AC7E100D969B3 /* InfoPlist.strings */; }; + BC98EAC30A3A01060048ADFF /* QuickTime.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC98EA9F0A3A01060048ADFF /* QuickTime.framework */; }; + BCA18E830D33C991002092F0 /* ScriptingBridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCA18E820D33C991002092F0 /* ScriptingBridge.framework */; }; + BCBE22AF093B34BD00FAD628 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F58C12A803991E3601B0D373 /* InfoPlist.strings */; }; + BCBE22B0093B34BD00FAD628 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F58C12AA03991E3601B0D373 /* Localizable.strings */; }; + BCBE22B1093B34BD00FAD628 /* OrgWincentSynergyPref.xib in Resources */ = {isa = PBXBuildFile; fileRef = F58C12AC03991E3601B0D373 /* OrgWincentSynergyPref.xib */; }; + BCBE22B2093B34BD00FAD628 /* defaults.plist in Resources */ = {isa = PBXBuildFile; fileRef = F57E7B5303AD61D401A8AA3E /* defaults.plist */; }; + BCBE22B3093B34BD00FAD628 /* synergyFloater.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC7B628303D466F400B1B2E1 /* synergyFloater.xib */; }; + BCBE22B4093B34BD00FAD628 /* pin.png in Resources */ = {isa = PBXBuildFile; fileRef = BCDC340703E41D2800A59DD4 /* pin.png */; }; + BCBE22B5093B34BD00FAD628 /* SynergyPref.png in Resources */ = {isa = PBXBuildFile; fileRef = BCDC340903E41D2800A59DD4 /* SynergyPref.png */; }; + BCBE22B6093B34BD00FAD628 /* pin.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCDC340D03E41DBA00A59DD4 /* pin.xib */; }; + BCBE22B7093B34BD00FAD628 /* SynergyLarge.png in Resources */ = {isa = PBXBuildFile; fileRef = BCDC340803E41D2800A59DD4 /* SynergyLarge.png */; }; + BCBE22B8093B34BD00FAD628 /* Unicodes.plist in Resources */ = {isa = PBXBuildFile; fileRef = BC689891046AC30F00A80001 /* Unicodes.plist */; }; + BCBE22BA093B34BD00FAD628 /* about.rtf in Resources */ = {isa = PBXBuildFile; fileRef = BCDF68270753B6A100EF20AF /* about.rtf */; }; + BCBE22BB093B34BD00FAD628 /* NoCoverArt.png in Resources */ = {isa = PBXBuildFile; fileRef = BCAC73AF07E5FE9300FDA956 /* NoCoverArt.png */; }; + BCBE2314093B34BD00FAD628 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F5B86F61039931F1010F9052 /* InfoPlist.strings */; }; + BCBE2315093B34BD00FAD628 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F5B86F63039931F1010F9052 /* Localizable.strings */; }; + BCBE2316093B34BD00FAD628 /* synergyMain.xib in Resources */ = {isa = PBXBuildFile; fileRef = F5B86F65039931F1010F9052 /* synergyMain.xib */; }; + BCBE2317093B34BD00FAD628 /* defaults.plist in Resources */ = {isa = PBXBuildFile; fileRef = F57E7B5303AD61D401A8AA3E /* defaults.plist */; }; + BCBE2318093B34BD00FAD628 /* synergyFloater.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC7B628303D466F400B1B2E1 /* synergyFloater.xib */; }; + BCBE2319093B34BD00FAD628 /* feedback.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCDC33EC03E2BA3C00A59DD4 /* feedback.xib */; }; + BCBE231A093B34BD00FAD628 /* volume.png in Resources */ = {isa = PBXBuildFile; fileRef = BCDC33F003E2FFFA00A59DD4 /* volume.png */; }; + BCBE231B093B34BD00FAD628 /* SynergyLarge.png in Resources */ = {isa = PBXBuildFile; fileRef = BCDC33F703E419E800A59DD4 /* SynergyLarge.png */; }; + BCBE231C093B34BD00FAD628 /* nextFeedback.png in Resources */ = {isa = PBXBuildFile; fileRef = BCE15E9C03E421D5005E6BE2 /* nextFeedback.png */; }; + BCBE231D093B34BD00FAD628 /* pauseFeedback.png in Resources */ = {isa = PBXBuildFile; fileRef = BCE15E9D03E421D5005E6BE2 /* pauseFeedback.png */; }; + BCBE231E093B34BD00FAD628 /* playFeedback.png in Resources */ = {isa = PBXBuildFile; fileRef = BCE15E9E03E421D5005E6BE2 /* playFeedback.png */; }; + BCBE231F093B34BD00FAD628 /* prevFeedback.png in Resources */ = {isa = PBXBuildFile; fileRef = BCE15E9F03E421D5005E6BE2 /* prevFeedback.png */; }; + BCBE2320093B34BD00FAD628 /* playPauseFeedback.png in Resources */ = {isa = PBXBuildFile; fileRef = BCE15EA403E42BE9005E6BE2 /* playPauseFeedback.png */; }; + BCBE2321093B34BD00FAD628 /* bars.png in Resources */ = {isa = PBXBuildFile; fileRef = BCE15EA603E43204005E6BE2 /* bars.png */; }; + BCBE2322093B34BD00FAD628 /* shuffleOff.png in Resources */ = {isa = PBXBuildFile; fileRef = BC927305040BA1860099CFAC /* shuffleOff.png */; }; + BCBE2323093B34BD00FAD628 /* shuffleOn.png in Resources */ = {isa = PBXBuildFile; fileRef = BC927306040BA1860099CFAC /* shuffleOn.png */; }; + BCBE2324093B34BD00FAD628 /* repeatAll.png in Resources */ = {isa = PBXBuildFile; fileRef = BC927353040CE2330099CFAC /* repeatAll.png */; }; + BCBE2325093B34BD00FAD628 /* repeatOff.png in Resources */ = {isa = PBXBuildFile; fileRef = BC927354040CE2330099CFAC /* repeatOff.png */; }; + BCBE2326093B34BD00FAD628 /* repeatOne.png in Resources */ = {isa = PBXBuildFile; fileRef = BC927355040CE2330099CFAC /* repeatOne.png */; }; + BCBE2327093B34BD00FAD628 /* Synergy.icns in Resources */ = {isa = PBXBuildFile; fileRef = F54314930394BB6601AC36F3 /* Synergy.icns */; }; + BCBE2328093B34BD00FAD628 /* musicGlyph.png in Resources */ = {isa = PBXBuildFile; fileRef = BCC06705045CD73700A80001 /* musicGlyph.png */; }; + BCBE2329093B34BD00FAD628 /* musicGlyphSelected.png in Resources */ = {isa = PBXBuildFile; fileRef = BCBB9316046FC5EB00A80001 /* musicGlyphSelected.png */; }; + BCBE232A093B34BD00FAD628 /* Button Sets in Resources */ = {isa = PBXBuildFile; fileRef = BC3BD42406DE6FF5004768FD /* Button Sets */; }; + BCBE232B093B34BD00FAD628 /* SynergyButtons.icns in Resources */ = {isa = PBXBuildFile; fileRef = BC3BD60306DE9F4E004768FD /* SynergyButtons.icns */; }; + BCBE232C093B34BD00FAD628 /* NoCoverArt.png in Resources */ = {isa = PBXBuildFile; fileRef = BCAC739107E5FDF800FDA956 /* NoCoverArt.png */; }; + BCBE232E093B34BD00FAD628 /* Growl.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = BC680D640834C48F00ABF3B8 /* Growl.framework */; }; + BCBE2354093B34BD00FAD628 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5CB1D7A0394B24501754549 /* Carbon.framework */; }; + BCBE2355093B34BD00FAD628 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5CB1D7B0394B24501754549 /* Cocoa.framework */; }; + BCBE2357093B34BD00FAD628 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCBE04380426C12B00A80001 /* SystemConfiguration.framework */; }; + BCBE2358093B34BD00FAD628 /* Growl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BC680D640834C48F00ABF3B8 /* Growl.framework */; }; + BCCC011A0BB4377300A36444 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5CB1D7B0394B24501754549 /* Cocoa.framework */; }; + BCCC01200BB437EF00A36444 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F5CB1D7A0394B24501754549 /* Carbon.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXBuildRule section */ + BCBE22E8093B34BD00FAD628 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; + BCBE2363093B34BD00FAD628 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc.4_0; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; +/* End PBXBuildRule section */ + +/* Begin PBXContainerItemProxy section */ + BC1567BD0D18653E001991C5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 089C1669FE841209C02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = BC6A2A3D0D149F500080E56B; + remoteInfo = "Synergy Preferences"; + }; + BC1567C30D18656E001991C5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 089C1669FE841209C02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = BC6A2A3D0D149F500080E56B; + remoteInfo = "Synergy Preferences"; + }; + BC1567C50D186570001991C5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 089C1669FE841209C02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = BCBE228A093B34BD00FAD628; + remoteInfo = SynergyPref; + }; + BC8B9A7F0D22ECC000A11CB9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 089C1669FE841209C02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = BC881F3E0D1AD08C00D969B3; + remoteInfo = "Update build number"; + }; + BC8B9A810D22ECC400A11CB9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 089C1669FE841209C02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = BC1567C00D186568001991C5; + remoteInfo = "Synergy Prefs + SynergyPref"; + }; + BCBE23A0093B34BD00FAD628 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 089C1669FE841209C02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = BCBE22EA093B34BD00FAD628; + remoteInfo = "SynergyApp (Upgraded)"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + BC1567BF0D186568001991C5 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "Synergy Preferences.app/Contents/PreferencePanes"; + dstSubfolderSpec = 16; + files = ( + BC8814840D193B7200D969B3 /* Synergy.prefPane in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BCBE22DE093B34BD00FAD628 /* Copy Files (Synergy.app) */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = Contents/Helpers; + dstSubfolderSpec = 1; + files = ( + BC0A12B90A3B1A6100B30B7A /* Synergy.app in Copy Files (Synergy.app) */, + ); + name = "Copy Files (Synergy.app)"; + runOnlyForDeploymentPostprocessing = 0; + }; + BCBE232D093B34BD00FAD628 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + BCBE232E093B34BD00FAD628 /* Growl.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 089C1672FE841209C02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; + BC024B13104ADB1F001A9488 /* NSMutableString+WOEditingUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSMutableString+WOEditingUtilities.h"; path = "WOPublic/NSMutableString+WOEditingUtilities.h"; sourceTree = ""; }; + BC024B14104ADB1F001A9488 /* NSMutableString+WOEditingUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSMutableString+WOEditingUtilities.m"; path = "WOPublic/NSMutableString+WOEditingUtilities.m"; sourceTree = ""; }; + BC024B15104ADB1F001A9488 /* NSString+WOCreation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+WOCreation.h"; path = "WOPublic/NSString+WOCreation.h"; sourceTree = ""; }; + BC024B16104ADB1F001A9488 /* NSString+WOCreation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+WOCreation.m"; path = "WOPublic/NSString+WOCreation.m"; sourceTree = ""; }; + BC037D100430328600A80001 /* WOControlButtons.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOControlButtons.h; path = SynergyCommon/Classes/WOControlButtons.h; sourceTree = ""; }; + BC037D110430328600A80001 /* WOControlButtons.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOControlButtons.m; path = SynergyCommon/Classes/WOControlButtons.m; sourceTree = ""; }; + BC040E40041F3C180052CE41 /* WOButtonState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOButtonState.h; path = SynergyApp/Classes/WOButtonState.h; sourceTree = ""; }; + BC040E41041F3C180052CE41 /* WOButtonState.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOButtonState.m; path = SynergyApp/Classes/WOButtonState.m; sourceTree = ""; }; + BC08FB9D0404F1E00099965D /* WOSysctl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOSysctl.h; path = "SynergyApp/Other Sources/WOSysctl.h"; sourceTree = ""; }; + BC08FB9E0404F1E00099965D /* WOSysctl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = WOSysctl.c; path = "SynergyApp/Other Sources/WOSysctl.c"; sourceTree = ""; }; + BC0AE4E30406480B00248FD8 /* playPauseImage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = playPauseImage.png; path = SynergyApp/Resources/playPauseImage.png; sourceTree = ""; }; + BC0B53920982BCD00033B1A7 /* MacOSX_Universal_60px.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; name = MacOSX_Universal_60px.gif; path = SynergyPref/Resources/MacOSX_Universal_60px.gif; sourceTree = ""; }; + BC0B61BA03C9D2580035855F /* WOCommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOCommon.h; path = SynergyCommon/Classes/WOCommon.h; sourceTree = ""; }; + BC0B9CF20FF401C6007AE543 /* release-style.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "release-style.xcconfig"; path = "buildtools/release-style.xcconfig"; sourceTree = ""; }; + BC0B9CF30FF401C6007AE543 /* debug-style.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "debug-style.xcconfig"; path = "buildtools/debug-style.xcconfig"; sourceTree = ""; }; + BC0B9CF40FF401C6007AE543 /* pref-pane-target.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "pref-pane-target.xcconfig"; path = "buildtools/pref-pane-target.xcconfig"; sourceTree = ""; }; + BC0B9CF50FF401C6007AE543 /* cocoa-app-target.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "cocoa-app-target.xcconfig"; path = "buildtools/cocoa-app-target.xcconfig"; sourceTree = ""; }; + BC0B9CF60FF401C6007AE543 /* base-style.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = "base-style.xcconfig"; path = "buildtools/base-style.xcconfig"; sourceTree = ""; }; + BC0B9D560FF40845007AE543 /* WOPublic */ = {isa = PBXFileReference; lastKnownFileType = folder; path = WOPublic; sourceTree = ""; }; + BC0B9D640FF408A5007AE543 /* WOConvenienceMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOConvenienceMacros.h; path = WOPublic/WOConvenienceMacros.h; sourceTree = ""; }; + BC0B9DA50FF40A1F007AE543 /* NSMutableString+WOEditingUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSMutableString+WOEditingUtilities.h"; path = "WOPublic/NSMutableString+WOEditingUtilities.h"; sourceTree = ""; }; + BC0B9DA60FF40A1F007AE543 /* NSMutableString+WOEditingUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSMutableString+WOEditingUtilities.m"; path = "WOPublic/NSMutableString+WOEditingUtilities.m"; sourceTree = ""; }; + BC0B9DA70FF40A1F007AE543 /* NSString+WOCreation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+WOCreation.h"; path = "WOPublic/NSString+WOCreation.h"; sourceTree = ""; }; + BC0B9DA80FF40A1F007AE543 /* NSString+WOCreation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+WOCreation.m"; path = "WOPublic/NSString+WOCreation.m"; sourceTree = ""; }; + BC0B9DA90FF40A1F007AE543 /* NSString+WOFileUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+WOFileUtilities.h"; path = "WOPublic/NSString+WOFileUtilities.h"; sourceTree = ""; }; + BC0B9DAA0FF40A1F007AE543 /* NSString+WOFileUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+WOFileUtilities.m"; path = "WOPublic/NSString+WOFileUtilities.m"; sourceTree = ""; }; + BC0B9DAD0FF40A3E007AE543 /* WOLogManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOLogManager.h; path = WOPublic/WOLogManager.h; sourceTree = ""; }; + BC0B9DAE0FF40A3E007AE543 /* WOLogManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOLogManager.m; path = WOPublic/WOLogManager.m; sourceTree = ""; }; + BC0B9DAF0FF40A3E007AE543 /* WOLoginItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOLoginItem.h; path = WOPublic/WOLoginItem.h; sourceTree = ""; }; + BC0B9DB00FF40A3E007AE543 /* WOLoginItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOLoginItem.m; path = WOPublic/WOLoginItem.m; sourceTree = ""; }; + BC0B9DB10FF40A3E007AE543 /* WOLoginItemList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOLoginItemList.h; path = WOPublic/WOLoginItemList.h; sourceTree = ""; }; + BC0B9DB20FF40A3E007AE543 /* WOLoginItemList.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOLoginItemList.m; path = WOPublic/WOLoginItemList.m; sourceTree = ""; }; + BC0B9DB30FF40A3E007AE543 /* WOObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOObject.h; path = WOPublic/WOObject.h; sourceTree = ""; }; + BC0B9DB40FF40A3E007AE543 /* WOObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOObject.m; path = WOPublic/WOObject.m; sourceTree = ""; }; + BC0B9DBE0FF40A80007AE543 /* NSDictionary+WOCreation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+WOCreation.h"; path = "WOPublic/NSDictionary+WOCreation.h"; sourceTree = ""; }; + BC0B9DBF0FF40A80007AE543 /* NSDictionary+WOCreation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+WOCreation.m"; path = "WOPublic/NSDictionary+WOCreation.m"; sourceTree = ""; }; + BC0B9DC00FF40A8A007AE543 /* WOConvenienceMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOConvenienceMacros.h; path = WOPublic/WOConvenienceMacros.h; sourceTree = ""; }; + BC0B9DC10FF40A8A007AE543 /* WODebugMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WODebugMacros.h; path = WOPublic/WODebugMacros.h; sourceTree = ""; }; + BC0B9DC20FF40A8A007AE543 /* WOMemory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOMemory.h; path = WOPublic/WOMemory.h; sourceTree = ""; }; + BC0B9DC30FF40A8A007AE543 /* WOMemoryBarrier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOMemoryBarrier.h; path = WOPublic/WOMemoryBarrier.h; sourceTree = ""; }; + BC1567AA0D185F5C001991C5 /* WOPreferencePane.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOPreferencePane.h; path = SynergyPreferences/WOPreferencePane.h; sourceTree = ""; }; + BC1567AB0D185F5C001991C5 /* WOPreferencePane.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOPreferencePane.m; path = SynergyPreferences/WOPreferencePane.m; sourceTree = ""; }; + BC17717A044386A900A80001 /* NSString+WOExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSString+WOExtensions.h"; path = "SynergyCommon/Classes/NSString+WOExtensions.h"; sourceTree = ""; }; + BC17717B044386A900A80001 /* NSString+WOExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSString+WOExtensions.m"; path = "SynergyCommon/Classes/NSString+WOExtensions.m"; sourceTree = ""; }; + BC26C14B041C82F500A80001 /* WONSScreenExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WONSScreenExtensions.h; path = SynergyCommon/Classes/WONSScreenExtensions.h; sourceTree = ""; }; + BC26C14C041C82F500A80001 /* WONSScreenExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WONSScreenExtensions.m; path = SynergyCommon/Classes/WONSScreenExtensions.m; sourceTree = ""; }; + BC27E64A042DEF5300A80001 /* WOSongInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOSongInfo.h; path = SynergyApp/Classes/WOSongInfo.h; sourceTree = ""; }; + BC27E64B042DEF5300A80001 /* WOSongInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOSongInfo.m; path = SynergyApp/Classes/WOSongInfo.m; sourceTree = ""; }; + BC2A3E7A10F205270051E3BF /* WOConvenienceMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOConvenienceMacros.h; path = WOPublic/WOConvenienceMacros.h; sourceTree = ""; }; + BC2B328804574EF800A80001 /* French */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = French; path = SynergyApp/Resources/French.lproj/Localizable.strings; sourceTree = ""; }; + BC2B328A0457FF1F00A80001 /* French */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = French; path = SynergyPref/Resources/French.lproj/Localizable.strings; sourceTree = ""; }; + BC2C26AA0482824B00A80001 /* Dutch */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = Dutch; path = SynergyPref/Resources/Dutch.lproj/Unicodes.plist; sourceTree = ""; }; + BC2C26AB0482825B00A80001 /* Dutch */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = Dutch; path = SynergyPref/Resources/Dutch.lproj/Localizable.strings; sourceTree = ""; }; + BC2D22EB072E9D6D007E3CBD /* Norwegian */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = Norwegian; path = SynergyPref/Resources/Norwegian.lproj/InfoPlist.strings; sourceTree = ""; }; + BC30616403FA8FDD007C4E56 /* WOSynergyGlobal.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = WOSynergyGlobal.h; path = SynergyCommon/Classes/WOSynergyGlobal.h; sourceTree = ""; }; + BC3BD42406DE6FF5004768FD /* Button Sets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "Button Sets"; path = "SynergyApp/Resources/Button Sets"; sourceTree = ""; }; + BC3BD60306DE9F4E004768FD /* SynergyButtons.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = SynergyButtons.icns; path = SynergyApp/Resources/SynergyButtons.icns; sourceTree = ""; }; + BC3C3168080C9E8D00DF9C20 /* zh_CN */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = zh_CN; path = SynergyApp/Resources/zh_CN.lproj/InfoPlist.strings; sourceTree = ""; }; + BC3C3169080C9E8D00DF9C20 /* zh_CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh_CN; path = SynergyApp/Resources/zh_CN.lproj/Localizable.strings; sourceTree = ""; }; + BC3C3172080C9ECD00DF9C20 /* zh_CN */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = zh_CN; path = SynergyPref/Resources/zh_CN.lproj/about.rtf; sourceTree = ""; }; + BC3C3173080C9ECD00DF9C20 /* zh_CN */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = zh_CN; path = SynergyPref/Resources/zh_CN.lproj/InfoPlist.strings; sourceTree = ""; }; + BC3C3174080C9ECD00DF9C20 /* zh_CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh_CN; path = SynergyPref/Resources/zh_CN.lproj/Localizable.strings; sourceTree = ""; }; + BC3C3177080C9ED500DF9C20 /* zh_CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = zh_CN; path = SynergyPref/Resources/zh_CN.lproj/Unicodes.plist; sourceTree = ""; }; + BC3C8AFF0AFF9FC50066E6D7 /* SynergyController+WOAudioscrobbler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "SynergyController+WOAudioscrobbler.h"; path = "SynergyApp/Classes/SynergyController+WOAudioscrobbler.h"; sourceTree = ""; }; + BC3C8B000AFF9FC50066E6D7 /* SynergyController+WOAudioscrobbler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "SynergyController+WOAudioscrobbler.m"; path = "SynergyApp/Classes/SynergyController+WOAudioscrobbler.m"; sourceTree = ""; }; + BC3C8C400B00A89D0066E6D7 /* WOAudioscrobblerController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOAudioscrobblerController.h; path = SynergyPref/Classes/WOAudioscrobblerController.h; sourceTree = ""; }; + BC3C8C410B00A89D0066E6D7 /* WOAudioscrobblerController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOAudioscrobblerController.m; path = SynergyPref/Classes/WOAudioscrobblerController.m; sourceTree = ""; }; + BC3C93120B00DCBE0066E6D7 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /System/Library/Frameworks/Security.framework; sourceTree = ""; }; + BC3C95FC0B0108A00066E6D7 /* NSTimer+WOPausable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSTimer+WOPausable.h"; path = "SynergyApp/Categories/NSTimer+WOPausable.h"; sourceTree = ""; }; + BC3C95FD0B0108A00066E6D7 /* NSTimer+WOPausable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSTimer+WOPausable.m"; path = "SynergyApp/Categories/NSTimer+WOPausable.m"; sourceTree = ""; }; + BC5346D70D200ED600D8D287 /* German */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = German; path = SynergyApp/Resources/German.lproj/InfoPlist.strings; sourceTree = ""; }; + BC5346D80D200ED600D8D287 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = SynergyApp/Resources/pt.lproj/InfoPlist.strings; sourceTree = ""; }; + BC5346D90D200ED600D8D287 /* Norwegian */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Norwegian; path = SynergyApp/Resources/Norwegian.lproj/InfoPlist.strings; sourceTree = ""; }; + BC5346DA0D200ED600D8D287 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = SynergyApp/Resources/tr.lproj/InfoPlist.strings; sourceTree = ""; }; + BC5346DB0D200ED600D8D287 /* French */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = French; path = SynergyApp/Resources/French.lproj/InfoPlist.strings; sourceTree = ""; }; + BC5346DC0D200ED600D8D287 /* Japanese */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Japanese; path = SynergyApp/Resources/Japanese.lproj/InfoPlist.strings; sourceTree = ""; }; + BC5346E00D200F2F00D8D287 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = SynergyPref/Resources/pt.lproj/InfoPlist.strings; sourceTree = ""; }; + BC5346E10D200F2F00D8D287 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = SynergyPref/Resources/tr.lproj/InfoPlist.strings; sourceTree = ""; }; + BC5346F70D200F7100D8D287 /* Swedish */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Swedish; path = SynergyApp/Resources/Swedish.lproj/InfoPlist.strings; sourceTree = ""; }; + BC542869040520370052F570 /* WOCoverDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOCoverDownloader.h; path = SynergyApp/Classes/WOCoverDownloader.h; sourceTree = ""; }; + BC54286A040520370052F570 /* WOCoverDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOCoverDownloader.m; path = SynergyApp/Classes/WOCoverDownloader.m; sourceTree = ""; }; + BC55A138103AA1C300B5AB83 /* WOConvenienceMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOConvenienceMacros.h; path = WOPublic/WOConvenienceMacros.h; sourceTree = ""; }; + BC55A139103AA1FF00B5AB83 /* NSDictionary+WOCreation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+WOCreation.h"; path = "WOPublic/NSDictionary+WOCreation.h"; sourceTree = ""; }; + BC55A13A103AA1FF00B5AB83 /* NSDictionary+WOCreation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+WOCreation.m"; path = "WOPublic/NSDictionary+WOCreation.m"; sourceTree = ""; }; + BC55A1A4103ABA9000B5AB83 /* NSDictionary+WOCreation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+WOCreation.h"; path = "WOPublic/NSDictionary+WOCreation.h"; sourceTree = ""; }; + BC55A1A5103ABA9000B5AB83 /* NSDictionary+WOCreation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+WOCreation.m"; path = "WOPublic/NSDictionary+WOCreation.m"; sourceTree = ""; }; + BC56F21803DC506600BD6ADA /* WOSynergyAnchorWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOSynergyAnchorWindow.h; path = SynergyPref/Classes/WOSynergyAnchorWindow.h; sourceTree = ""; }; + BC56F21903DC506600BD6ADA /* WOSynergyAnchorWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOSynergyAnchorWindow.m; path = SynergyPref/Classes/WOSynergyAnchorWindow.m; sourceTree = ""; }; + BC56F21C03DC508200BD6ADA /* WOSynergyAnchorView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOSynergyAnchorView.h; path = SynergyPref/Classes/WOSynergyAnchorView.h; sourceTree = ""; }; + BC56F21D03DC508200BD6ADA /* WOSynergyAnchorView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOSynergyAnchorView.m; path = SynergyPref/Classes/WOSynergyAnchorView.m; sourceTree = ""; }; + BC56F22303DC5A1800BD6ADA /* WOSynergyAnchorController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOSynergyAnchorController.h; path = SynergyPref/Classes/WOSynergyAnchorController.h; sourceTree = ""; }; + BC56F22403DC5A1800BD6ADA /* WOSynergyAnchorController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOSynergyAnchorController.m; path = SynergyPref/Classes/WOSynergyAnchorController.m; sourceTree = ""; }; + BC588013046F9F6F00A80001 /* German */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = German; path = SynergyPref/Resources/German.lproj/InfoPlist.strings; sourceTree = ""; }; + BC588014046F9F8B00A80001 /* German */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = German; path = SynergyPref/Resources/German.lproj/Localizable.strings; sourceTree = ""; }; + BC588016046F9F9C00A80001 /* German */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = German; path = SynergyPref/Resources/German.lproj/Unicodes.plist; sourceTree = ""; }; + BC588017046FA03100A80001 /* German */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = German; path = SynergyApp/Resources/German.lproj/Localizable.strings; sourceTree = ""; }; + BC5BAB4E03C1B27100799024 /* WOCarbonWrappers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOCarbonWrappers.h; path = SynergyCommon/Classes/WOCarbonWrappers.h; sourceTree = ""; }; + BC5BAB4F03C1B27100799024 /* WOCarbonWrappers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOCarbonWrappers.m; path = SynergyCommon/Classes/WOCarbonWrappers.m; sourceTree = ""; }; + BC5CC3260409B4E2001CCC4A /* WOButtonSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOButtonSet.h; path = SynergyCommon/Classes/WOButtonSet.h; sourceTree = ""; }; + BC5CC3270409B4E2001CCC4A /* WOButtonSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOButtonSet.m; path = SynergyCommon/Classes/WOButtonSet.m; sourceTree = ""; }; + BC5D0AC3042FCFFE00A80001 /* WONSFileManagerExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WONSFileManagerExtensions.h; path = SynergyApp/Classes/WONSFileManagerExtensions.h; sourceTree = ""; }; + BC5D0AC4042FCFFE00A80001 /* WONSFileManagerExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WONSFileManagerExtensions.m; path = SynergyApp/Classes/WONSFileManagerExtensions.m; sourceTree = ""; }; + BC5D0AC7042FDFFF00A80001 /* WOExceptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOExceptions.h; path = SynergyCommon/Classes/WOExceptions.h; sourceTree = ""; }; + BC5D0AD8042FE8A300A80001 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = ""; }; + BC617DFF0AF7CC9D00E1268F /* WOAudioscrobbler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOAudioscrobbler.h; path = SynergyApp/Classes/WOAudioscrobbler.h; sourceTree = ""; }; + BC617E000AF7CC9D00E1268F /* WOAudioscrobbler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOAudioscrobbler.m; path = SynergyApp/Classes/WOAudioscrobbler.m; sourceTree = ""; }; + BC680D640834C48F00ABF3B8 /* Growl.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Growl.framework; path = SynergyApp/Growl.framework; sourceTree = ""; }; + BC68988904691BF000A80001 /* Japanese */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = Japanese; path = SynergyPref/Resources/Japanese.lproj/Localizable.strings; sourceTree = ""; }; + BC68988B04691C0700A80001 /* Japanese */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = Japanese; path = SynergyApp/Resources/Japanese.lproj/Localizable.strings; sourceTree = ""; }; + BC689895046AC32100A80001 /* French */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = French; path = SynergyPref/Resources/French.lproj/Unicodes.plist; sourceTree = ""; }; + BC689896046AC32E00A80001 /* Spanish */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = Spanish; path = SynergyPref/Resources/Spanish.lproj/Unicodes.plist; sourceTree = ""; }; + BC689898046BE0FC00A80001 /* Japanese */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = Japanese; path = SynergyPref/Resources/Japanese.lproj/InfoPlist.strings; sourceTree = ""; }; + BC6A2A3E0D149F500080E56B /* Synergy Preferences.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Synergy Preferences.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + BC6A2A400D149F500080E56B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = SynergyPreferences/Info.plist; sourceTree = ""; }; + BC6A2A590D14A1000080E56B /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = SynergyPreferences/en.lproj/SynergyPreferences.xib; sourceTree = ""; }; + BC6A2A5B0D14A1210080E56B /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = SynergyPreferences/main.m; sourceTree = ""; }; + BC7566750D1EFF49005AD9F2 /* zh_TW */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = zh_TW; path = SynergyPref/Resources/zh_TW.lproj/audioscrobbler.xib; sourceTree = ""; }; + BC7566760D1EFF49005AD9F2 /* zh_CN */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = zh_CN; path = SynergyPref/Resources/zh_CN.lproj/audioscrobbler.xib; sourceTree = ""; }; + BC7566770D1EFF49005AD9F2 /* tr */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = tr; path = SynergyPref/Resources/tr.lproj/audioscrobbler.xib; sourceTree = ""; }; + BC7566780D1EFF49005AD9F2 /* Swedish */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Swedish; path = SynergyPref/Resources/Swedish.lproj/audioscrobbler.xib; sourceTree = ""; }; + BC7566790D1EFF49005AD9F2 /* Spanish */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Spanish; path = SynergyPref/Resources/Spanish.lproj/audioscrobbler.xib; sourceTree = ""; }; + BC75667A0D1EFF49005AD9F2 /* ru */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ru; path = SynergyPref/Resources/ru.lproj/audioscrobbler.xib; sourceTree = ""; }; + BC75667B0D1EFF49005AD9F2 /* pt */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = pt; path = SynergyPref/Resources/pt.lproj/audioscrobbler.xib; sourceTree = ""; }; + BC75667C0D1EFF49005AD9F2 /* Norwegian */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Norwegian; path = SynergyPref/Resources/Norwegian.lproj/audioscrobbler.xib; sourceTree = ""; }; + BC75667D0D1EFF49005AD9F2 /* Japanese */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Japanese; path = SynergyPref/Resources/Japanese.lproj/audioscrobbler.xib; sourceTree = ""; }; + BC75667E0D1EFF49005AD9F2 /* it */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = it; path = SynergyPref/Resources/it.lproj/audioscrobbler.xib; sourceTree = ""; }; + BC75667F0D1EFF49005AD9F2 /* German */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = German; path = SynergyPref/Resources/German.lproj/audioscrobbler.xib; sourceTree = ""; }; + BC7566800D1EFF49005AD9F2 /* French */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = French; path = SynergyPref/Resources/French.lproj/audioscrobbler.xib; sourceTree = ""; }; + BC7566810D1EFF49005AD9F2 /* Dutch */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Dutch; path = SynergyPref/Resources/Dutch.lproj/audioscrobbler.xib; sourceTree = ""; }; + BC78E3ED103738FE0087DA74 /* tag-release.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "tag-release.sh"; sourceTree = ""; }; + BC7B627303D45F7D00B1B2E1 /* WORoundedRect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WORoundedRect.h; path = SynergyCommon/Classes/WORoundedRect.h; sourceTree = ""; }; + BC7B627403D45F7D00B1B2E1 /* WORoundedRect.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WORoundedRect.m; path = SynergyCommon/Classes/WORoundedRect.m; sourceTree = ""; }; + BC7B627703D4602200B1B2E1 /* WOSynergyFloaterView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOSynergyFloaterView.h; path = SynergyApp/Classes/WOSynergyFloaterView.h; sourceTree = ""; }; + BC7B627803D4602200B1B2E1 /* WOSynergyFloaterView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOSynergyFloaterView.m; path = SynergyApp/Classes/WOSynergyFloaterView.m; sourceTree = ""; }; + BC7B627B03D460B300B1B2E1 /* WOSynergyFloaterWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOSynergyFloaterWindow.h; path = SynergyApp/Classes/WOSynergyFloaterWindow.h; sourceTree = ""; }; + BC7B627C03D460B300B1B2E1 /* WOSynergyFloaterWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOSynergyFloaterWindow.m; path = SynergyApp/Classes/WOSynergyFloaterWindow.m; sourceTree = ""; }; + BC7B627F03D4613B00B1B2E1 /* WOSynergyFloaterController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOSynergyFloaterController.h; path = SynergyApp/Classes/WOSynergyFloaterController.h; sourceTree = ""; }; + BC7B628003D4613B00B1B2E1 /* WOSynergyFloaterController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOSynergyFloaterController.m; path = SynergyApp/Classes/WOSynergyFloaterController.m; sourceTree = ""; }; + BC7B628303D466F400B1B2E1 /* synergyFloater.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = synergyFloater.xib; path = SynergyApp/Resources/synergyFloater.xib; sourceTree = ""; }; + BC7E248003DB8FEE002EDF57 /* WOProcessManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOProcessManager.h; path = SynergyApp/Classes/WOProcessManager.h; sourceTree = ""; }; + BC7E248103DB8FEE002EDF57 /* WOProcessManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOProcessManager.m; path = SynergyApp/Classes/WOProcessManager.m; sourceTree = ""; }; + BC8815B10D1AAA9900D969B3 /* Synergy.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = Synergy.icns; path = SynergyPreferences/Synergy.icns; sourceTree = ""; }; + BC8815CF0D1AC7E100D969B3 /* en */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = en; path = SynergyPreferences/en.lproj/InfoPlist.strings; sourceTree = ""; }; + BC88161D0D1ACA5D00D969B3 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = SynergyPreferences/es.lproj/InfoPlist.strings; sourceTree = ""; }; + BC88175D0D1ACB2700D969B3 /* WOSynergy_Version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WOSynergy_Version.h; sourceTree = ""; }; + BC881F850D1AFE9300D969B3 /* en */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = en; path = SynergyApp/Resources/en.lproj/Localizable.strings; sourceTree = ""; }; + BC881F860D1AFE9300D969B3 /* en */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = en; path = SynergyApp/Resources/en.lproj/InfoPlist.strings; sourceTree = ""; }; + BC881F870D1AFE9300D969B3 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = SynergyApp/Resources/en.lproj/synergyMain.xib; sourceTree = ""; }; + BC881F910D1AFEF100D969B3 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = SynergyPref/Resources/en.lproj/OrgWincentSynergyPref.xib; sourceTree = ""; }; + BC881F920D1AFEF100D969B3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = SynergyPref/Resources/en.lproj/about.rtf; sourceTree = ""; }; + BC881F930D1AFEF100D969B3 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = SynergyPref/Resources/en.lproj/audioscrobbler.xib; sourceTree = ""; }; + BC881F940D1AFEF100D969B3 /* en */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; name = en; path = SynergyPref/Resources/en.lproj/Unicodes.plist; sourceTree = ""; }; + BC881F950D1AFEF100D969B3 /* en */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = en; path = SynergyPref/Resources/en.lproj/Localizable.strings; sourceTree = ""; }; + BC881F960D1AFEF100D969B3 /* en */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = en; path = SynergyPref/Resources/en.lproj/InfoPlist.strings; sourceTree = ""; }; + BC8B9BB00D23111400A11CB9 /* distro.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = distro.sh; sourceTree = ""; }; + BC8BE0B4127DBC9400B6B534 /* sign.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = sign.sh; sourceTree = ""; }; + BC911C1303C31CBC000D6AD2 /* WOKeyCaptureView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOKeyCaptureView.h; path = SynergyPref/Classes/WOKeyCaptureView.h; sourceTree = ""; }; + BC911C1403C31CBC000D6AD2 /* WOKeyCaptureView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOKeyCaptureView.m; path = SynergyPref/Classes/WOKeyCaptureView.m; sourceTree = ""; }; + BC927305040BA1860099CFAC /* shuffleOff.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = shuffleOff.png; path = SynergyApp/Resources/shuffleOff.png; sourceTree = ""; }; + BC927306040BA1860099CFAC /* shuffleOn.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = shuffleOn.png; path = SynergyApp/Resources/shuffleOn.png; sourceTree = ""; }; + BC927353040CE2330099CFAC /* repeatAll.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = repeatAll.png; path = SynergyApp/Resources/repeatAll.png; sourceTree = ""; }; + BC927354040CE2330099CFAC /* repeatOff.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = repeatOff.png; path = SynergyApp/Resources/repeatOff.png; sourceTree = ""; }; + BC927355040CE2330099CFAC /* repeatOne.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = repeatOne.png; path = SynergyApp/Resources/repeatOne.png; sourceTree = ""; }; + BC9885CD0D19226800B00823 /* WOPreferenceWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOPreferenceWindow.h; path = SynergyPreferences/WOPreferenceWindow.h; sourceTree = ""; }; + BC9885CE0D19226800B00823 /* WOPreferenceWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOPreferenceWindow.m; path = SynergyPreferences/WOPreferenceWindow.m; sourceTree = ""; }; + BC9885F80D1927FF00B00823 /* WOSynergyPreferencesController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOSynergyPreferencesController.h; path = SynergyPreferences/WOSynergyPreferencesController.h; sourceTree = ""; }; + BC9885F90D1927FF00B00823 /* WOSynergyPreferencesController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOSynergyPreferencesController.m; path = SynergyPreferences/WOSynergyPreferencesController.m; sourceTree = ""; }; + BC98E7560A39D8C60048ADFF /* NSImage+WOAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSImage+WOAdditions.h"; path = "SynergyApp/Categories/NSImage+WOAdditions.h"; sourceTree = ""; }; + BC98E7570A39D8C60048ADFF /* NSImage+WOAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSImage+WOAdditions.m"; path = "SynergyApp/Categories/NSImage+WOAdditions.m"; sourceTree = ""; }; + BC98E7CB0A39F0EF0048ADFF /* NSAppleScript+WOAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSAppleScript+WOAdditions.h"; path = "SynergyApp/Categories/NSAppleScript+WOAdditions.h"; sourceTree = ""; }; + BC98E7CC0A39F0EF0048ADFF /* NSAppleScript+WOAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSAppleScript+WOAdditions.m"; path = "SynergyApp/Categories/NSAppleScript+WOAdditions.m"; sourceTree = ""; }; + BC98EA9F0A3A01060048ADFF /* QuickTime.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickTime.framework; path = /System/Library/Frameworks/QuickTime.framework; sourceTree = ""; }; + BCA18E820D33C991002092F0 /* ScriptingBridge.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ScriptingBridge.framework; path = /System/Library/Frameworks/ScriptingBridge.framework; sourceTree = ""; }; + BCA18E860D33C9B1002092F0 /* iTunes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iTunes.h; sourceTree = ""; }; + BCA7F4A3048379BC00A80001 /* Swedish */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = Swedish; path = SynergyPref/Resources/Swedish.lproj/InfoPlist.strings; sourceTree = ""; }; + BCA7F4A404837A0C00A80001 /* Dutch */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = Dutch; path = SynergyPref/Resources/Dutch.lproj/InfoPlist.strings; sourceTree = ""; }; + BCA7F4A504837A4100A80001 /* Swedish */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = Swedish; path = SynergyPref/Resources/Swedish.lproj/Localizable.strings; sourceTree = ""; }; + BCA7F4A704837A9300A80001 /* Swedish */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = Swedish; path = SynergyPref/Resources/Swedish.lproj/Unicodes.plist; sourceTree = ""; }; + BCA7F4A804837B7300A80001 /* Swedish */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = Swedish; path = SynergyApp/Resources/Swedish.lproj/Localizable.strings; sourceTree = ""; }; + BCAC739107E5FDF800FDA956 /* NoCoverArt.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = NoCoverArt.png; path = SynergyApp/Resources/NoCoverArt.png; sourceTree = ""; }; + BCAC73AF07E5FE9300FDA956 /* NoCoverArt.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = NoCoverArt.png; path = SynergyPref/Resources/NoCoverArt.png; sourceTree = ""; }; + BCB0BD8A0682231300FFC46C /* Norwegian */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Norwegian; path = SynergyPref/Resources/Norwegian.lproj/Localizable.strings; sourceTree = ""; }; + BCB5C0E107D7DF4A00D08CA9 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = tr; path = SynergyPref/Resources/tr.lproj/about.rtf; sourceTree = ""; }; + BCB5C0F107D7DF5900D08CA9 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = SynergyPref/Resources/tr.lproj/Localizable.strings; sourceTree = ""; }; + BCB5C0F807D7DF7B00D08CA9 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = SynergyApp/Resources/tr.lproj/Localizable.strings; sourceTree = ""; }; + BCBB9316046FC5EB00A80001 /* musicGlyphSelected.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = musicGlyphSelected.png; path = SynergyApp/Resources/musicGlyphSelected.png; sourceTree = ""; }; + BCBB9A360470726C00A80001 /* Dutch */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = Dutch; path = SynergyApp/Resources/Dutch.lproj/Localizable.strings; sourceTree = ""; }; + BCBB9A370470727600A80001 /* Dutch */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = Dutch; path = SynergyApp/Resources/Dutch.lproj/InfoPlist.strings; sourceTree = ""; }; + BCBE04380426C12B00A80001 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = /System/Library/Frameworks/SystemConfiguration.framework; sourceTree = ""; }; + BCBE22E5093B34BD00FAD628 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = SynergyPref/Info.plist; sourceTree = ""; }; + BCBE2360093B34BD00FAD628 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = SynergyApp/Info.plist; sourceTree = ""; }; + BCBE2364093B34BD00FAD628 /* Synergy.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Synergy.app; sourceTree = BUILT_PRODUCTS_DIR; }; + BCC066EF045BF68A00A80001 /* WOPopUpButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOPopUpButton.h; path = SynergyApp/Classes/WOPopUpButton.h; sourceTree = ""; }; + BCC066F0045BF68A00A80001 /* WOPopUpButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOPopUpButton.m; path = SynergyApp/Classes/WOPopUpButton.m; sourceTree = ""; }; + BCC06705045CD73700A80001 /* musicGlyph.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = musicGlyph.png; path = SynergyApp/Resources/musicGlyph.png; sourceTree = ""; }; + BCC06707045CF19700A80001 /* French */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = French; path = SynergyPref/Resources/French.lproj/InfoPlist.strings; sourceTree = ""; }; + BCC06709045D179D00A80001 /* WOButtonCellWithTrackingRect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOButtonCellWithTrackingRect.h; path = SynergyApp/Classes/WOButtonCellWithTrackingRect.h; sourceTree = ""; }; + BCC0670C045D1B8400A80001 /* WOButtonCellWithTrackingRect.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = WOButtonCellWithTrackingRect.m; path = SynergyApp/Classes/WOButtonCellWithTrackingRect.m; sourceTree = ""; }; + BCC2DD090445BC6B00A80001 /* WOSynergyFloater.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = WOSynergyFloater.h; path = SynergyApp/Classes/WOSynergyFloater.h; sourceTree = ""; }; + BCD1967F0D3258D400D324C3 /* Norwegian */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Norwegian; path = SynergyApp/Resources/Norwegian.lproj/Localizable.strings; sourceTree = ""; }; + BCD86C680766964800FAA179 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = SynergyApp/Resources/pt.lproj/Localizable.strings; sourceTree = ""; }; + BCD86C87076696EA00FAA179 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = SynergyPref/Resources/pt.lproj/Localizable.strings; sourceTree = ""; }; + BCD86CA507669B3600FAA179 /* zh_TW */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = zh_TW; path = SynergyApp/Resources/zh_TW.lproj/InfoPlist.strings; sourceTree = ""; }; + BCD86CA607669B3600FAA179 /* zh_TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh_TW; path = SynergyApp/Resources/zh_TW.lproj/Localizable.strings; sourceTree = ""; }; + BCD8710B07669C4A00FAA179 /* zh_TW */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = zh_TW; path = SynergyPref/Resources/zh_TW.lproj/InfoPlist.strings; sourceTree = ""; }; + BCD8710C07669C4A00FAA179 /* zh_TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh_TW; path = SynergyPref/Resources/zh_TW.lproj/Localizable.strings; sourceTree = ""; }; + BCD8710E07669C4A00FAA179 /* zh_TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = zh_TW; path = SynergyPref/Resources/zh_TW.lproj/Unicodes.plist; sourceTree = ""; }; + BCD8711207669C8900FAA179 /* zh_TW */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = zh_TW; path = SynergyPref/Resources/zh_TW.lproj/about.rtf; sourceTree = ""; }; + BCD8712707669D4300FAA179 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = it; path = SynergyPref/Resources/it.lproj/about.rtf; sourceTree = ""; }; + BCD8712807669D4300FAA179 /* it */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = it; path = SynergyPref/Resources/it.lproj/InfoPlist.strings; sourceTree = ""; }; + BCD8712907669D4300FAA179 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = SynergyPref/Resources/it.lproj/Localizable.strings; sourceTree = ""; }; + BCD8712B07669D4300FAA179 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = it; path = SynergyPref/Resources/it.lproj/Unicodes.plist; sourceTree = ""; }; + BCD8714007669DC400FAA179 /* it */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = it; path = SynergyApp/Resources/it.lproj/InfoPlist.strings; sourceTree = ""; }; + BCD8714107669DC400FAA179 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = SynergyApp/Resources/it.lproj/Localizable.strings; sourceTree = ""; }; + BCDC33E003E2799000A59DD4 /* WOFeedbackView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOFeedbackView.h; path = SynergyApp/Classes/WOFeedbackView.h; sourceTree = ""; }; + BCDC33E103E2799000A59DD4 /* WOFeedbackView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOFeedbackView.m; path = SynergyApp/Classes/WOFeedbackView.m; sourceTree = ""; }; + BCDC33E403E279CB00A59DD4 /* WOFeedbackController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOFeedbackController.h; path = SynergyApp/Classes/WOFeedbackController.h; sourceTree = ""; }; + BCDC33E503E279CB00A59DD4 /* WOFeedbackController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOFeedbackController.m; path = SynergyApp/Classes/WOFeedbackController.m; sourceTree = ""; }; + BCDC33E803E279D900A59DD4 /* WOFeedbackWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOFeedbackWindow.h; path = SynergyApp/Classes/WOFeedbackWindow.h; sourceTree = ""; }; + BCDC33E903E279D900A59DD4 /* WOFeedbackWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOFeedbackWindow.m; path = SynergyApp/Classes/WOFeedbackWindow.m; sourceTree = ""; }; + BCDC33EC03E2BA3C00A59DD4 /* feedback.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = feedback.xib; path = SynergyApp/Resources/feedback.xib; sourceTree = ""; }; + BCDC33F003E2FFFA00A59DD4 /* volume.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = volume.png; path = SynergyApp/Resources/volume.png; sourceTree = ""; }; + BCDC33F203E419E800A59DD4 /* nextImage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = nextImage.png; path = SynergyApp/Resources/nextImage.png; sourceTree = ""; }; + BCDC33F303E419E800A59DD4 /* pauseImage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = pauseImage.png; path = SynergyApp/Resources/pauseImage.png; sourceTree = ""; }; + BCDC33F403E419E800A59DD4 /* playImage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = playImage.png; path = SynergyApp/Resources/playImage.png; sourceTree = ""; }; + BCDC33F503E419E800A59DD4 /* prevImage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = prevImage.png; path = SynergyApp/Resources/prevImage.png; sourceTree = ""; }; + BCDC33F603E419E800A59DD4 /* stopImage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = stopImage.png; path = SynergyApp/Resources/stopImage.png; sourceTree = ""; }; + BCDC33F703E419E800A59DD4 /* SynergyLarge.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = SynergyLarge.png; path = SynergyApp/Resources/SynergyLarge.png; sourceTree = ""; }; + BCDC340103E41B8B00A59DD4 /* nextImage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = nextImage.png; path = SynergyPref/Resources/nextImage.png; sourceTree = ""; }; + BCDC340203E41B8B00A59DD4 /* playImage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = playImage.png; path = SynergyPref/Resources/playImage.png; sourceTree = ""; }; + BCDC340303E41B8B00A59DD4 /* prevImage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = prevImage.png; path = SynergyPref/Resources/prevImage.png; sourceTree = ""; }; + BCDC340703E41D2800A59DD4 /* pin.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = pin.png; path = SynergyPref/Resources/pin.png; sourceTree = ""; }; + BCDC340803E41D2800A59DD4 /* SynergyLarge.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = SynergyLarge.png; path = SynergyPref/Resources/SynergyLarge.png; sourceTree = ""; }; + BCDC340903E41D2800A59DD4 /* SynergyPref.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = SynergyPref.png; path = SynergyPref/Resources/SynergyPref.png; sourceTree = ""; }; + BCDC340D03E41DBA00A59DD4 /* pin.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = pin.xib; path = SynergyPref/Resources/pin.xib; sourceTree = ""; }; + BCDF68280753B6A100EF20AF /* Japanese */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = Japanese; path = SynergyPref/Resources/Japanese.lproj/about.rtf; sourceTree = ""; }; + BCDF68290753B6A100EF20AF /* French */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = French; path = SynergyPref/Resources/French.lproj/about.rtf; sourceTree = ""; }; + BCDF682A0753B6A100EF20AF /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = ru; path = SynergyPref/Resources/ru.lproj/about.rtf; sourceTree = ""; }; + BCDF682B0753B6A100EF20AF /* Swedish */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = Swedish; path = SynergyPref/Resources/Swedish.lproj/about.rtf; sourceTree = ""; }; + BCDF682C0753B6A100EF20AF /* Norwegian */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = Norwegian; path = SynergyPref/Resources/Norwegian.lproj/about.rtf; sourceTree = ""; }; + BCDF682E0753B6A100EF20AF /* Dutch */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = Dutch; path = SynergyPref/Resources/Dutch.lproj/about.rtf; sourceTree = ""; }; + BCDF682F0753B6A100EF20AF /* German */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = German; path = SynergyPref/Resources/German.lproj/about.rtf; sourceTree = ""; }; + BCDF68300753B6A100EF20AF /* Spanish */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = Spanish; path = SynergyPref/Resources/Spanish.lproj/about.rtf; sourceTree = ""; }; + BCE03A5A061ACC1A00238E7E /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = ru; path = SynergyPref/Resources/ru.lproj/Unicodes.plist; sourceTree = ""; }; + BCE15E9C03E421D5005E6BE2 /* nextFeedback.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = nextFeedback.png; path = SynergyApp/Resources/nextFeedback.png; sourceTree = ""; }; + BCE15E9D03E421D5005E6BE2 /* pauseFeedback.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = pauseFeedback.png; path = SynergyApp/Resources/pauseFeedback.png; sourceTree = ""; }; + BCE15E9E03E421D5005E6BE2 /* playFeedback.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = playFeedback.png; path = SynergyApp/Resources/playFeedback.png; sourceTree = ""; }; + BCE15E9F03E421D5005E6BE2 /* prevFeedback.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = prevFeedback.png; path = SynergyApp/Resources/prevFeedback.png; sourceTree = ""; }; + BCE15EA403E42BE9005E6BE2 /* playPauseFeedback.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = playPauseFeedback.png; path = SynergyApp/Resources/playPauseFeedback.png; sourceTree = ""; }; + BCE15EA603E43204005E6BE2 /* bars.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = bars.png; path = SynergyApp/Resources/bars.png; sourceTree = ""; }; + BCE15EA803E459B6005E6BE2 /* WOFeedbackDefaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOFeedbackDefaults.h; path = SynergyApp/Classes/WOFeedbackDefaults.h; sourceTree = ""; }; + BCE15EB203E558A1005E6BE2 /* compileAppleScript.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = compileAppleScript.sh; path = SynergyApp/Scripts/compileAppleScript.sh; sourceTree = ""; }; + BCE4635F03CAB6BF00CB9B3F /* WONSStringExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WONSStringExtensions.h; path = SynergyCommon/Classes/WONSStringExtensions.h; sourceTree = ""; }; + BCE4636003CAB6BF00CB9B3F /* WONSStringExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WONSStringExtensions.m; path = SynergyCommon/Classes/WONSStringExtensions.m; sourceTree = ""; }; + BCE8D3E90420BEAA00A80001 /* WOButtonWithTrackingRect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WOButtonWithTrackingRect.h; path = SynergyApp/Classes/WOButtonWithTrackingRect.h; sourceTree = ""; }; + BCE8D3EA0420BEAA00A80001 /* WOButtonWithTrackingRect.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = WOButtonWithTrackingRect.m; path = SynergyApp/Classes/WOButtonWithTrackingRect.m; sourceTree = ""; }; + BCF3AFAF060B3C9E00B2B13C /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = SynergyPref/Resources/ru.lproj/Localizable.strings; sourceTree = ""; }; + BCF3AFB0060B3CAF00B2B13C /* ru */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = ru; path = SynergyPref/Resources/ru.lproj/InfoPlist.strings; sourceTree = ""; }; + BCF3AFB1060B3D2A00B2B13C /* ru */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = ru; path = SynergyApp/Resources/ru.lproj/InfoPlist.strings; sourceTree = ""; }; + BCF3AFB2060B3D4700B2B13C /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = SynergyApp/Resources/ru.lproj/Localizable.strings; sourceTree = ""; }; + BCFE77750B0211FE005B3442 /* Synergy.prefPane */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Synergy.prefPane; sourceTree = BUILT_PRODUCTS_DIR; }; + F506C035013D953901CA16C8 /* PreferencePanes.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PreferencePanes.framework; path = /System/Library/Frameworks/PreferencePanes.framework; sourceTree = ""; }; + F5277179039F7A0B013BBD0D /* WOPreferences.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = WOPreferences.h; path = SynergyCommon/Classes/WOPreferences.h; sourceTree = ""; }; + F527717A039F7A0B013BBD0D /* WOPreferences.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = WOPreferences.m; path = SynergyCommon/Classes/WOPreferences.m; sourceTree = ""; }; + F54314780394BAF401AC36F3 /* HotkeyCapableApplication.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = HotkeyCapableApplication.h; path = SynergyApp/Classes/HotkeyCapableApplication.h; sourceTree = ""; }; + F54314790394BAF401AC36F3 /* HotkeyCapableApplication.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = HotkeyCapableApplication.m; path = SynergyApp/Classes/HotkeyCapableApplication.m; sourceTree = ""; }; + F543147A0394BAF401AC36F3 /* SynergyController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = SynergyController.h; path = SynergyApp/Classes/SynergyController.h; sourceTree = ""; }; + F543147B0394BAF401AC36F3 /* SynergyController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = SynergyController.m; path = SynergyApp/Classes/SynergyController.m; sourceTree = ""; }; + F543148F0394BB0E01AC36F3 /* main.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = main.m; path = "SynergyApp/Other Sources/main.m"; sourceTree = ""; }; + F54314910394BB2101AC36F3 /* getSongInfo.applescript */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.applescript; name = getSongInfo.applescript; path = SynergyApp/Scripts/getSongInfo.applescript; sourceTree = ""; }; + F54314930394BB6601AC36F3 /* Synergy.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = Synergy.icns; path = SynergyApp/Resources/Synergy.icns; sourceTree = ""; }; + F54FD5E70393F70D010D9943 /* RELEASE PROCEDURE */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = "RELEASE PROCEDURE"; sourceTree = ""; }; + F55DFBBE03A84D64010663F9 /* WODebug.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = WODebug.h; path = SynergyCommon/Classes/WODebug.h; sourceTree = ""; }; + F56702DC03A4BC7201842AA0 /* WOSynergyView.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = WOSynergyView.h; path = SynergyCommon/Classes/WOSynergyView.h; sourceTree = ""; }; + F56702DD03A4BC7201842AA0 /* WOSynergyView.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = WOSynergyView.m; path = SynergyCommon/Classes/WOSynergyView.m; sourceTree = ""; }; + F56E9BFC0394A55F010CA6FF /* OrgWincentSynergyPref.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = OrgWincentSynergyPref.h; path = SynergyPref/Classes/OrgWincentSynergyPref.h; sourceTree = ""; }; + F56E9BFD0394A55F010CA6FF /* OrgWincentSynergyPref.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = OrgWincentSynergyPref.m; path = SynergyPref/Classes/OrgWincentSynergyPref.m; sourceTree = ""; }; + F577917B0395498401EE990F /* WODistributedNotification.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = WODistributedNotification.h; path = SynergyCommon/Classes/WODistributedNotification.h; sourceTree = ""; }; + F577917C0395498401EE990F /* WODistributedNotification.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = WODistributedNotification.m; path = SynergyCommon/Classes/WODistributedNotification.m; sourceTree = ""; }; + F57E7B5303AD61D401A8AA3E /* defaults.plist */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.plist.xml; name = defaults.plist; path = SynergyCommon/Resources/defaults.plist; sourceTree = ""; }; + F58C12B103991E4E01B0D373 /* Spanish */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = Spanish; path = SynergyPref/Resources/Spanish.lproj/InfoPlist.strings; sourceTree = ""; }; + F58C12B203991E6001B0D373 /* Spanish */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = Spanish; path = SynergyPref/Resources/Spanish.lproj/Localizable.strings; sourceTree = ""; }; + F5B86F6A0399320C010F9052 /* Spanish */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = Spanish; path = SynergyApp/Resources/Spanish.lproj/InfoPlist.strings; sourceTree = ""; }; + F5B86F6B03993218010F9052 /* Spanish */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = Spanish; path = SynergyApp/Resources/Spanish.lproj/Localizable.strings; sourceTree = ""; }; + F5B86F6D039932B1010F9052 /* build.sh */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.script.sh; name = build.sh; path = SynergyApp/Scripts/build.sh; sourceTree = ""; }; + F5CAFBDB039C8FC1019C532D /* Development notes.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = "Development notes.txt"; sourceTree = ""; }; + F5CAFBDE039C9165019C532D /* Icon.r */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.rez; name = Icon.r; path = SynergyPref/Resources/Icon.r; sourceTree = ""; }; + F5CB1D790394B24501754549 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; + F5CB1D7A0394B24501754549 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; + F5CB1D7B0394B24501754549 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; + F5CB1D7C0394B24501754549 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + BC6A2A3C0D149F500080E56B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BC8814EB0D19F5EB00D969B3 /* Security.framework in Frameworks */, + BC6A2A650D14A18C0080E56B /* Cocoa.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BCBE22D9093B34BD00FAD628 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BCCC01200BB437EF00A36444 /* Carbon.framework in Frameworks */, + BCCC011A0BB4377300A36444 /* Cocoa.framework in Frameworks */, + BC3C93470B00DCBF0066E6D7 /* Security.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BCBE2352093B34BD00FAD628 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BCBE2354093B34BD00FAD628 /* Carbon.framework in Frameworks */, + BCBE2355093B34BD00FAD628 /* Cocoa.framework in Frameworks */, + BCBE2357093B34BD00FAD628 /* SystemConfiguration.framework in Frameworks */, + BCBE2358093B34BD00FAD628 /* Growl.framework in Frameworks */, + BC98EAC30A3A01060048ADFF /* QuickTime.framework in Frameworks */, + BC3C93480B00DCBF0066E6D7 /* Security.framework in Frameworks */, + BCA18E830D33C991002092F0 /* ScriptingBridge.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 089C166AFE841209C02AAC07 /* Synergy */ = { + isa = PBXGroup; + children = ( + BC5F9B140CFC338F00A5957F /* Configuration */, + BC6738260CFA0ACE0097E36B /* Scripts */, + BC0B9D560FF40845007AE543 /* WOPublic */, + BC88175D0D1ACB2700D969B3 /* WOSynergy_Version.h */, + F54FD5E70393F70D010D9943 /* RELEASE PROCEDURE */, + F5CAFBDB039C8FC1019C532D /* Development notes.txt */, + F5964509039B4115014C8642 /* SynergyCommon */, + F5CB1D6B0394AF0D01754549 /* SynergyApp */, + F5CB219F0394B36D01754549 /* SynergyPref */, + BC6A2A490D149FA00080E56B /* Synergy Preferences */, + F5FF36EC0399FF2A01EE0299 /* SynergyDiskImage */, + F56944CE03AE3D7301A2C68D /* SynergyDocs */, + BC8817350D1ACAE500D969B3 /* Frameworks */, + 19C28FB8FE9D52D311CA2CBB /* Products */, + ); + name = Synergy; + sourceTree = ""; + }; + 089C1671FE841209C02AAC07 /* Frameworks and Libraries */ = { + isa = PBXGroup; + children = ( + F506C035013D953901CA16C8 /* PreferencePanes.framework */, + 089C1672FE841209C02AAC07 /* Foundation.framework */, + 089C167FFE841241C02AAC07 /* AppKit.framework */, + ); + name = "Frameworks and Libraries"; + sourceTree = ""; + }; + 089C167CFE841241C02AAC07 /* Resources */ = { + isa = PBXGroup; + children = ( + BC3C8CB20B00B8B80066E6D7 /* audioscrobbler.xib */, + BCBE22E5093B34BD00FAD628 /* Info.plist */, + F58C12A803991E3601B0D373 /* InfoPlist.strings */, + F58C12AA03991E3601B0D373 /* Localizable.strings */, + F58C12AC03991E3601B0D373 /* OrgWincentSynergyPref.xib */, + BCDC340D03E41DBA00A59DD4 /* pin.xib */, + BCDF68270753B6A100EF20AF /* about.rtf */, + BC689891046AC30F00A80001 /* Unicodes.plist */, + F5CAFBDE039C9165019C532D /* Icon.r */, + BCDC340703E41D2800A59DD4 /* pin.png */, + BCDC340803E41D2800A59DD4 /* SynergyLarge.png */, + BCAC73AF07E5FE9300FDA956 /* NoCoverArt.png */, + BCDC340903E41D2800A59DD4 /* SynergyPref.png */, + BCDC340103E41B8B00A59DD4 /* nextImage.png */, + BCDC340203E41B8B00A59DD4 /* playImage.png */, + BCDC340303E41B8B00A59DD4 /* prevImage.png */, + BC0B53920982BCD00033B1A7 /* MacOSX_Universal_60px.gif */, + ); + name = Resources; + sourceTree = ""; + }; + 08FB77AFFE84173DC02AAC07 /* Classes */ = { + isa = PBXGroup; + children = ( + F56E9BFC0394A55F010CA6FF /* OrgWincentSynergyPref.h */, + F56E9BFD0394A55F010CA6FF /* OrgWincentSynergyPref.m */, + BC911C1303C31CBC000D6AD2 /* WOKeyCaptureView.h */, + BC911C1403C31CBC000D6AD2 /* WOKeyCaptureView.m */, + BC56F21803DC506600BD6ADA /* WOSynergyAnchorWindow.h */, + BC56F21903DC506600BD6ADA /* WOSynergyAnchorWindow.m */, + BC56F21C03DC508200BD6ADA /* WOSynergyAnchorView.h */, + BC56F21D03DC508200BD6ADA /* WOSynergyAnchorView.m */, + BC56F22303DC5A1800BD6ADA /* WOSynergyAnchorController.h */, + BC56F22403DC5A1800BD6ADA /* WOSynergyAnchorController.m */, + ); + name = Classes; + sourceTree = ""; + }; + 19C28FB8FE9D52D311CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + BCBE2364093B34BD00FAD628 /* Synergy.app */, + BCFE77750B0211FE005B3442 /* Synergy.prefPane */, + BC6A2A3E0D149F500080E56B /* Synergy Preferences.app */, + ); + name = Products; + sourceTree = ""; + }; + BC0B9D620FF40882007AE543 /* Headers */ = { + isa = PBXGroup; + children = ( + BC0B9D640FF408A5007AE543 /* WOConvenienceMacros.h */, + ); + name = Headers; + sourceTree = ""; + }; + BC0B9D630FF4088A007AE543 /* Categories */ = { + isa = PBXGroup; + children = ( + BC55A1A4103ABA9000B5AB83 /* NSDictionary+WOCreation.h */, + BC55A1A5103ABA9000B5AB83 /* NSDictionary+WOCreation.m */, + ); + name = Categories; + sourceTree = ""; + }; + BC0B9D9C0FF409E3007AE543 /* Classes */ = { + isa = PBXGroup; + children = ( + BC0B9DAD0FF40A3E007AE543 /* WOLogManager.h */, + BC0B9DAE0FF40A3E007AE543 /* WOLogManager.m */, + BC0B9DAF0FF40A3E007AE543 /* WOLoginItem.h */, + BC0B9DB00FF40A3E007AE543 /* WOLoginItem.m */, + BC0B9DB10FF40A3E007AE543 /* WOLoginItemList.h */, + BC0B9DB20FF40A3E007AE543 /* WOLoginItemList.m */, + BC0B9DB30FF40A3E007AE543 /* WOObject.h */, + BC0B9DB40FF40A3E007AE543 /* WOObject.m */, + ); + name = Classes; + sourceTree = ""; + }; + BC0B9D9F0FF409EA007AE543 /* Categories */ = { + isa = PBXGroup; + children = ( + BC0B9DBE0FF40A80007AE543 /* NSDictionary+WOCreation.h */, + BC0B9DBF0FF40A80007AE543 /* NSDictionary+WOCreation.m */, + BC0B9DA50FF40A1F007AE543 /* NSMutableString+WOEditingUtilities.h */, + BC0B9DA60FF40A1F007AE543 /* NSMutableString+WOEditingUtilities.m */, + BC0B9DA70FF40A1F007AE543 /* NSString+WOCreation.h */, + BC0B9DA80FF40A1F007AE543 /* NSString+WOCreation.m */, + BC0B9DA90FF40A1F007AE543 /* NSString+WOFileUtilities.h */, + BC0B9DAA0FF40A1F007AE543 /* NSString+WOFileUtilities.m */, + ); + name = Categories; + sourceTree = ""; + }; + BC0B9DA20FF409F0007AE543 /* Headers */ = { + isa = PBXGroup; + children = ( + BC0B9DC00FF40A8A007AE543 /* WOConvenienceMacros.h */, + BC0B9DC10FF40A8A007AE543 /* WODebugMacros.h */, + BC0B9DC20FF40A8A007AE543 /* WOMemory.h */, + BC0B9DC30FF40A8A007AE543 /* WOMemoryBarrier.h */, + ); + name = Headers; + sourceTree = ""; + }; + BC55A137103AA1B000B5AB83 /* WOPublic */ = { + isa = PBXGroup; + children = ( + BC2A3E7A10F205270051E3BF /* WOConvenienceMacros.h */, + BC024B13104ADB1F001A9488 /* NSMutableString+WOEditingUtilities.h */, + BC024B14104ADB1F001A9488 /* NSMutableString+WOEditingUtilities.m */, + BC024B15104ADB1F001A9488 /* NSString+WOCreation.h */, + BC024B16104ADB1F001A9488 /* NSString+WOCreation.m */, + BC55A139103AA1FF00B5AB83 /* NSDictionary+WOCreation.h */, + BC55A13A103AA1FF00B5AB83 /* NSDictionary+WOCreation.m */, + BC55A138103AA1C300B5AB83 /* WOConvenienceMacros.h */, + ); + name = WOPublic; + sourceTree = ""; + }; + BC584A3A0FEFF24800F7BD9E /* WOPublic */ = { + isa = PBXGroup; + children = ( + BC0B9D630FF4088A007AE543 /* Categories */, + BC0B9D620FF40882007AE543 /* Headers */, + ); + name = WOPublic; + sourceTree = ""; + }; + BC5F9B140CFC338F00A5957F /* Configuration */ = { + isa = PBXGroup; + children = ( + BC0B9CF20FF401C6007AE543 /* release-style.xcconfig */, + BC0B9CF30FF401C6007AE543 /* debug-style.xcconfig */, + BC0B9CF40FF401C6007AE543 /* pref-pane-target.xcconfig */, + BC0B9CF50FF401C6007AE543 /* cocoa-app-target.xcconfig */, + BC0B9CF60FF401C6007AE543 /* base-style.xcconfig */, + ); + name = Configuration; + sourceTree = ""; + }; + BC6738260CFA0ACE0097E36B /* Scripts */ = { + isa = PBXGroup; + children = ( + BC8BE0B4127DBC9400B6B534 /* sign.sh */, + BC78E3ED103738FE0087DA74 /* tag-release.sh */, + BC8B9BB00D23111400A11CB9 /* distro.sh */, + ); + name = Scripts; + sourceTree = ""; + }; + BC6A2A490D149FA00080E56B /* Synergy Preferences */ = { + isa = PBXGroup; + children = ( + BC88153E0D19FB4100D969B3 /* WOPublic */, + BC9885D00D1922C100B00823 /* Other Sources */, + BC9885D10D1922C900B00823 /* Classes */, + BC9885D20D1922CF00B00823 /* Resources */, + ); + name = "Synergy Preferences"; + sourceTree = ""; + }; + BC88153E0D19FB4100D969B3 /* WOPublic */ = { + isa = PBXGroup; + children = ( + BC0B9DA20FF409F0007AE543 /* Headers */, + BC0B9D9F0FF409EA007AE543 /* Categories */, + BC0B9D9C0FF409E3007AE543 /* Classes */, + ); + name = WOPublic; + sourceTree = ""; + }; + BC8817350D1ACAE500D969B3 /* Frameworks */ = { + isa = PBXGroup; + children = ( + BCA18E820D33C991002092F0 /* ScriptingBridge.framework */, + BC3C93120B00DCBE0066E6D7 /* Security.framework */, + BC98EA9F0A3A01060048ADFF /* QuickTime.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + BC963A7003E268E700965C5C /* Resources */ = { + isa = PBXGroup; + children = ( + ); + name = Resources; + sourceTree = ""; + }; + BC9885D00D1922C100B00823 /* Other Sources */ = { + isa = PBXGroup; + children = ( + BC6A2A5B0D14A1210080E56B /* main.m */, + ); + name = "Other Sources"; + sourceTree = ""; + }; + BC9885D10D1922C900B00823 /* Classes */ = { + isa = PBXGroup; + children = ( + BC1567AA0D185F5C001991C5 /* WOPreferencePane.h */, + BC1567AB0D185F5C001991C5 /* WOPreferencePane.m */, + BC9885CD0D19226800B00823 /* WOPreferenceWindow.h */, + BC9885CE0D19226800B00823 /* WOPreferenceWindow.m */, + BC9885F80D1927FF00B00823 /* WOSynergyPreferencesController.h */, + BC9885F90D1927FF00B00823 /* WOSynergyPreferencesController.m */, + ); + name = Classes; + sourceTree = ""; + }; + BC9885D20D1922CF00B00823 /* Resources */ = { + isa = PBXGroup; + children = ( + BC8815B10D1AAA9900D969B3 /* Synergy.icns */, + BC8815CE0D1AC7E100D969B3 /* InfoPlist.strings */, + BC6A2A580D14A1000080E56B /* SynergyPreferences.xib */, + BC6A2A400D149F500080E56B /* Info.plist */, + ); + name = Resources; + sourceTree = ""; + }; + BC98E7530A39D8AE0048ADFF /* Categories */ = { + isa = PBXGroup; + children = ( + BC5D0AC3042FCFFE00A80001 /* WONSFileManagerExtensions.h */, + BC5D0AC4042FCFFE00A80001 /* WONSFileManagerExtensions.m */, + BC98E7560A39D8C60048ADFF /* NSImage+WOAdditions.h */, + BC98E7570A39D8C60048ADFF /* NSImage+WOAdditions.m */, + BC98E7CB0A39F0EF0048ADFF /* NSAppleScript+WOAdditions.h */, + BC98E7CC0A39F0EF0048ADFF /* NSAppleScript+WOAdditions.m */, + BC3C95FC0B0108A00066E6D7 /* NSTimer+WOPausable.h */, + BC3C95FD0B0108A00066E6D7 /* NSTimer+WOPausable.m */, + ); + name = Categories; + sourceTree = ""; + }; + F56944CE03AE3D7301A2C68D /* SynergyDocs */ = { + isa = PBXGroup; + children = ( + F56944CF03AE3D8601A2C68D /* Scripts */, + ); + name = SynergyDocs; + sourceTree = ""; + }; + F56944CF03AE3D8601A2C68D /* Scripts */ = { + isa = PBXGroup; + children = ( + ); + name = Scripts; + sourceTree = ""; + }; + F5779F3D039615DC01EE990F /* Scripts */ = { + isa = PBXGroup; + children = ( + ); + name = Scripts; + sourceTree = ""; + }; + F57E7B5203AD61BB01A8AA3E /* Resources */ = { + isa = PBXGroup; + children = ( + F57E7B5303AD61D401A8AA3E /* defaults.plist */, + BC7B628303D466F400B1B2E1 /* synergyFloater.xib */, + ); + name = Resources; + sourceTree = ""; + }; + F5964509039B4115014C8642 /* SynergyCommon */ = { + isa = PBXGroup; + children = ( + F57E7B5203AD61BB01A8AA3E /* Resources */, + F596450A039B411C014C8642 /* Classes */, + ); + name = SynergyCommon; + sourceTree = ""; + }; + F596450A039B411C014C8642 /* Classes */ = { + isa = PBXGroup; + children = ( + BC3C8C400B00A89D0066E6D7 /* WOAudioscrobblerController.h */, + BC3C8C410B00A89D0066E6D7 /* WOAudioscrobblerController.m */, + BC5BAB4E03C1B27100799024 /* WOCarbonWrappers.h */, + BC5BAB4F03C1B27100799024 /* WOCarbonWrappers.m */, + BC0B61BA03C9D2580035855F /* WOCommon.h */, + F55DFBBE03A84D64010663F9 /* WODebug.h */, + F577917B0395498401EE990F /* WODistributedNotification.h */, + F577917C0395498401EE990F /* WODistributedNotification.m */, + BCE4635F03CAB6BF00CB9B3F /* WONSStringExtensions.h */, + BCE4636003CAB6BF00CB9B3F /* WONSStringExtensions.m */, + F5277179039F7A0B013BBD0D /* WOPreferences.h */, + F527717A039F7A0B013BBD0D /* WOPreferences.m */, + BC30616403FA8FDD007C4E56 /* WOSynergyGlobal.h */, + F56702DC03A4BC7201842AA0 /* WOSynergyView.h */, + F56702DD03A4BC7201842AA0 /* WOSynergyView.m */, + BC7B627303D45F7D00B1B2E1 /* WORoundedRect.h */, + BC7B627403D45F7D00B1B2E1 /* WORoundedRect.m */, + BCC2DD090445BC6B00A80001 /* WOSynergyFloater.h */, + BC7B627703D4602200B1B2E1 /* WOSynergyFloaterView.h */, + BC7B627803D4602200B1B2E1 /* WOSynergyFloaterView.m */, + BC7B627B03D460B300B1B2E1 /* WOSynergyFloaterWindow.h */, + BC7B627C03D460B300B1B2E1 /* WOSynergyFloaterWindow.m */, + BC7B627F03D4613B00B1B2E1 /* WOSynergyFloaterController.h */, + BC7B628003D4613B00B1B2E1 /* WOSynergyFloaterController.m */, + BC5CC3260409B4E2001CCC4A /* WOButtonSet.h */, + BC5CC3270409B4E2001CCC4A /* WOButtonSet.m */, + BC26C14B041C82F500A80001 /* WONSScreenExtensions.h */, + BC26C14C041C82F500A80001 /* WONSScreenExtensions.m */, + BC5D0AC7042FDFFF00A80001 /* WOExceptions.h */, + BC037D100430328600A80001 /* WOControlButtons.h */, + BC037D110430328600A80001 /* WOControlButtons.m */, + BC17717A044386A900A80001 /* NSString+WOExtensions.h */, + BC17717B044386A900A80001 /* NSString+WOExtensions.m */, + ); + name = Classes; + sourceTree = ""; + }; + F5CB1D6B0394AF0D01754549 /* SynergyApp */ = { + isa = PBXGroup; + children = ( + BC55A137103AA1B000B5AB83 /* WOPublic */, + BC98E7530A39D8AE0048ADFF /* Categories */, + F5CB21860394B27B01754549 /* Classes */, + F5CB21870394B27F01754549 /* Resources */, + F5CB218A0394B2CD01754549 /* Other Sources */, + F5CB21890394B29401754549 /* Scripts */, + F5CB21880394B28301754549 /* Frameworks and Libraries */, + ); + name = SynergyApp; + sourceTree = ""; + }; + F5CB21860394B27B01754549 /* Classes */ = { + isa = PBXGroup; + children = ( + BCA18E860D33C9B1002092F0 /* iTunes.h */, + F54314780394BAF401AC36F3 /* HotkeyCapableApplication.h */, + F54314790394BAF401AC36F3 /* HotkeyCapableApplication.m */, + F543147A0394BAF401AC36F3 /* SynergyController.h */, + F543147B0394BAF401AC36F3 /* SynergyController.m */, + BC7E248003DB8FEE002EDF57 /* WOProcessManager.h */, + BC7E248103DB8FEE002EDF57 /* WOProcessManager.m */, + BCDC33E003E2799000A59DD4 /* WOFeedbackView.h */, + BCDC33E103E2799000A59DD4 /* WOFeedbackView.m */, + BCE15EA803E459B6005E6BE2 /* WOFeedbackDefaults.h */, + BCDC33E403E279CB00A59DD4 /* WOFeedbackController.h */, + BCDC33E503E279CB00A59DD4 /* WOFeedbackController.m */, + BCDC33E803E279D900A59DD4 /* WOFeedbackWindow.h */, + BCDC33E903E279D900A59DD4 /* WOFeedbackWindow.m */, + BC542869040520370052F570 /* WOCoverDownloader.h */, + BC54286A040520370052F570 /* WOCoverDownloader.m */, + BC27E64A042DEF5300A80001 /* WOSongInfo.h */, + BC27E64B042DEF5300A80001 /* WOSongInfo.m */, + BC040E40041F3C180052CE41 /* WOButtonState.h */, + BC040E41041F3C180052CE41 /* WOButtonState.m */, + BCE8D3E90420BEAA00A80001 /* WOButtonWithTrackingRect.h */, + BCE8D3EA0420BEAA00A80001 /* WOButtonWithTrackingRect.m */, + BCC06709045D179D00A80001 /* WOButtonCellWithTrackingRect.h */, + BCC0670C045D1B8400A80001 /* WOButtonCellWithTrackingRect.m */, + BCC066EF045BF68A00A80001 /* WOPopUpButton.h */, + BCC066F0045BF68A00A80001 /* WOPopUpButton.m */, + BC617DFF0AF7CC9D00E1268F /* WOAudioscrobbler.h */, + BC617E000AF7CC9D00E1268F /* WOAudioscrobbler.m */, + BC3C8AFF0AFF9FC50066E6D7 /* SynergyController+WOAudioscrobbler.h */, + BC3C8B000AFF9FC50066E6D7 /* SynergyController+WOAudioscrobbler.m */, + ); + name = Classes; + sourceTree = ""; + }; + F5CB21870394B27F01754549 /* Resources */ = { + isa = PBXGroup; + children = ( + BC3BD60306DE9F4E004768FD /* SynergyButtons.icns */, + BC3BD42406DE6FF5004768FD /* Button Sets */, + BCC06705045CD73700A80001 /* musicGlyph.png */, + BCBB9316046FC5EB00A80001 /* musicGlyphSelected.png */, + BCBE2360093B34BD00FAD628 /* Info.plist */, + F5B86F61039931F1010F9052 /* InfoPlist.strings */, + F5B86F63039931F1010F9052 /* Localizable.strings */, + F5B86F65039931F1010F9052 /* synergyMain.xib */, + BCDC33EC03E2BA3C00A59DD4 /* feedback.xib */, + BC927353040CE2330099CFAC /* repeatAll.png */, + BC927354040CE2330099CFAC /* repeatOff.png */, + BC927355040CE2330099CFAC /* repeatOne.png */, + BC927305040BA1860099CFAC /* shuffleOff.png */, + BC927306040BA1860099CFAC /* shuffleOn.png */, + BCE15E9C03E421D5005E6BE2 /* nextFeedback.png */, + BCE15E9D03E421D5005E6BE2 /* pauseFeedback.png */, + BCE15E9E03E421D5005E6BE2 /* playFeedback.png */, + BCE15E9F03E421D5005E6BE2 /* prevFeedback.png */, + BCDC33F203E419E800A59DD4 /* nextImage.png */, + BCDC33F303E419E800A59DD4 /* pauseImage.png */, + BC0AE4E30406480B00248FD8 /* playPauseImage.png */, + BCE15EA403E42BE9005E6BE2 /* playPauseFeedback.png */, + BCDC33F403E419E800A59DD4 /* playImage.png */, + BCDC33F503E419E800A59DD4 /* prevImage.png */, + BCDC33F603E419E800A59DD4 /* stopImage.png */, + BCAC739107E5FDF800FDA956 /* NoCoverArt.png */, + BCDC33F703E419E800A59DD4 /* SynergyLarge.png */, + F54314930394BB6601AC36F3 /* Synergy.icns */, + BCDC33F003E2FFFA00A59DD4 /* volume.png */, + BCE15EA603E43204005E6BE2 /* bars.png */, + ); + name = Resources; + sourceTree = ""; + }; + F5CB21880394B28301754549 /* Frameworks and Libraries */ = { + isa = PBXGroup; + children = ( + BC680D640834C48F00ABF3B8 /* Growl.framework */, + BC5D0AD8042FE8A300A80001 /* CoreFoundation.framework */, + BCBE04380426C12B00A80001 /* SystemConfiguration.framework */, + F5CB1D7B0394B24501754549 /* Cocoa.framework */, + F5CB1D790394B24501754549 /* AppKit.framework */, + F5CB1D7A0394B24501754549 /* Carbon.framework */, + F5CB1D7C0394B24501754549 /* Foundation.framework */, + ); + name = "Frameworks and Libraries"; + sourceTree = ""; + }; + F5CB21890394B29401754549 /* Scripts */ = { + isa = PBXGroup; + children = ( + F54314910394BB2101AC36F3 /* getSongInfo.applescript */, + F5B86F6D039932B1010F9052 /* build.sh */, + BCE15EB203E558A1005E6BE2 /* compileAppleScript.sh */, + ); + name = Scripts; + sourceTree = ""; + }; + F5CB218A0394B2CD01754549 /* Other Sources */ = { + isa = PBXGroup; + children = ( + F543148F0394BB0E01AC36F3 /* main.m */, + BC08FB9D0404F1E00099965D /* WOSysctl.h */, + BC08FB9E0404F1E00099965D /* WOSysctl.c */, + ); + name = "Other Sources"; + sourceTree = ""; + }; + F5CB219F0394B36D01754549 /* SynergyPref */ = { + isa = PBXGroup; + children = ( + BC584A3A0FEFF24800F7BD9E /* WOPublic */, + 08FB77AFFE84173DC02AAC07 /* Classes */, + 089C167CFE841241C02AAC07 /* Resources */, + F5779F3D039615DC01EE990F /* Scripts */, + 089C1671FE841209C02AAC07 /* Frameworks and Libraries */, + ); + name = SynergyPref; + sourceTree = ""; + }; + F5FF36EC0399FF2A01EE0299 /* SynergyDiskImage */ = { + isa = PBXGroup; + children = ( + BC963A7003E268E700965C5C /* Resources */, + F5FF36ED0399FF3101EE0299 /* Scripts */, + ); + name = SynergyDiskImage; + sourceTree = ""; + }; + F5FF36ED0399FF3101EE0299 /* Scripts */ = { + isa = PBXGroup; + children = ( + ); + name = Scripts; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + BC6A2A3D0D149F500080E56B /* Synergy Preferences */ = { + isa = PBXNativeTarget; + buildConfigurationList = BC6A2A430D149F510080E56B /* Build configuration list for PBXNativeTarget "Synergy Preferences" */; + buildPhases = ( + BC6A2A3A0D149F500080E56B /* Resources */, + BC6A2A3B0D149F500080E56B /* Sources */, + BC6A2A3C0D149F500080E56B /* Frameworks */, + BC8815CD0D1AC75000D969B3 /* Run Script (Preprocess InfoPlist.strings) */, + BC8BE14B127DD96A00B6B534 /* Run Script (sign) */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Synergy Preferences"; + productName = "Synergy Preferences"; + productReference = BC6A2A3E0D149F500080E56B /* Synergy Preferences.app */; + productType = "com.apple.product-type.application"; + }; + BCBE228A093B34BD00FAD628 /* SynergyPref */ = { + isa = PBXNativeTarget; + buildConfigurationList = BCBE22E0093B34BD00FAD628 /* Build configuration list for PBXNativeTarget "SynergyPref" */; + buildPhases = ( + BCBE22AE093B34BD00FAD628 /* Resources */, + BCBE22BC093B34BD00FAD628 /* Sources */, + BCBE22D9093B34BD00FAD628 /* Frameworks */, + BCBE22DD093B34BD00FAD628 /* Run Script (update strings) */, + BCBE22DE093B34BD00FAD628 /* Copy Files (Synergy.app) */, + BC5C50CC0BA85B4E00A2215A /* Run Script (Preprocess InfoPlist.strings) */, + BC8BE149127DD95300B6B534 /* Run Script (sign) */, + ); + buildRules = ( + BCBE22E8093B34BD00FAD628 /* PBXBuildRule */, + ); + dependencies = ( + BC1567BE0D18653E001991C5 /* PBXTargetDependency */, + BCBE23A1093B34BD00FAD628 /* PBXTargetDependency */, + ); + name = SynergyPref; + productName = Synergy; + productReference = BCFE77750B0211FE005B3442 /* Synergy.prefPane */; + productType = "com.apple.product-type.bundle"; + }; + BCBE22EA093B34BD00FAD628 /* SynergyApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = BCBE235B093B34BD00FAD628 /* Build configuration list for PBXNativeTarget "SynergyApp" */; + buildPhases = ( + BCBE2313093B34BD00FAD628 /* Resources */, + BCBE232D093B34BD00FAD628 /* CopyFiles */, + BCBE232F093B34BD00FAD628 /* Sources */, + BCBE2352093B34BD00FAD628 /* Frameworks */, + BCBE2359093B34BD00FAD628 /* Run Script (compile AppleScripts) */, + BCBE235A093B34BD00FAD628 /* Run Script (update strings) */, + BC5C50D00BA85B6400A2215A /* Run Script (Preprocess InfoPlist.strings) */, + BC8BE0D7127DBCA900B6B534 /* Run Script (sign) */, + ); + buildRules = ( + BCBE2363093B34BD00FAD628 /* PBXBuildRule */, + ); + dependencies = ( + ); + name = SynergyApp; + productInstallPath = "$(USER_APPS_DIR)"; + productName = Synergy; + productReference = BCBE2364093B34BD00FAD628 /* Synergy.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 089C1669FE841209C02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = BC1A68FB085BD178004E0E61 /* Build configuration list for PBXProject "Synergy" */; + compatibilityVersion = "Xcode 3.0"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + Spanish, + Dutch, + Swedish, + ru, + Norwegian, + Norweigan, + pt, + zh_TW, + it, + tr, + zh_CN, + en, + es, + ); + mainGroup = 089C166AFE841209C02AAC07 /* Synergy */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + BCBE228A093B34BD00FAD628 /* SynergyPref */, + BCBE22EA093B34BD00FAD628 /* SynergyApp */, + BC6A2A3D0D149F500080E56B /* Synergy Preferences */, + BC1567C00D186568001991C5 /* Synergy Prefs + SynergyPref */, + BC881F3E0D1AD08C00D969B3 /* Update build number */, + BC8B9A730D22EC7F00A11CB9 /* Distribution */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + BC6A2A3A0D149F500080E56B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BC6A2A5A0D14A1000080E56B /* SynergyPreferences.xib in Resources */, + BC8815B20D1AAA9900D969B3 /* Synergy.icns in Resources */, + BC8815D00D1AC7E100D969B3 /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BCBE22AE093B34BD00FAD628 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BCBE22AF093B34BD00FAD628 /* InfoPlist.strings in Resources */, + BCBE22B0093B34BD00FAD628 /* Localizable.strings in Resources */, + BCBE22B1093B34BD00FAD628 /* OrgWincentSynergyPref.xib in Resources */, + BCBE22B2093B34BD00FAD628 /* defaults.plist in Resources */, + BCBE22B3093B34BD00FAD628 /* synergyFloater.xib in Resources */, + BCBE22B4093B34BD00FAD628 /* pin.png in Resources */, + BCBE22B5093B34BD00FAD628 /* SynergyPref.png in Resources */, + BCBE22B6093B34BD00FAD628 /* pin.xib in Resources */, + BCBE22B7093B34BD00FAD628 /* SynergyLarge.png in Resources */, + BCBE22B8093B34BD00FAD628 /* Unicodes.plist in Resources */, + BCBE22BA093B34BD00FAD628 /* about.rtf in Resources */, + BCBE22BB093B34BD00FAD628 /* NoCoverArt.png in Resources */, + BC0B53930982BCD00033B1A7 /* MacOSX_Universal_60px.gif in Resources */, + BC3C8CB80B00B8B80066E6D7 /* audioscrobbler.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BCBE2313093B34BD00FAD628 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BCBE2314093B34BD00FAD628 /* InfoPlist.strings in Resources */, + BCBE2315093B34BD00FAD628 /* Localizable.strings in Resources */, + BCBE2316093B34BD00FAD628 /* synergyMain.xib in Resources */, + BCBE2317093B34BD00FAD628 /* defaults.plist in Resources */, + BCBE2318093B34BD00FAD628 /* synergyFloater.xib in Resources */, + BCBE2319093B34BD00FAD628 /* feedback.xib in Resources */, + BCBE231A093B34BD00FAD628 /* volume.png in Resources */, + BCBE231B093B34BD00FAD628 /* SynergyLarge.png in Resources */, + BCBE231C093B34BD00FAD628 /* nextFeedback.png in Resources */, + BCBE231D093B34BD00FAD628 /* pauseFeedback.png in Resources */, + BCBE231E093B34BD00FAD628 /* playFeedback.png in Resources */, + BCBE231F093B34BD00FAD628 /* prevFeedback.png in Resources */, + BCBE2320093B34BD00FAD628 /* playPauseFeedback.png in Resources */, + BCBE2321093B34BD00FAD628 /* bars.png in Resources */, + BCBE2322093B34BD00FAD628 /* shuffleOff.png in Resources */, + BCBE2323093B34BD00FAD628 /* shuffleOn.png in Resources */, + BCBE2324093B34BD00FAD628 /* repeatAll.png in Resources */, + BCBE2325093B34BD00FAD628 /* repeatOff.png in Resources */, + BCBE2326093B34BD00FAD628 /* repeatOne.png in Resources */, + BCBE2327093B34BD00FAD628 /* Synergy.icns in Resources */, + BCBE2328093B34BD00FAD628 /* musicGlyph.png in Resources */, + BCBE2329093B34BD00FAD628 /* musicGlyphSelected.png in Resources */, + BCBE232A093B34BD00FAD628 /* Button Sets in Resources */, + BCBE232B093B34BD00FAD628 /* SynergyButtons.icns in Resources */, + BCBE232C093B34BD00FAD628 /* NoCoverArt.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + BC5C50CC0BA85B4E00A2215A /* Run Script (Preprocess InfoPlist.strings) */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script (Preprocess InfoPlist.strings)"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = ". \"${BUILDTOOLS_DIR}/PreprocessInfoPlistStrings.sh\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}\"\n"; + }; + BC5C50D00BA85B6400A2215A /* Run Script (Preprocess InfoPlist.strings) */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script (Preprocess InfoPlist.strings)"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = ". \"${BUILDTOOLS_DIR}/PreprocessInfoPlistStrings.sh\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}\"\n"; + }; + BC8815CD0D1AC75000D969B3 /* Run Script (Preprocess InfoPlist.strings) */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script (Preprocess InfoPlist.strings)"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = ". \"${BUILDTOOLS_DIR}/PreprocessInfoPlistStrings.sh\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}\"\n"; + }; + BC881F3D0D1AD08C00D969B3 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "set -e\ncd \"${SOURCE_ROOT}\"\n\"${BUILDTOOLS_DIR}/UpdateGitRevisionNumbers.rb\"\n. \"${BUILDTOOLS_DIR}/UpdateBuildVersionNumbers.sh\" \"WOSynergy_Version.h\"\n"; + }; + BC8B9A720D22EC7F00A11CB9 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = "/bin/sh -e"; + shellScript = ". \"$SOURCE_ROOT/distro.sh\"\n"; + }; + BC8BE0D7127DBCA900B6B534 /* Run Script (sign) */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script (sign)"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = ". $SOURCE_ROOT/sign.sh"; + }; + BC8BE149127DD95300B6B534 /* Run Script (sign) */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script (sign)"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = ". $SOURCE_ROOT/sign.sh"; + }; + BC8BE14B127DD96A00B6B534 /* Run Script (sign) */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script (sign)"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = ". $SOURCE_ROOT/sign.sh"; + }; + BCBE22DD093B34BD00FAD628 /* Run Script (update strings) */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 12; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/SynergyPref/Classes/WOKeyCaptureView.m", + "$(SRCROOT)/SynergyPref/Classes/OrgWincentSynergyPref.m", + ); + name = "Run Script (update strings)"; + outputPaths = ( + "$(SRCROOT)/SynergyPref/Resources/en.lproj/Localizable.strings", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = "/bin/sh -e"; + shellScript = ". \"${BUILDTOOLS_DIR}/CheckInputOutputFiles.sh\" ||\n . \"${BUILDTOOLS_DIR}/UpdateStringsFiles.sh\" \\\n \"${SOURCE_ROOT}/SynergyPref/Classes\" \\\n \"${SOURCE_ROOT}/SynergyPref/Resources\"\n"; + }; + BCBE2359093B34BD00FAD628 /* Run Script (compile AppleScripts) */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + name = "Run Script (compile AppleScripts)"; + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "#!/bin/sh\n#\n# IF YOU EDIT THIS SCRIPT, REMEMBER TO PLACE AN EDITED COPY IN THE TARGET/BUILD SETTINGS AREA\n#\n# $Id: project.pbxproj,v 1.22 2003/02/24 06:43:04 wincent Exp $\n#\n# this is ugly, but we override the default AppleScript compilation command in \n# order to add this -x flag (save as execute only)\n\n/bin/mkdir -p \"${BUILT_PRODUCTS_DIR}/Synergy.app/Contents/Resources/Scripts\"\n\n/usr/bin/osacompile -x -d \\\n -i /System/Library/Frameworks/AppleScriptKit.framework \\\n -U getSongInfo.applescript \\\n -o \"${BUILT_PRODUCTS_DIR}/Synergy.app/Contents/Resources/Scripts/getSongInfo.scpt\" \\\n \"${SOURCE_ROOT}/SynergyApp/Scripts/getSongInfo.applescript\"\n \n "; + }; + BCBE235A093B34BD00FAD628 /* Run Script (update strings) */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/SynergyApp/Classes/SynergyController.m", + "$(SRCROOT)/SynergyApp/Classes/HotkeyCapableApplication.m", + ); + name = "Run Script (update strings)"; + outputPaths = ( + "$(SRCROOT)/SynergyApp/Resources/en.lproj/Localizable.strings", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = ". \"${BUILDTOOLS_DIR}/CheckInputOutputFiles.sh\" ||\n . \"${BUILDTOOLS_DIR}/UpdateStringsFiles.sh\" \\\n \"${SOURCE_ROOT}/SynergyApp/Classes\" \\\n \"${SOURCE_ROOT}/SynergyApp/Resources\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + BC6A2A3B0D149F500080E56B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BC0B9DD00FF40AC4007AE543 /* main.m in Sources */, + BC0B9DD10FF40AC4007AE543 /* NSDictionary+WOCreation.m in Sources */, + BC0B9DD20FF40AC4007AE543 /* NSMutableString+WOEditingUtilities.m in Sources */, + BC0B9DD30FF40AC4007AE543 /* NSString+WOCreation.m in Sources */, + BC0B9DD40FF40AC4007AE543 /* NSString+WOFileUtilities.m in Sources */, + BC0B9DD50FF40AC4007AE543 /* WOLoginItem.m in Sources */, + BC0B9DD60FF40AC4007AE543 /* WOLoginItemList.m in Sources */, + BC0B9DD70FF40AC4007AE543 /* WOLogManager.m in Sources */, + BC0B9DD80FF40AC4007AE543 /* WOObject.m in Sources */, + BC0B9DD90FF40AC4007AE543 /* WOPreferencePane.m in Sources */, + BC0B9DDA0FF40AC4007AE543 /* WOPreferenceWindow.m in Sources */, + BC0B9DDB0FF40AC4007AE543 /* WOSynergyPreferencesController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BCBE22BC093B34BD00FAD628 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BC0B9D7D0FF409A7007AE543 /* OrgWincentSynergyPref.m in Sources */, + BC0B9D7E0FF409A7007AE543 /* WOAudioscrobblerController.m in Sources */, + BC0B9D7F0FF409A7007AE543 /* WOButtonCellWithTrackingRect.m in Sources */, + BC0B9D800FF409A7007AE543 /* WOButtonSet.m in Sources */, + BC0B9D810FF409A7007AE543 /* WOButtonState.m in Sources */, + BC0B9D820FF409A7007AE543 /* WOButtonWithTrackingRect.m in Sources */, + BC0B9D830FF409A7007AE543 /* WOControlButtons.m in Sources */, + BC0B9D840FF409A7007AE543 /* WODistributedNotification.m in Sources */, + BC0B9D860FF409A7007AE543 /* WOKeyCaptureView.m in Sources */, + BC0B9D870FF409A7007AE543 /* WONSFileManagerExtensions.m in Sources */, + BC0B9D880FF409A7007AE543 /* WONSScreenExtensions.m in Sources */, + BC0B9D890FF409A7007AE543 /* WOPopUpButton.m in Sources */, + BC0B9D8A0FF409A7007AE543 /* WOPreferences.m in Sources */, + BC0B9D8B0FF409A7007AE543 /* WOProcessManager.m in Sources */, + BC0B9D8C0FF409A7007AE543 /* WORoundedRect.m in Sources */, + BC0B9D8D0FF409A7007AE543 /* WOSynergyAnchorController.m in Sources */, + BC0B9D8E0FF409A7007AE543 /* WOSynergyAnchorView.m in Sources */, + BC0B9D8F0FF409A7007AE543 /* WOSynergyAnchorWindow.m in Sources */, + BC0B9D900FF409A7007AE543 /* WOSynergyFloaterController.m in Sources */, + BC0B9D910FF409A7007AE543 /* WOSynergyFloaterView.m in Sources */, + BC0B9D920FF409A7007AE543 /* WOSynergyFloaterWindow.m in Sources */, + BC0B9D930FF409A7007AE543 /* WOSynergyView.m in Sources */, + BC55A1A6103ABA9000B5AB83 /* NSDictionary+WOCreation.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BCBE232F093B34BD00FAD628 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BC0B9E270FF40C56007AE543 /* HotkeyCapableApplication.m in Sources */, + BC0B9E280FF40C56007AE543 /* main.m in Sources */, + BC0B9E290FF40C56007AE543 /* NSAppleScript+WOAdditions.m in Sources */, + BC0B9E2A0FF40C56007AE543 /* NSImage+WOAdditions.m in Sources */, + BC0B9E2B0FF40C56007AE543 /* NSString+WOExtensions.m in Sources */, + BC0B9E2C0FF40C56007AE543 /* NSTimer+WOPausable.m in Sources */, + BC0B9E2D0FF40C56007AE543 /* SynergyController+WOAudioscrobbler.m in Sources */, + BC0B9E2E0FF40C56007AE543 /* SynergyController.m in Sources */, + BC0B9E2F0FF40C56007AE543 /* WOAudioscrobbler.m in Sources */, + BC0B9E300FF40C56007AE543 /* WOAudioscrobblerController.m in Sources */, + BC0B9E310FF40C56007AE543 /* WOButtonCellWithTrackingRect.m in Sources */, + BC0B9E320FF40C56007AE543 /* WOButtonSet.m in Sources */, + BC0B9E330FF40C56007AE543 /* WOButtonState.m in Sources */, + BC0B9E340FF40C56007AE543 /* WOButtonWithTrackingRect.m in Sources */, + BC0B9E350FF40C56007AE543 /* WOCarbonWrappers.m in Sources */, + BC0B9E360FF40C56007AE543 /* WOControlButtons.m in Sources */, + BC0B9E370FF40C56007AE543 /* WOCoverDownloader.m in Sources */, + BC0B9E380FF40C56007AE543 /* WODistributedNotification.m in Sources */, + BC0B9E390FF40C56007AE543 /* WOFeedbackController.m in Sources */, + BC0B9E3A0FF40C56007AE543 /* WOFeedbackView.m in Sources */, + BC0B9E3B0FF40C56007AE543 /* WOFeedbackWindow.m in Sources */, + BC0B9E3D0FF40C56007AE543 /* WONSFileManagerExtensions.m in Sources */, + BC0B9E3E0FF40C56007AE543 /* WONSScreenExtensions.m in Sources */, + BC0B9E3F0FF40C56007AE543 /* WONSStringExtensions.m in Sources */, + BC0B9E400FF40C56007AE543 /* WOPopUpButton.m in Sources */, + BC0B9E410FF40C56007AE543 /* WOPreferences.m in Sources */, + BC0B9E420FF40C56007AE543 /* WOProcessManager.m in Sources */, + BC0B9E430FF40C56007AE543 /* WORoundedRect.m in Sources */, + BC0B9E440FF40C56007AE543 /* WOSongInfo.m in Sources */, + BC0B9E450FF40C56007AE543 /* WOSynergyFloaterController.m in Sources */, + BC0B9E460FF40C56007AE543 /* WOSynergyFloaterView.m in Sources */, + BC0B9E470FF40C56007AE543 /* WOSynergyFloaterWindow.m in Sources */, + BC0B9E480FF40C56007AE543 /* WOSynergyView.m in Sources */, + BC0B9E490FF40C56007AE543 /* WOSysctl.c in Sources */, + BC55A13B103AA1FF00B5AB83 /* NSDictionary+WOCreation.m in Sources */, + BC024B17104ADB1F001A9488 /* NSMutableString+WOEditingUtilities.m in Sources */, + BC024B18104ADB1F001A9488 /* NSString+WOCreation.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + BC1567BE0D18653E001991C5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BC6A2A3D0D149F500080E56B /* Synergy Preferences */; + targetProxy = BC1567BD0D18653E001991C5 /* PBXContainerItemProxy */; + }; + BC1567C40D18656E001991C5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BC6A2A3D0D149F500080E56B /* Synergy Preferences */; + targetProxy = BC1567C30D18656E001991C5 /* PBXContainerItemProxy */; + }; + BC1567C60D186570001991C5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BCBE228A093B34BD00FAD628 /* SynergyPref */; + targetProxy = BC1567C50D186570001991C5 /* PBXContainerItemProxy */; + }; + BC8B9A800D22ECC000A11CB9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BC881F3E0D1AD08C00D969B3 /* Update build number */; + targetProxy = BC8B9A7F0D22ECC000A11CB9 /* PBXContainerItemProxy */; + }; + BC8B9A820D22ECC400A11CB9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BC1567C00D186568001991C5 /* Synergy Prefs + SynergyPref */; + targetProxy = BC8B9A810D22ECC400A11CB9 /* PBXContainerItemProxy */; + }; + BCBE23A1093B34BD00FAD628 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BCBE22EA093B34BD00FAD628 /* SynergyApp */; + targetProxy = BCBE23A0093B34BD00FAD628 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + BC3C8CB20B00B8B80066E6D7 /* audioscrobbler.xib */ = { + isa = PBXVariantGroup; + children = ( + BC881F930D1AFEF100D969B3 /* en */, + BC7566750D1EFF49005AD9F2 /* zh_TW */, + BC7566760D1EFF49005AD9F2 /* zh_CN */, + BC7566770D1EFF49005AD9F2 /* tr */, + BC7566780D1EFF49005AD9F2 /* Swedish */, + BC7566790D1EFF49005AD9F2 /* Spanish */, + BC75667A0D1EFF49005AD9F2 /* ru */, + BC75667B0D1EFF49005AD9F2 /* pt */, + BC75667C0D1EFF49005AD9F2 /* Norwegian */, + BC75667D0D1EFF49005AD9F2 /* Japanese */, + BC75667E0D1EFF49005AD9F2 /* it */, + BC75667F0D1EFF49005AD9F2 /* German */, + BC7566800D1EFF49005AD9F2 /* French */, + BC7566810D1EFF49005AD9F2 /* Dutch */, + ); + name = audioscrobbler.xib; + sourceTree = ""; + }; + BC689891046AC30F00A80001 /* Unicodes.plist */ = { + isa = PBXVariantGroup; + children = ( + BC689895046AC32100A80001 /* French */, + BC689896046AC32E00A80001 /* Spanish */, + BC588016046F9F9C00A80001 /* German */, + BC2C26AA0482824B00A80001 /* Dutch */, + BCA7F4A704837A9300A80001 /* Swedish */, + BCE03A5A061ACC1A00238E7E /* ru */, + BCD8710E07669C4A00FAA179 /* zh_TW */, + BCD8712B07669D4300FAA179 /* it */, + BC3C3177080C9ED500DF9C20 /* zh_CN */, + BC881F940D1AFEF100D969B3 /* en */, + ); + name = Unicodes.plist; + sourceTree = ""; + }; + BC6A2A580D14A1000080E56B /* SynergyPreferences.xib */ = { + isa = PBXVariantGroup; + children = ( + BC6A2A590D14A1000080E56B /* en */, + ); + name = SynergyPreferences.xib; + sourceTree = ""; + }; + BC8815CE0D1AC7E100D969B3 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + BC8815CF0D1AC7E100D969B3 /* en */, + BC88161D0D1ACA5D00D969B3 /* es */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + BCDF68270753B6A100EF20AF /* about.rtf */ = { + isa = PBXVariantGroup; + children = ( + BCDF68280753B6A100EF20AF /* Japanese */, + BCDF68290753B6A100EF20AF /* French */, + BCDF682A0753B6A100EF20AF /* ru */, + BCDF682B0753B6A100EF20AF /* Swedish */, + BCDF682C0753B6A100EF20AF /* Norwegian */, + BCDF682E0753B6A100EF20AF /* Dutch */, + BCDF682F0753B6A100EF20AF /* German */, + BCDF68300753B6A100EF20AF /* Spanish */, + BCD8711207669C8900FAA179 /* zh_TW */, + BCD8712707669D4300FAA179 /* it */, + BCB5C0E107D7DF4A00D08CA9 /* tr */, + BC3C3172080C9ECD00DF9C20 /* zh_CN */, + BC881F920D1AFEF100D969B3 /* en */, + ); + name = about.rtf; + sourceTree = ""; + }; + F58C12A803991E3601B0D373 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + F58C12B103991E4E01B0D373 /* Spanish */, + BCC06707045CF19700A80001 /* French */, + BC689898046BE0FC00A80001 /* Japanese */, + BC588013046F9F6F00A80001 /* German */, + BCA7F4A3048379BC00A80001 /* Swedish */, + BCA7F4A404837A0C00A80001 /* Dutch */, + BCF3AFB0060B3CAF00B2B13C /* ru */, + BC2D22EB072E9D6D007E3CBD /* Norwegian */, + BCD8710B07669C4A00FAA179 /* zh_TW */, + BCD8712807669D4300FAA179 /* it */, + BC3C3173080C9ECD00DF9C20 /* zh_CN */, + BC881F960D1AFEF100D969B3 /* en */, + BC5346E00D200F2F00D8D287 /* pt */, + BC5346E10D200F2F00D8D287 /* tr */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + F58C12AA03991E3601B0D373 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + F58C12B203991E6001B0D373 /* Spanish */, + BC2B328A0457FF1F00A80001 /* French */, + BC68988904691BF000A80001 /* Japanese */, + BC588014046F9F8B00A80001 /* German */, + BC2C26AB0482825B00A80001 /* Dutch */, + BCA7F4A504837A4100A80001 /* Swedish */, + BCF3AFAF060B3C9E00B2B13C /* ru */, + BCB0BD8A0682231300FFC46C /* Norwegian */, + BCD86C87076696EA00FAA179 /* pt */, + BCD8710C07669C4A00FAA179 /* zh_TW */, + BCD8712907669D4300FAA179 /* it */, + BCB5C0F107D7DF5900D08CA9 /* tr */, + BC3C3174080C9ECD00DF9C20 /* zh_CN */, + BC881F950D1AFEF100D969B3 /* en */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + F58C12AC03991E3601B0D373 /* OrgWincentSynergyPref.xib */ = { + isa = PBXVariantGroup; + children = ( + BC881F910D1AFEF100D969B3 /* en */, + ); + name = OrgWincentSynergyPref.xib; + sourceTree = ""; + }; + F5B86F61039931F1010F9052 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + F5B86F6A0399320C010F9052 /* Spanish */, + BCBB9A370470727600A80001 /* Dutch */, + BCF3AFB1060B3D2A00B2B13C /* ru */, + BCD86CA507669B3600FAA179 /* zh_TW */, + BCD8714007669DC400FAA179 /* it */, + BC3C3168080C9E8D00DF9C20 /* zh_CN */, + BC881F860D1AFE9300D969B3 /* en */, + BC5346D70D200ED600D8D287 /* German */, + BC5346D80D200ED600D8D287 /* pt */, + BC5346D90D200ED600D8D287 /* Norwegian */, + BC5346DA0D200ED600D8D287 /* tr */, + BC5346DB0D200ED600D8D287 /* French */, + BC5346DC0D200ED600D8D287 /* Japanese */, + BC5346F70D200F7100D8D287 /* Swedish */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + F5B86F63039931F1010F9052 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + F5B86F6B03993218010F9052 /* Spanish */, + BC2B328804574EF800A80001 /* French */, + BC68988B04691C0700A80001 /* Japanese */, + BC588017046FA03100A80001 /* German */, + BCBB9A360470726C00A80001 /* Dutch */, + BCA7F4A804837B7300A80001 /* Swedish */, + BCF3AFB2060B3D4700B2B13C /* ru */, + BCD86C680766964800FAA179 /* pt */, + BCD86CA607669B3600FAA179 /* zh_TW */, + BCD8714107669DC400FAA179 /* it */, + BCB5C0F807D7DF7B00D08CA9 /* tr */, + BC3C3169080C9E8D00DF9C20 /* zh_CN */, + BC881F850D1AFE9300D969B3 /* en */, + BCD1967F0D3258D400D324C3 /* Norwegian */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + F5B86F65039931F1010F9052 /* synergyMain.xib */ = { + isa = PBXVariantGroup; + children = ( + BC881F870D1AFE9300D969B3 /* en */, + ); + name = synergyMain.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + BC1567C10D186568001991C5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "Synergy Prefs + SynergyPref"; + }; + name = Debug; + }; + BC1567C20D186568001991C5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "Synergy Prefs + SynergyPref"; + }; + name = Release; + }; + BC1A68FC085BD178004E0E61 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BC0B9CF30FF401C6007AE543 /* debug-style.xcconfig */; + buildSettings = { + INFOPLIST_PREFIX_HEADER = WOSynergy_Version.h; + }; + name = Debug; + }; + BC1A68FD085BD178004E0E61 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BC0B9CF20FF401C6007AE543 /* release-style.xcconfig */; + buildSettings = { + CODE_SIGNING_IDENTIFIER = org.wincent.synergy; + INFOPLIST_PREFIX_HEADER = WOSynergy_Version.h; + }; + name = Release; + }; + BC6A2A410D149F510080E56B /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BC0B9CF50FF401C6007AE543 /* cocoa-app-target.xcconfig */; + buildSettings = { + INFOPLIST_FILE = SynergyPreferences/Info.plist; + PRODUCT_NAME = "Synergy Preferences"; + }; + name = Debug; + }; + BC6A2A420D149F510080E56B /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BC0B9CF50FF401C6007AE543 /* cocoa-app-target.xcconfig */; + buildSettings = { + INFOPLIST_FILE = SynergyPreferences/Info.plist; + PRODUCT_NAME = "Synergy Preferences"; + }; + name = Release; + }; + BC881F3F0D1AD08D00D969B3 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + PRODUCT_NAME = "Update build number"; + }; + name = Debug; + }; + BC881F400D1AD08D00D969B3 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + PRODUCT_NAME = "Update build number"; + ZERO_LINK = NO; + }; + name = Release; + }; + BC8B9A740D22EC7F00A11CB9 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = Distribution; + }; + name = Debug; + }; + BC8B9A750D22EC7F00A11CB9 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = Distribution; + }; + name = Release; + }; + BCBE22E1093B34BD00FAD628 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BC0B9CF40FF401C6007AE543 /* pref-pane-target.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TARGET_BUILD_DIR)/Synergy Preferences.app/Contents/MacOS/Synergy Preferences"; + GCC_PREPROCESSOR_DEFINITIONS = ( + SYNERGY_PREF_BUILD, + DEBUG, + ); + INFOPLIST_FILE = SynergyPref/Info.plist; + PRODUCT_NAME = Synergy; + }; + name = Debug; + }; + BCBE22E2093B34BD00FAD628 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BC0B9CF40FF401C6007AE543 /* pref-pane-target.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TARGET_BUILD_DIR)/Synergy Preferences.app/Contents/MacOS/Synergy Preferences"; + GCC_PREPROCESSOR_DEFINITIONS = SYNERGY_PREF_BUILD; + INFOPLIST_FILE = SynergyPref/Info.plist; + PRODUCT_NAME = Synergy; + }; + name = Release; + }; + BCBE235C093B34BD00FAD628 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BC0B9CF50FF401C6007AE543 /* cocoa-app-target.xcconfig */; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = "$(SOURCE_ROOT)/SynergyApp"; + GCC_PREPROCESSOR_DEFINITIONS = ( + SYNERGY_APP_BUILD, + DEBUG, + ); + INFOPLIST_FILE = SynergyApp/Info.plist; + OTHER_LDFLAGS = "-lcrypto"; + PRODUCT_NAME = Synergy; + }; + name = Debug; + }; + BCBE235D093B34BD00FAD628 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BC0B9CF50FF401C6007AE543 /* cocoa-app-target.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = "Don't Code Sign"; + FRAMEWORK_SEARCH_PATHS = "$(SOURCE_ROOT)/SynergyApp"; + GCC_PREPROCESSOR_DEFINITIONS = SYNERGY_APP_BUILD; + INFOPLIST_FILE = SynergyApp/Info.plist; + OTHER_LDFLAGS = "-lcrypto"; + PRODUCT_NAME = Synergy; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + BC1567DE0D1865B1001991C5 /* Build configuration list for PBXAggregateTarget "Synergy Prefs + SynergyPref" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BC1567C10D186568001991C5 /* Debug */, + BC1567C20D186568001991C5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BC1A68FB085BD178004E0E61 /* Build configuration list for PBXProject "Synergy" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BC1A68FC085BD178004E0E61 /* Debug */, + BC1A68FD085BD178004E0E61 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BC6A2A430D149F510080E56B /* Build configuration list for PBXNativeTarget "Synergy Preferences" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BC6A2A410D149F510080E56B /* Debug */, + BC6A2A420D149F510080E56B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BC881F500D1AD0B200D969B3 /* Build configuration list for PBXAggregateTarget "Update build number" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BC881F3F0D1AD08D00D969B3 /* Debug */, + BC881F400D1AD08D00D969B3 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BC8B9A860D22ECE600A11CB9 /* Build configuration list for PBXAggregateTarget "Distribution" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BC8B9A740D22EC7F00A11CB9 /* Debug */, + BC8B9A750D22EC7F00A11CB9 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BCBE22E0093B34BD00FAD628 /* Build configuration list for PBXNativeTarget "SynergyPref" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BCBE22E1093B34BD00FAD628 /* Debug */, + BCBE22E2093B34BD00FAD628 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BCBE235B093B34BD00FAD628 /* Build configuration list for PBXNativeTarget "SynergyApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BCBE235C093B34BD00FAD628 /* Debug */, + BCBE235D093B34BD00FAD628 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 089C1669FE841209C02AAC07 /* Project object */; +} diff --git a/SynergyApp/Categories/NSAppleScript+WOAdditions.h b/SynergyApp/Categories/NSAppleScript+WOAdditions.h new file mode 100644 index 0000000..da12b89 --- /dev/null +++ b/SynergyApp/Categories/NSAppleScript+WOAdditions.h @@ -0,0 +1,15 @@ +// +// NSAppleScript+WOAdditions.h +// Synergy +// +// Created by Wincent Colaiuta on 9 June 2006. +// Copyright 2006-2008 Wincent Colaiuta. + +#import + +@interface NSAppleScript (WOAdditions) + +/*! Convenience wrapper for executeAppleEvent:error:. */ +- (NSAppleEventDescriptor *)executeWithParameters:(NSArray *)parameters error:(NSDictionary **)errorInfo; + +@end diff --git a/SynergyApp/Categories/NSAppleScript+WOAdditions.m b/SynergyApp/Categories/NSAppleScript+WOAdditions.m new file mode 100644 index 0000000..d90333c --- /dev/null +++ b/SynergyApp/Categories/NSAppleScript+WOAdditions.m @@ -0,0 +1,35 @@ +// +// NSAppleScript+WOAdditions.m +// Synergy +// +// Created by Wincent Colaiuta on 09/06/06. +// Copyright 2006-2008 Wincent Colaiuta. + +#import "NSAppleScript+WOAdditions.h" + +@implementation NSAppleScript (WOAdditions) + +- (NSAppleEventDescriptor *)executeWithParameters:(NSArray *)parameters error:(NSDictionary **)errorInfo +{ + NSParameterAssert(parameters != nil); + NSAppleEventDescriptor *event = [NSAppleEventDescriptor appleEventWithEventClass:kCoreEventClass + eventID:kAEOpen + targetDescriptor:nil + returnID:kAutoGenerateReturnID + transactionID:kAnyTransactionID]; + NSAppleEventDescriptor *directObject = [NSAppleEventDescriptor listDescriptor]; + for (unsigned int i = 0, max = [parameters count]; i < max; i++) + { + id parameter = [parameters objectAtIndex:i]; + if ([parameter isKindOfClass:[NSAppleEventDescriptor class]]) + [directObject insertDescriptor:parameter atIndex:i]; + else if ([parameter isKindOfClass:[NSString class]]) + [directObject insertDescriptor:[NSAppleEventDescriptor descriptorWithString:parameter] atIndex:i]; + else if ([parameter respondsToSelector:@selector(intValue)]) + [directObject insertDescriptor:[NSAppleEventDescriptor descriptorWithInt32:[parameter intValue]] atIndex:i]; + } + [event setDescriptor:directObject forKeyword:keyDirectObject]; + return [self executeAppleEvent:event error:errorInfo]; +} + +@end diff --git a/SynergyApp/Categories/NSImage+WOAdditions.h b/SynergyApp/Categories/NSImage+WOAdditions.h new file mode 100644 index 0000000..e974fcd --- /dev/null +++ b/SynergyApp/Categories/NSImage+WOAdditions.h @@ -0,0 +1,14 @@ +// +// NSImage+WOAdditions.h +// Synergy +// +// Created by Wincent Colaiuta on 09/06/06. +// Copyright 2006-2008 Wincent Colaiuta. + +#import + +@interface NSImage (WOAdditions) + +- (NSData *)PICTRepresentation; + +@end diff --git a/SynergyApp/Categories/NSImage+WOAdditions.m b/SynergyApp/Categories/NSImage+WOAdditions.m new file mode 100644 index 0000000..14283e6 --- /dev/null +++ b/SynergyApp/Categories/NSImage+WOAdditions.m @@ -0,0 +1,55 @@ +// +// NSImage+WOAdditions.m +// Synergy +// +// Created by Wincent Colaiuta on 09/06/06. +// Copyright 2006-2008 Wincent Colaiuta. + +#import "NSImage+WOAdditions.h" + +#import + +@implementation NSImage (WOAdditions) + +- (NSData *)PICTRepresentation +{ + // try shortcut first + NSEnumerator *enumerator = [[self representations] objectEnumerator]; + id rep = nil; + while ((rep = [enumerator nextObject])) + { + if ([rep respondsToSelector:@selector(PICTRepresentation)]) + return [rep PICTRepresentation]; + } + + // try using QuickTime to perform the conversion + MovieImportComponent importer = OpenDefaultComponent(GraphicsImporterComponentType, kQTFileTypeTIFF); + if (!importer) return nil; + + Handle source = NULL; + PicHandle destination = NULL; + NSData *PICTData = nil; + NSData *TIFFData = [self TIFFRepresentation]; + if (TIFFData) + { + if (PtrToHand([TIFFData bytes], &source, (long)[TIFFData length]) == noErr) // allocates the memory and copies the data + { + // docs: "you must not dispose this handle until the graphics importer has been closed" + if (GraphicsImportSetDataHandle(importer, source) == noErr) + { + if (GraphicsImportGetAsPicture(importer, &destination) == noErr) + { + PICTData = [NSData dataWithBytes:*destination length:(unsigned)GetHandleSize((Handle)destination)]; + DisposeHandle((Handle)destination); + } + } + } + } + CloseComponent(importer); + if (source) + DisposeHandle(source); + + return PICTData; +} + +@end diff --git a/SynergyApp/Categories/NSTimer+WOPausable.h b/SynergyApp/Categories/NSTimer+WOPausable.h new file mode 100644 index 0000000..e2ed828 --- /dev/null +++ b/SynergyApp/Categories/NSTimer+WOPausable.h @@ -0,0 +1,34 @@ +// +// NSTimer+WOPausable.h +// Synergy +// +// Created by Wincent Colaiuta on 7 November 2006. +// Copyright 2006-2008 Wincent Colaiuta. + +#import + +//@interface WOPausableTimer : NSTimer { +@interface NSTimer (WOPausable) + +//! Pauses the receiver. +//! +//! Each pause message increments a counter and each resume message decrements that counter. +//! If the receiver is invalid, has no effect. +//! +- (void)pause; + +//! Resumes the receiver. +//! +//! Each pause message increments a counter and each resume message decrements that counter; when the counter returns to zero the receiver is resumed. +//! If the receiver is invalid, has no effect. +//! +//! \warn the caller must balance pause and receive calls in order to automatically clean up state; otherwise must call the cancel method to manually clean up state +- (void)resume; + +//! Returns YES if the timer is valid but paused, otherwise returns NO. +- (BOOL)isPaused; + +//! If the receiver is valid calls invalidate and then cleans up internal state information; otherwise just cleans up internal state information. +- (void)cancel; + +@end diff --git a/SynergyApp/Categories/NSTimer+WOPausable.m b/SynergyApp/Categories/NSTimer+WOPausable.m new file mode 100644 index 0000000..3811e83 --- /dev/null +++ b/SynergyApp/Categories/NSTimer+WOPausable.m @@ -0,0 +1,111 @@ +// NSTimer+WOPausable.m +// Synergy +// +// Copyright 2006-2009 Wincent Colaiuta. All rights reserved. + +// TODO: move this to WOPublic + +// category header +#import "NSTimer+WOPausable.h" + +// WOPublic headers +#import "WOPublic/WODebugMacros.h" + +// hideous hack required because Apple makes it difficult or impossible to usefully subclass NSTimer +static NSMapTable *WOPauseableTimerIVarOldFireDate = NULL; +static NSMapTable *WOPauseableTimerIVarPauseDate = NULL; +static NSMapTable *WOPauseableTimerIVarPauseStack = NULL; + +@implementation NSTimer (WOPausable) + +#pragma mark - +#pragma mark NSObject overrides + ++ (void)load +{ + static volatile BOOL initializationComplete = NO; + @synchronized([NSTimer class]) + { + if (!initializationComplete) + { + WOPauseableTimerIVarOldFireDate = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 0); + WOPauseableTimerIVarPauseDate = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks, NSObjectMapValueCallBacks, 0); + WOPauseableTimerIVarPauseStack = NSCreateMapTable(NSNonRetainedObjectMapKeyCallBacks, NSIntegerMapValueCallBacks, 0); + initializationComplete = YES; + } + } +} + +#pragma mark - +#pragma mark Custom methods + +- (void)pause +{ + // this is slow because NSMapInsert is not threadsafe, requiring class-wide synchronization + // (a proper NSTimer subclass with instance variables wouldn't need locks) + // although clearly, callers would be unwise to manipulate an NSTimer from multiple threads in any case + if ([self isValid]) + { + @synchronized (WOPauseableTimerIVarPauseStack) + { + int pauseStack = (int)NSMapGet(WOPauseableTimerIVarPauseStack, self) + 1; + NSMapInsert(WOPauseableTimerIVarPauseStack, self, (void *)pauseStack); + if (pauseStack == 1) + { + NSMapInsert(WOPauseableTimerIVarOldFireDate, self, [self fireDate]); + [self setFireDate:[NSDate distantFuture]]; + NSMapInsert(WOPauseableTimerIVarPauseDate, self, [NSDate date]); + } + } + } +} + +- (void)resume +{ + if ([self isValid]) + { + @synchronized (WOPauseableTimerIVarPauseStack) + { + int pauseStack = (int)NSMapGet(WOPauseableTimerIVarPauseStack, self) - 1; + WOAssert(pauseStack >= 0); + NSMapInsert(WOPauseableTimerIVarPauseStack, self, (void *)pauseStack); + if (pauseStack == 0) + { + NSDate *pauseDate = NSMapGet(WOPauseableTimerIVarPauseDate, self); + NSDate *oldFireDate = NSMapGet(WOPauseableTimerIVarOldFireDate, self); + [self setFireDate:[oldFireDate dateByAddingTimeInterval:-[pauseDate timeIntervalSinceNow]]]; + NSMapRemove(WOPauseableTimerIVarOldFireDate, self); + NSMapRemove(WOPauseableTimerIVarPauseDate, self); + NSMapRemove(WOPauseableTimerIVarPauseStack, self); + } + } + } +} + +- (BOOL)isPaused +{ + if ([self isValid]) + { + @synchronized (WOPauseableTimerIVarPauseStack) + { + if ((int)NSMapGet(WOPauseableTimerIVarPauseStack, self) > 0) + return YES; + } + } + return NO; +} + +- (void)cancel +{ + if ([self isValid]) + [self invalidate]; + + @synchronized (WOPauseableTimerIVarPauseStack) + { + NSMapRemove(WOPauseableTimerIVarOldFireDate, self); + NSMapRemove(WOPauseableTimerIVarPauseDate, self); + NSMapRemove(WOPauseableTimerIVarPauseStack, self); + } +} + +@end diff --git a/SynergyApp/Classes/HotkeyCapableApplication.h b/SynergyApp/Classes/HotkeyCapableApplication.h new file mode 100644 index 0000000..d81f6bc --- /dev/null +++ b/SynergyApp/Classes/HotkeyCapableApplication.h @@ -0,0 +1,75 @@ +// +// HotkeyCapableApplication.h +// Synergy +// +// Created by Wincent Colaiuta on Fri Nov 22 2002. +// Copyright 2002-2008 Wincent Colaiuta. + +#import +#import + +@class WOPreferences, WOButtonState; + +@interface HotkeyCapableApplication : NSApplication { + + // pointer to a WOPreferences object + WOPreferences *synergyPreferences; + + // keycodes for which to trap (these come from preferences) + UInt32 quitCode; + UInt32 playCode; + UInt32 prevCode; + UInt32 nextCode; + UInt32 showHideCode; + UInt32 volumeUpCode; + UInt32 volumeDownCode; + UInt32 showHideFloaterCode; + UInt32 rateAs0Code; + UInt32 rateAs1Code; + UInt32 rateAs2Code; + UInt32 rateAs3Code; + UInt32 rateAs4Code; + UInt32 rateAs5Code; + UInt32 toggleMuteCode; + UInt32 toggleShuffleCode; + UInt32 setRepeatModeCode; + UInt32 activateITunesCode; + + UInt32 increaseRatingCode; + UInt32 decreaseRatingCode; + + // for identifying "fast forward" (press+hold "next" hot key) operation + WOButtonState *fastForward; + + // and for "rewind" + WOButtonState *rewind; + + // and for "volume up" + WOButtonState *volumeUp; + + // and for "volume down" + WOButtonState *volumeDown; + + NSView *newNextResponder; + + // remember whether hot keys have actually been registered + BOOL _hotkeysRegistered; +} + +// We only define a few methods here, all related to management of global +// hot-keys (most of the real work is done in SynergyController). + +- (void)sendEvent:(NSEvent *)theEvent; + +- (void)bringSelfToFront; + +- (void)closeDown; + +- (void)closeDownWithConfirmation; + +// stuff moved out of main SynergyController class +- (void)handleEvent:(NSEvent*)theEvent; +- (void)registerHotkeys; +- (void)unregisterHotkeys; + +@end diff --git a/SynergyApp/Classes/HotkeyCapableApplication.m b/SynergyApp/Classes/HotkeyCapableApplication.m new file mode 100644 index 0000000..461ae39 --- /dev/null +++ b/SynergyApp/Classes/HotkeyCapableApplication.m @@ -0,0 +1,709 @@ +// HotkeyCapableApplication.m +// Synergy +// +// Copyright 2002-2010 Wincent Colaiuta. All rights reserved. + +#import + +// In the future, will re-impliment this idea as a WOApplication class which +// allows you to set a delegate for handling hot key events. +// +// ie. you make a main controller class, but you set WOApplication as your +// NSPrincipalClass... from the controller, you set the controller as the delegate +// and perhaps I can make a WOHotKeyHandler class to encapsulate each hot key +// (or something similar) +// you can exit the app by just calling (from anywhere) +// [[WOApplication sharedApplication] terminate]; +// +// with this system the WOApplication class will provide equivalent +// functionality to the present system, but without the need to ever edit this +// file + +#import "WODebug.h" +#import "WOPopUpButton.h" +#import "WOPreferences.h" +#import "HotkeyCapableApplication.h" +#import "SynergyController.h" +#import "WOButtonState.h" +#import "WOSynergyGlobal.h" + +// We subclass NSApplication so that we can use Carbon calls to intercept global hot-key events +@implementation HotkeyCapableApplication + +// Method for registering and intercepting global hot-key events (Carbon) +// based on http://www.unsanity.org/archives/000045.php + +enum { + // NSEvent subtypes for hotkey events (undocumented). + kEventHotKeyPressedSubtype = 6, + kEventHotKeyReleasedSubtype = 9, +}; + +// move this stuff somewhere more sensible: +EventHotKeyRef quitHotKeyRef; +EventHotKeyRef playHotKeyRef; +EventHotKeyRef prevHotKeyRef; +EventHotKeyRef nextHotKeyRef; +EventHotKeyRef showHideHotKeyRef; +EventHotKeyRef volumeUpHotKeyRef; +EventHotKeyRef volumeDownHotKeyRef; +EventHotKeyRef showHideFloaterHotKeyRef; + +EventHotKeyRef rateAs0HotKeyRef; +EventHotKeyRef rateAs1HotKeyRef; +EventHotKeyRef rateAs2HotKeyRef; +EventHotKeyRef rateAs3HotKeyRef; +EventHotKeyRef rateAs4HotKeyRef; +EventHotKeyRef rateAs5HotKeyRef; + +EventHotKeyRef toggleMuteHotKeyRef; +EventHotKeyRef toggleShuffleHotKeyRef; +EventHotKeyRef setRepeatModeHotKeyRef; + +EventHotKeyRef activateITunesHotKeyRef; + +EventHotKeyRef increaseRatingHotKeyRef; +EventHotKeyRef decreaseRatingHotKeyRef; + +// other classes can call [NSApp respondsToSelector:@selector(isSynergyApp)] +// to find out if running from app or from prefPane +- (BOOL)isSynergyApp +{ + return YES; +} + +// we are not registering the hotkeys... where to do it? we only want to do it once the buttons +// are in view... +// do it here for now... in future will have to wait for Synergy.app to launch +- (void) awakeFromNib +{ + synergyPreferences = [WOPreferences sharedInstance]; + + // ensure these are a "safe" values + fastForward = nil; + rewind = nil; + volumeUp = nil; + volumeDown = nil; + newNextResponder = nil; + + [self registerHotkeys]; + + // if the user presses a hot key before SynergyController has set itself up, then + // I don't know what will happen.... + // eg. it could do a [mainTimer fire] before mainTimer has been set up... + // or it could fail to even make the call +} + + +- (void)sendEvent:(NSEvent *)theEvent +{ + NSEventType eventType = [theEvent type]; + if (eventType == NSSystemDefined) + { + short subtype = [theEvent subtype]; + if (subtype == kEventHotKeyPressedSubtype || + subtype == kEventHotKeyReleasedSubtype) + { + [self handleEvent:theEvent]; // a hot key has been pressed (or released) + [super sendEvent:theEvent]; // we were swallowing these events (oops) up until 1.0a8! + } + } + + // special case -- work around for apparent bug which prevents NSRightMouseDown from getting transmitted to our NSStatusItem + if ([[self nextResponder] isKindOfClass:[WOPopUpButton class]] && + eventType == NSRightMouseDown) + { + // dispatch the event directly + WOPopUpButton *responderView = (WOPopUpButton *)[self nextResponder]; + + // must use [NSEvent mouseLocation] because [theEvent window] can return nil and therefore cannot be used for calculations + NSPoint globalMouseLoc = [NSEvent mouseLocation]; + NSPoint internalMouseLoc = [[responderView window] convertScreenToBase:globalMouseLoc]; + NSRect responderBounds = [responderView frame]; + + if(NSMouseInRect(internalMouseLoc, responderBounds, NO)) + [[self nextResponder] rightMouseDown:theEvent]; + } + else + // pass the event through the usual channels + [super sendEvent:theEvent]; +} + +- (void)closeDown +{ + [[SynergyController sharedInstance] cleanupBeforeExit]; // have to cleanup manually + exit(0); // nasty +} + +- (void)bringSelfToFront +{ + ProcessSerialNumber myPID; + OSErr errCode = GetCurrentProcess(&myPID); + if (errCode == noErr) + (void)SetFrontProcess(&myPID); + else + ELOG(@"Unable to determine current process ID"); +} + +/* + Need to add a check in here to ensure Synergy.prefPane isn't being displayed, because we will + behave differently if it is: + -- if Synergy.prefPane is running -- we can display dialog and quit, BUT on quit tell prefPane + to update its stop/start button + -- if it is not running, just quit as normal + */ +- (void) closeDownWithConfirmation +{ + // only display this if we are quitting due to a hotkey press + NSString *title = NSLocalizedString(@"Are you sure you want to quit Synergy?",@"Ask for confirmation before quitting"); + NSString *message = NSLocalizedString(@"If you quit Synergy its iTunes control buttons will be removed from the menubar.",@"Quit confirmation explanation"); + NSString *defaultButton = NSLocalizedString(@"Quit",@"Quit button"); + NSString *alternateButton = NSLocalizedString(@"Cancel",@"Cancel button"); + NSString *otherButton = nil; + + + // find out frontmost process + ProcessSerialNumber frontPID; + OSErr errCode = GetFrontProcess(&frontPID); + + [self bringSelfToFront]; + + int status = NSRunAlertPanel(title, message, defaultButton, alternateButton, otherButton); + + // restore frontmost process + if (errCode == noErr) + (void)SetFrontProcess(&frontPID); + else + ELOG(@"Unable to determine frontmost process ID"); + + if ( status == NSAlertDefaultReturn ) + // trying to clean up another object's garbage, which is a no-no in Cocoa... bad practice. + [self closeDown]; +} + + +- (void)handleEvent:(NSEvent*)theEvent +{ + // we get in here on keydown, not keyup + if ([[synergyPreferences objectOnDiskForKey:_woGlobalHotkeysPrefKey] boolValue] == NO) + return; // global hotkey handling is turned off, so return immediately + else + { + EventHotKeyRef hotKeyRef; + hotKeyRef= (EventHotKeyRef)[theEvent data1]; //data1 is our hot key ref + + // differentiate between hot key press and hot key release for certain + // events where a hold-operation will have a special effect + // eg. prev/next held down = rewind/fast forward + // vol up/down held down = continual volume up/down + // rating up/down held down = continual rating increase/decrease + short subtype = [theEvent subtype]; + if (subtype == kEventHotKeyPressedSubtype) + { + // all these get handled the standard way + if (hotKeyRef != nil ) + { + if (hotKeyRef == quitHotKeyRef) + { + // unregister the hotkeys to prevent their use while displaying the quit confirmation dialog + [self unregisterHotkeys]; + [self closeDownWithConfirmation]; + [self registerHotkeys]; + } + else if (hotKeyRef == playHotKeyRef) + [[SynergyController sharedInstance] playPauseHotKeyPressed]; + else if (hotKeyRef == prevHotKeyRef) + rewind = [[WOButtonState alloc] initWithTarget:[SynergyController sharedInstance] + selector:@selector(rewindHotKeyPressed)]; + else if (hotKeyRef == nextHotKeyRef) + fastForward = [[WOButtonState alloc] initWithTarget:[SynergyController sharedInstance] + selector:@selector(fastForwardHotKeyPressed)]; + else if (hotKeyRef == showHideHotKeyRef) + [[SynergyController sharedInstance] showHideHotKeyPressed]; + else if (hotKeyRef == volumeUpHotKeyRef) + [[SynergyController sharedInstance] volumeUpHotKeyPressed]; + else if (hotKeyRef == volumeDownHotKeyRef) + [[SynergyController sharedInstance] volumeDownHotKeyPressed]; + else if (hotKeyRef == showHideFloaterHotKeyRef) + [[SynergyController sharedInstance] showHideFloaterHotKeyPressed]; + else if (hotKeyRef == rateAs0HotKeyRef) + [[SynergyController sharedInstance] rateAs0HotKeyPressed]; + else if (hotKeyRef == rateAs1HotKeyRef) + [[SynergyController sharedInstance] rateAs1HotKeyPressed]; + else if (hotKeyRef == rateAs2HotKeyRef) + [[SynergyController sharedInstance] rateAs2HotKeyPressed]; + else if (hotKeyRef == rateAs3HotKeyRef) + [[SynergyController sharedInstance] rateAs3HotKeyPressed]; + else if (hotKeyRef == rateAs4HotKeyRef) + [[SynergyController sharedInstance] rateAs4HotKeyPressed]; + else if (hotKeyRef == rateAs5HotKeyRef) + [[SynergyController sharedInstance] rateAs5HotKeyPressed]; + else if (hotKeyRef == toggleMuteHotKeyRef) + [[SynergyController sharedInstance] toggleMuteHotKeyPressed]; + else if (hotKeyRef == toggleShuffleHotKeyRef) + [[SynergyController sharedInstance] toggleShuffleHotKeyPressed]; + else if (hotKeyRef == setRepeatModeHotKeyRef) + [[SynergyController sharedInstance] setRepeatModeHotKeyPressed]; + else if (hotKeyRef == activateITunesHotKeyRef) + [[SynergyController sharedInstance] activateITunesHotKeyPressed]; + else if (hotKeyRef == increaseRatingHotKeyRef) + [[SynergyController sharedInstance] increaseRatingHotKeyPressed]; + else if (hotKeyRef == decreaseRatingHotKeyRef) + [[SynergyController sharedInstance] decreaseRatingHotKeyPressed]; + else + ELOG(@"Unknown HotKeyRef received."); // shouldn't happen + } + } + else if (subtype == kEventHotKeyReleasedSubtype) + { + // special cases here (items which behave differently on hot key release) + + if (hotKeyRef != nil ) + { + if (hotKeyRef == prevHotKeyRef) + { + // make sure we have a WOButtonState object + if (rewind) + { + // "prev" hot key released... check to see if timer expired + if ([rewind timerRunning]) + { + // timer still running, so this isn't a "click+hold" + [rewind cancelTimer]; + + // tell iTunes to go to prev track + [[SynergyController sharedInstance] prevHotKeyPressed]; + } + else + { + // it was a "click+hold", so tell iTunes to resume + [[SynergyController sharedInstance] rewindHotKeyReleased]; + } + + // clean up WOButtonState object + rewind = nil; + } + } + else if (hotKeyRef == nextHotKeyRef) + { + // make sure we have a WOButtonState object + if (fastForward) + { + // "next" hot key released... check to see if timer expired + if ([fastForward timerRunning]) + { + // timer still running, so this isn't a "click+hold" + [fastForward cancelTimer]; + + // tell iTunes to go to next track + [[SynergyController sharedInstance] nextHotKeyPressed]; + } + else + { + // it was a "click+hold", so tell iTunes to resume + [[SynergyController sharedInstance] fastForwardHotKeyReleased]; + } + + // clean up WOButtonState object + fastForward = nil; + } + } + } + } + } +} + +- (void) registerHotkeys +{ + // we've been asked to register the hot keys + if ([[synergyPreferences objectOnDiskForKey:_woGlobalHotkeysPrefKey] boolValue] == NO) + return; // do nothing... + + // else, note that we have registered them... + _hotkeysRegistered = YES; + + unsigned int quitModifier = [[synergyPreferences objectOnDiskForKey:_woQuitModifierPrefKey] unsignedIntValue]; + unsigned int playModifier = [[synergyPreferences objectOnDiskForKey:_woPlayModifierPrefKey] unsignedIntValue]; + unsigned int prevModifier = [[synergyPreferences objectOnDiskForKey:_woPrevModifierPrefKey] unsignedIntValue]; + unsigned int nextModifier = [[synergyPreferences objectOnDiskForKey:_woNextModifierPrefKey] unsignedIntValue]; + unsigned int showHideModifier = [[synergyPreferences objectOnDiskForKey:_woShowHideModifierPrefKey] unsignedIntValue]; + unsigned int volumeUpModifier = [[synergyPreferences objectOnDiskForKey:_woVolumeUpModifierPrefKey] unsignedIntValue]; + unsigned int volumeDownModifier = [[synergyPreferences objectOnDiskForKey:_woVolumeDownModifierPrefKey] unsignedIntValue]; + unsigned int showHideFloaterModifier = [[synergyPreferences objectOnDiskForKey:_woShowHideFloaterModifierPrefKey] unsignedIntValue]; + unsigned int rateAs0Modifier = [[synergyPreferences objectOnDiskForKey:_woRateAs0ModifierPrefKey] unsignedIntValue]; + unsigned int rateAs1Modifier = [[synergyPreferences objectOnDiskForKey:_woRateAs1ModifierPrefKey] unsignedIntValue]; + unsigned int rateAs2Modifier = [[synergyPreferences objectOnDiskForKey:_woRateAs2ModifierPrefKey] unsignedIntValue]; + unsigned int rateAs3Modifier = [[synergyPreferences objectOnDiskForKey:_woRateAs3ModifierPrefKey] unsignedIntValue]; + unsigned int rateAs4Modifier = [[synergyPreferences objectOnDiskForKey:_woRateAs4ModifierPrefKey] unsignedIntValue]; + unsigned int rateAs5Modifier = [[synergyPreferences objectOnDiskForKey:_woRateAs5ModifierPrefKey] unsignedIntValue]; + unsigned int toggleMuteModifier = [[synergyPreferences objectOnDiskForKey:_woToggleMuteModifierPrefKey] unsignedIntValue]; + unsigned int toggleShuffleModifier = [[synergyPreferences objectOnDiskForKey:_woToggleShuffleModifierPrefKey] unsignedIntValue]; + unsigned int setRepeatModeModifier = [[synergyPreferences objectOnDiskForKey:_woSetRepeatModeModifierPrefKey] unsignedIntValue]; + unsigned int activateITunesModifier = [[synergyPreferences objectOnDiskForKey:_woActivateITunesModifierPrefKey] unsignedIntValue]; + unsigned int increaseRatingModifier = [[synergyPreferences objectOnDiskForKey:_woIncreaseRatingModifierPrefKey] unsignedIntValue]; + unsigned int decreaseRatingModifier = [[synergyPreferences objectOnDiskForKey:_woDecreaseRatingModifierPrefKey] unsignedIntValue]; + + quitCode = [[synergyPreferences objectOnDiskForKey:_woQuitKeycodePrefKey] intValue]; + playCode = [[synergyPreferences objectOnDiskForKey:_woPlayKeycodePrefKey] intValue]; + prevCode = [[synergyPreferences objectOnDiskForKey:_woPrevKeycodePrefKey] intValue]; + nextCode = [[synergyPreferences objectOnDiskForKey:_woNextKeycodePrefKey] intValue]; + showHideCode = [[synergyPreferences objectOnDiskForKey:_woShowHideKeycodePrefKey] intValue]; + volumeUpCode = [[synergyPreferences objectOnDiskForKey:_woVolumeUpKeycodePrefKey] intValue]; + volumeDownCode = [[synergyPreferences objectOnDiskForKey:_woVolumeDownKeycodePrefKey] intValue]; + showHideFloaterCode = [[synergyPreferences objectOnDiskForKey:_woShowHideFloaterKeycodePrefKey] intValue]; + rateAs0Code = [[synergyPreferences objectOnDiskForKey:_woRateAs0KeycodePrefKey] intValue]; + rateAs1Code = [[synergyPreferences objectOnDiskForKey:_woRateAs1KeycodePrefKey] intValue]; + rateAs2Code = [[synergyPreferences objectOnDiskForKey:_woRateAs2KeycodePrefKey] intValue]; + rateAs3Code = [[synergyPreferences objectOnDiskForKey:_woRateAs3KeycodePrefKey] intValue]; + rateAs4Code = [[synergyPreferences objectOnDiskForKey:_woRateAs4KeycodePrefKey] intValue]; + rateAs5Code = [[synergyPreferences objectOnDiskForKey:_woRateAs5KeycodePrefKey] intValue]; + toggleMuteCode = [[synergyPreferences objectOnDiskForKey:_woToggleMuteKeycodePrefKey] intValue]; + toggleShuffleCode = [[synergyPreferences objectOnDiskForKey:_woToggleShuffleKeycodePrefKey] intValue]; + setRepeatModeCode = [[synergyPreferences objectOnDiskForKey:_woSetRepeatModeKeycodePrefKey] intValue]; + activateITunesCode = [[synergyPreferences objectOnDiskForKey:_woActivateITunesKeycodePrefKey] intValue]; + increaseRatingCode = [[synergyPreferences objectOnDiskForKey:_woIncreaseRatingKeycodePrefKey] intValue]; + decreaseRatingCode = [[synergyPreferences objectOnDiskForKey:_woDecreaseRatingKeycodePrefKey] intValue]; + + // prepare the parameters so that we can register our hotkey + const UInt32 mySignature = synergyAppSignature; + + // Panther fix: API has changed and now allows "0" for modifier and keycode + if (/*(quitCode !=0) && */(quitModifier !=0)) + { + UInt32 quitKeyCode=quitCode; // F10 is default... 109 + EventHotKeyID quitHotKeyID; + quitHotKeyID.signature = mySignature; // (OSType) + quitHotKeyID.id = 1; // (UInt32) + //EventHotKeyRef quitHotKeyRef; + //then register it + RegisterEventHotKey(quitKeyCode, + quitModifier, + quitHotKeyID, + GetApplicationEventTarget(), + 0, + &quitHotKeyRef); + } + + // Panther fix: API has changed and now allows "0" for modifier and keycode + if (/*playCode !=0) && */(playModifier !=0)) + { + UInt32 playKeyCode=playCode; + EventHotKeyID playHotKeyID; + playHotKeyID.signature = mySignature; + playHotKeyID.id = 2; + //EventHotKeyRef playHotKeyRef; + RegisterEventHotKey(playKeyCode, + playModifier, + playHotKeyID, + GetApplicationEventTarget(), + 0, + &playHotKeyRef); + } + + // Panther fix: API has changed and now allows "0" for modifier and keycode + if (/*prevCode !=0) && */(prevModifier !=0)) + { + UInt32 prevKeyCode=prevCode; + EventHotKeyID prevHotKeyID; + prevHotKeyID.signature = mySignature; + prevHotKeyID.id = 3; + //EventHotKeyRef prevHotKeyRef; + RegisterEventHotKey(prevKeyCode, + prevModifier, + prevHotKeyID, + GetApplicationEventTarget(), + 0, + &prevHotKeyRef); + } + + // Panther fix: API has changed and now allows "0" for modifier and keycode + if (/*nextCode !=0) && */(nextModifier !=0)) + { + UInt32 nextKeyCode=nextCode; + EventHotKeyID nextHotKeyID; + nextHotKeyID.signature = mySignature; + nextHotKeyID.id = 4; + //EventHotKeyRef nextHotKeyRef;localize + RegisterEventHotKey(nextKeyCode, + nextModifier, + nextHotKeyID, + GetApplicationEventTarget(), + 0, + &nextHotKeyRef); + } + + // Panther fix: API has changed and now allows "0" for modifier and keycode + if (/*showHideCode !=0) && */(showHideModifier !=0)) + { + UInt32 showHideKeyCode=showHideCode; + EventHotKeyID showHideHotKeyID; + showHideHotKeyID.signature = mySignature; + showHideHotKeyID.id = 5; + RegisterEventHotKey(showHideKeyCode, + showHideModifier, + showHideHotKeyID, + GetApplicationEventTarget(), + 0, + &showHideHotKeyRef); + } + + // Panther fix: API has changed and now allows "0" for modifier and keycode + if (/*volumeUpCode !=0) && */(volumeUpModifier !=0)) + { + UInt32 volumeUpKeyCode = volumeUpCode; + EventHotKeyID volumeUpHotKeyID; + volumeUpHotKeyID.signature = mySignature; + volumeUpHotKeyID.id = 6; + RegisterEventHotKey(volumeUpKeyCode, + volumeUpModifier, + volumeUpHotKeyID, + GetApplicationEventTarget(), + 0, + &volumeUpHotKeyRef); + } + + // Panther fix: API has changed and now allows "0" for modifier and keycode + if (/*volumeDownCode !=0) && */(volumeDownModifier !=0)) + { + UInt32 volumeDownKeyCode = volumeDownCode; + EventHotKeyID volumeDownHotKeyID; + volumeDownHotKeyID.signature = mySignature; + volumeDownHotKeyID.id = 7; + RegisterEventHotKey(volumeDownKeyCode, + volumeDownModifier, + volumeDownHotKeyID, + GetApplicationEventTarget(), + 0, + &volumeDownHotKeyRef); + } + + // Panther fix: API has changed and now allows "0" for modifier and keycode + if (/*showHideFloaterCode !=0) && */(showHideFloaterModifier !=0)) + { + UInt32 showHideFloaterKeyCode=showHideFloaterCode; + EventHotKeyID showHideFloaterHotKeyID; + showHideFloaterHotKeyID.signature = mySignature; + showHideFloaterHotKeyID.id = 8; + RegisterEventHotKey(showHideFloaterKeyCode, + showHideFloaterModifier, + showHideFloaterHotKeyID, + GetApplicationEventTarget(), + 0, + &showHideFloaterHotKeyRef); + } + + // Panther fix: API has changed and now allows "0" for modifier and keycode + if (/*rateAs1Code !=0) && */(rateAs1Modifier !=0)) + { + UInt32 rateAs1KeyCode = rateAs1Code; + EventHotKeyID rateAs1HotKeyID; + rateAs1HotKeyID.signature = mySignature; + rateAs1HotKeyID.id = 9; + RegisterEventHotKey(rateAs1KeyCode, + rateAs1Modifier, + rateAs1HotKeyID, + GetApplicationEventTarget(), + 0, + &rateAs1HotKeyRef); + } + + // Panther fix: API has changed and now allows "0" for modifier and keycode + if (/*rateAs2Code !=0) && */(rateAs2Modifier !=0)) + { + UInt32 rateAs2KeyCode = rateAs2Code; + EventHotKeyID rateAs2HotKeyID; + rateAs2HotKeyID.signature = mySignature; + rateAs2HotKeyID.id = 10; + RegisterEventHotKey(rateAs2KeyCode, + rateAs2Modifier, + rateAs2HotKeyID, + GetApplicationEventTarget(), + 0, + &rateAs2HotKeyRef); + } + + // Panther fix: API has changed and now allows "0" for modifier and keycode + if (/*rateAs3Code !=0) && */(rateAs3Modifier !=0)) + { + UInt32 rateAs3KeyCode = rateAs3Code; + EventHotKeyID rateAs3HotKeyID; + rateAs3HotKeyID.signature = mySignature; + rateAs3HotKeyID.id = 11; + RegisterEventHotKey(rateAs3KeyCode, + rateAs3Modifier, + rateAs3HotKeyID, + GetApplicationEventTarget(), + 0, + &rateAs3HotKeyRef); + } + + // Panther fix: API has changed and now allows "0" for modifier and keycode + if (/*rateAs4Code !=0) && */(rateAs4Modifier !=0)) + { + UInt32 rateAs4KeyCode = rateAs4Code; + EventHotKeyID rateAs4HotKeyID; + rateAs4HotKeyID.signature = mySignature; + rateAs4HotKeyID.id = 12; + RegisterEventHotKey(rateAs4KeyCode, + rateAs4Modifier, + rateAs4HotKeyID, + GetApplicationEventTarget(), + 0, + &rateAs4HotKeyRef); + } + + // Panther fix: API has changed and now allows "0" for modifier and keycode + if (/*rateAs5Code !=0) && */(rateAs5Modifier !=0)) + { + UInt32 rateAs5KeyCode = rateAs5Code; + EventHotKeyID rateAs5HotKeyID; + rateAs5HotKeyID.signature = mySignature; + rateAs5HotKeyID.id = 13; + RegisterEventHotKey(rateAs5KeyCode, + rateAs5Modifier, + rateAs5HotKeyID, + GetApplicationEventTarget(), + 0, + &rateAs5HotKeyRef); + } + + // Panther fix: API has changed and now allows "0" for modifier and keycode + if (/*rateAs0Code !=0) && */(rateAs0Modifier !=0)) + { + UInt32 rateAs0KeyCode = rateAs0Code; + EventHotKeyID rateAs0HotKeyID; + rateAs0HotKeyID.signature = mySignature; + rateAs0HotKeyID.id = 14; + RegisterEventHotKey(rateAs0KeyCode, + rateAs0Modifier, + rateAs0HotKeyID, + GetApplicationEventTarget(), + 0, + &rateAs0HotKeyRef); + } + + // Panther fix: API has changed and now allows "0" for modifier and keycode + if (/*toggleMuteCode !=0) && */(toggleMuteModifier !=0)) + { + UInt32 toggleMuteKeyCode = toggleMuteCode; + EventHotKeyID toggleMuteHotKeyID; + toggleMuteHotKeyID.signature = mySignature; + toggleMuteHotKeyID.id = 15; + RegisterEventHotKey(toggleMuteKeyCode, + toggleMuteModifier, + toggleMuteHotKeyID, + GetApplicationEventTarget(), + 0, + &toggleMuteHotKeyRef); + } + + // Panther fix: API has changed and now allows "0" for modifier and keycode + if (/*toggleShuffleCode !=0) && */(toggleShuffleModifier !=0)) + { + UInt32 toggleShuffleKeyCode = toggleShuffleCode; + EventHotKeyID toggleShuffleHotKeyID; + toggleShuffleHotKeyID.signature = mySignature; + toggleShuffleHotKeyID.id = 16; + RegisterEventHotKey(toggleShuffleKeyCode, + toggleShuffleModifier, + toggleShuffleHotKeyID, + GetApplicationEventTarget(), + 0, + &toggleShuffleHotKeyRef); + } + + // Panther fix: API has changed and now allows "0" for modifier and keycode + if (/*setRepeatModeCode !=0) && */(setRepeatModeModifier !=0)) + { + UInt32 setRepeatModeKeyCode = setRepeatModeCode; + EventHotKeyID setRepeatModeHotKeyID; + setRepeatModeHotKeyID.signature = mySignature; + setRepeatModeHotKeyID.id = 17; + RegisterEventHotKey(setRepeatModeKeyCode, + setRepeatModeModifier, + setRepeatModeHotKeyID, + GetApplicationEventTarget(), + 0, + &setRepeatModeHotKeyRef); + } + + // Panther fix: API has changed and now allows "0" for modifier and keycode + if (/*activateITunesCode !=0) && */(activateITunesModifier !=0)) + { + UInt32 activateITunesKeyCode = activateITunesCode; + EventHotKeyID activateITunesHotKeyID; + activateITunesHotKeyID.signature = mySignature; + activateITunesHotKeyID.id = 18; + RegisterEventHotKey(activateITunesKeyCode, + activateITunesModifier, + activateITunesHotKeyID, + GetApplicationEventTarget(), + 0, + &activateITunesHotKeyRef); + } + + // Panther fix: API has changed and now allows "0" for modifier and keycode + if (/*increaseRatingCode !=0) && */(increaseRatingModifier !=0)) + { + UInt32 increaseRatingKeyCode = increaseRatingCode; + EventHotKeyID increaseRatingHotKeyID; + increaseRatingHotKeyID.signature = mySignature; + increaseRatingHotKeyID.id = 19; + RegisterEventHotKey(increaseRatingKeyCode, + increaseRatingModifier, + increaseRatingHotKeyID, + GetApplicationEventTarget(), + 0, + &increaseRatingHotKeyRef); + } + + // Panther fix: API has changed and now allows "0" for modifier and keycode + if (/*decreaseRatingCode !=0) && */(decreaseRatingModifier !=0)) + { + UInt32 decreaseRatingKeyCode = decreaseRatingCode; + EventHotKeyID decreaseRatingHotKeyID; + decreaseRatingHotKeyID.signature = mySignature; + decreaseRatingHotKeyID.id = 20; + RegisterEventHotKey(decreaseRatingKeyCode, + decreaseRatingModifier, + decreaseRatingHotKeyID, + GetApplicationEventTarget(), + 0, + &decreaseRatingHotKeyRef); + } +} + +- (void) unregisterHotkeys +{ + // Consequence of new Panther code: here we might be trying to unregister + // keys that were never registered + + // only unregister the keys if we registered them in the first place + if (!_hotkeysRegistered) + return; + + UnregisterEventHotKey(quitHotKeyRef); + UnregisterEventHotKey(playHotKeyRef); + UnregisterEventHotKey(prevHotKeyRef); + UnregisterEventHotKey(nextHotKeyRef); + UnregisterEventHotKey(showHideHotKeyRef); + UnregisterEventHotKey(volumeUpHotKeyRef); + UnregisterEventHotKey(volumeDownHotKeyRef); + UnregisterEventHotKey(showHideFloaterHotKeyRef); + UnregisterEventHotKey(rateAs0HotKeyRef); + UnregisterEventHotKey(rateAs1HotKeyRef); + UnregisterEventHotKey(rateAs2HotKeyRef); + UnregisterEventHotKey(rateAs3HotKeyRef); + UnregisterEventHotKey(rateAs4HotKeyRef); + UnregisterEventHotKey(rateAs5HotKeyRef); + UnregisterEventHotKey(toggleMuteHotKeyRef); + UnregisterEventHotKey(toggleShuffleHotKeyRef); + UnregisterEventHotKey(setRepeatModeHotKeyRef); + UnregisterEventHotKey(activateITunesHotKeyRef); + UnregisterEventHotKey(increaseRatingHotKeyRef); + UnregisterEventHotKey(decreaseRatingHotKeyRef); + + _hotkeysRegistered = NO; +} + +@end diff --git a/SynergyApp/Classes/SynergyController+WOAudioscrobbler.h b/SynergyApp/Classes/SynergyController+WOAudioscrobbler.h new file mode 100644 index 0000000..6b232bf --- /dev/null +++ b/SynergyApp/Classes/SynergyController+WOAudioscrobbler.h @@ -0,0 +1,44 @@ +// +// SynergyController+WOAudioscrobbler.h +// Synergy +// +// Created by Wincent Colaiuta on 6 November 2006. +// Copyright 2006-2008 Wincent Colaiuta. + +#import +#import "SynergyController.h" + +//! \name Timer user info dictionary keys +//! Key names used in the Audioscrobbler timer user info dictionary +//! \startgroup + +#define WO_AUDIOSCROBBLER_TRACK @"WOAudioscrobblerTrack" +#define WO_AUDIOSCROBBLER_ARTIST @"WOAudioscrobblerArtist" +#define WO_AUDIOSCROBBLER_ALBUM @"WOAudioscrobblerAlbum" +#define WO_AUDIOSCROBBLER_LENGTH @"WOAudioscrobblerLength" + +//! \endgroup + +// It's possible that this stuff probably all belongs in the darn WOAudioscrobbler class, but leave it here just for now. I wanted to keep a separation between the SynergyController (which talks to iTunes) and the WOAudioscobbler object (which submits to Audioscrobbler and should probably be called WOAudioscrobblerSubmitter; not sure: the name WOAudioscrobbler implies that it is an abstraction for Audioscrobbler itself). These methods are in a category to minimize pollution of the main controller (keep instance variables and methods out of the main controller). +@interface SynergyController (WOAudioscrobbler) + +- (void)audioscrobblerReadPreferences; + +- (BOOL)audioscrobblerEnabled; +- (void)audioscrobblerUpdate:(BOOL)newState; +- (void)audioscrobblerEnable; +- (void)audioscrobblerDisable; + +// called whenever iTunes posts a notification and conditions for potential track submission are met +- (void)audioscrobblerUpdateWithSong:(NSString *)track artist:(NSString *)artist album:(NSString *)album length:(unsigned)length; +- (void)audioscrobblerSetUpSubmissionTimer:(NSDictionary *)info length:(unsigned)length; + +// called when iTunes posts a notification and conditions are not met +- (void)audioscrobblerCurrentTrackIsTooShort; +- (void)audioscrobblerCurrentTrackIsNotRegularFile; +- (void)audioscrobblerNotPlaying:(NSString *)track artist:(NSString *)artist album:(NSString *)album length:(unsigned)length; + +// called whenever user skips within a track +- (void)audioscrobblerUserDidSkip; + +@end diff --git a/SynergyApp/Classes/SynergyController+WOAudioscrobbler.m b/SynergyApp/Classes/SynergyController+WOAudioscrobbler.m new file mode 100644 index 0000000..f4387db --- /dev/null +++ b/SynergyApp/Classes/SynergyController+WOAudioscrobbler.m @@ -0,0 +1,302 @@ +// SynergyController+WOAudioscrobbler.m +// Synergy +// +// Copyright 2006-2009 Wincent Colaiuta. All rights reserved. + +// classs header +#import "SynergyController+WOAudioscrobbler.h" + +// other headers +#import "WOAudioscrobblerController.h" +#import "WOAudioscrobbler.h" +#import "WOProcessManager.h" +#import "NSTimer+WOPausable.h" +#import "WOPreferences.h" +#import "WODebug.h" + +//! The number of seconds of "fuzziness" used when checking to see if the user has seeked within a track +#define WO_AUDIOSCROBBLER_SKIP_CHECK_RESOLUTION 5 + +// preferences key +NSString *WOSynergyPrefEnableLastFm = @"enableLastFm"; + +// some preferences are stored under the SynergyPreferences domain so that the +// preferences app can benefit from Cocoa Bindings +NSString *WOSynergyPreferencesAppBundleId = @"com.wincent.SynergyPreferences"; + +BOOL lastFmEnabled; + +// could make these instance variables, but for now leave them as file-scoped globals +NSTimer *audioscrobblerTimer; + +// which track are we monitoring? +NSMutableDictionary *audioscrobblerCurrentTrack; + +@implementation SynergyController (WOAudioscrobbler) + ++ (void)initialize +{ + Boolean keyExistsAndHasValidFormat; + Boolean enableLastFm = CFPreferencesGetAppBooleanValue((CFStringRef)WOSynergyPrefEnableLastFm, + (CFStringRef)WOSynergyPreferencesAppBundleId, + &keyExistsAndHasValidFormat); + lastFmEnabled = !(keyExistsAndHasValidFormat && !enableLastFm); +} + +- (void)audioscrobblerTimerFired:(NSTimer *)aTimer +{ + WOAudioscrobblerLog(@"Submission timer fired; will double-check that current track position matches expected value"); + NSParameterAssert(aTimer != nil); + NSDictionary *info = (NSDictionary *)[aTimer userInfo]; + NSParameterAssert(info != nil); + NSNumber *length = [info objectForKey:WO_AUDIOSCROBBLER_LENGTH]; + NSParameterAssert(length != nil); + + // query iTunes and ask it if we are where we think we should be (at the "trigger" point) + unsigned trigger = MIN((unsigned)240, ([length unsignedIntValue] / 2)); + + static NSAppleScript *script = nil; + if (!script) + // effectively leak this script, keeping it around for the lifetime of the program to avoid recompiling it every single time + script = [[NSAppleScript alloc] initWithSource:@"tell application \"iTunes\" to get player position"]; + if (!script) + NSLog(@"Error compiling AppleScript (audioscrobblerTimerFired:)"); + + // was a bug: this method reliably caused iTunes to respawn if it was quit + // there is still about a 2 second window of possible error in which iTunes can quit and the system will claim it is still running + // the proper solution is to use Apple Events (which don't cause a respawn) but that will be for Synergy Advance + NSAppleEventDescriptor *result = nil; + if ([WOProcessManager processRunningWithSignature:'hook']) + result = [script executeAndReturnError:NULL]; + if (result) + { + SInt32 position = [result int32Value]; + if ((position > WO_AUDIOSCROBBLER_SKIP_CHECK_RESOLUTION) && + (abs(position - trigger) < WO_AUDIOSCROBBLER_SKIP_CHECK_RESOLUTION)) + { + WOAudioscrobblerLog(@"Position matches, will submit to Audioscrobbler"); + [audioscrobbler submitSong:[info objectForKey:WO_AUDIOSCROBBLER_TRACK] + artist:[info objectForKey:WO_AUDIOSCROBBLER_ARTIST] + album:[info objectForKey:WO_AUDIOSCROBBLER_ALBUM] + length:[length unsignedIntValue]]; + } + else + WOAudioscrobblerLog(@"Position does not match (user must have skipped); will not submit to Audioscrobbler"); + } + else + NSLog(@"Error executing AppleScript (audioscrobblerTimerFired:)"); + audioscrobblerTimer = nil; +} + +// if the submission timer is running, invalidates it +- (void)audioscrobblerCancelSubmission +{ + WOAudioscrobblerLog(@"Cancelling previously existing submission timer, if any"); + [audioscrobblerTimer cancel]; + audioscrobblerTimer = nil; +} + +// called at launch and whenever the preferences get updated +- (void)audioscrobblerReadPreferences +{ + WOAudioscrobblerLog(@"Read preferences"); + BOOL firstTime = NO; + if (!audioscrobblerController) + { + WOAudioscrobblerLog(@"Perform first-time initialization"); + audioscrobblerController = [[WOAudioscrobblerController alloc] init]; // leak this, effectively a singleton + audioscrobbler = [[WOAudioscrobbler alloc] init]; // again, effectively a singleton + firstTime = YES; + } + +#ifdef USE_BUGGY_VERSION + // BUG: changes written to disk (confirmed by inspection) not picked up here + NSString *username = [[NSUserDefaults standardUserDefaults] objectForKey:WO_AUDIOSCROBBLER_USERNAME]; +#else + if (!CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication)) + WOAudioscrobblerLog(@"warning: CFPreferencesAppSynchronize returned false"); + NSString *username = NSMakeCollectable(CFPreferencesCopyAppValue + ((CFStringRef)WO_AUDIOSCROBBLER_USERNAME, kCFPreferencesCurrentApplication)); +#endif + NSString *password = [audioscrobblerController getPasswordFromKeychainForUsername:username]; + + NSString *oldUsername = [audioscrobbler user]; + if ((username && ![username isEqualToString:oldUsername]) || (!username && oldUsername)) + WOAudioscrobblerLog(@"Read new username"); + else + WOAudioscrobblerLog(@"Username is unchanged"); + + NSString *oldPassword = [audioscrobbler password]; + if ((password && ![password isEqualToString:oldPassword]) || (!password && oldPassword)) + WOAudioscrobblerLog(@"Read new password"); + else + WOAudioscrobblerLog(@"Password is unchanged"); + + [audioscrobbler setUser:username]; + [audioscrobbler setPassword:password]; + + if (firstTime) + { + WOAudioscrobblerLog(@"First time we've read the preferences; will start a new session"); + [audioscrobbler startSession]; + } + else + { + WOAudioscrobblerLog(@"Not the first time we've read the preferences; will refresh the existing session"); + [audioscrobbler refreshSession]; + } +} + +- (BOOL)audioscrobblerEnabled +{ + return lastFmEnabled; +} + +// this is trigger by notification from prefPane, so we don't re-notify in this case +- (void)audioscrobblerUpdate:(BOOL)newState +{ + if (newState != lastFmEnabled) + { + if (!newState) + [self audioscrobblerCancelSubmission]; + lastFmEnabled = newState; + } +} + +- (void)audioscrobblerEnable +{ + lastFmEnabled = YES; + CFPreferencesSetAppValue((CFStringRef)WOSynergyPrefEnableLastFm, kCFBooleanTrue, (CFStringRef)WOSynergyPreferencesAppBundleId); + if (!CFPreferencesAppSynchronize((CFStringRef)WOSynergyPreferencesAppBundleId)) + NSLog(@"warning: CFPreferencesAppSyncrhonize returned false"); +} + +- (void)audioscrobblerDisable +{ + // cancel existing submission timer, if any + [self audioscrobblerCancelSubmission]; + lastFmEnabled = NO; + CFPreferencesSetAppValue((CFStringRef)WOSynergyPrefEnableLastFm, kCFBooleanFalse, (CFStringRef)WOSynergyPreferencesAppBundleId); + if (!CFPreferencesAppSynchronize((CFStringRef)WOSynergyPreferencesAppBundleId)) + NSLog(@"warning: CFPreferencesAppSyncrhonize returned false"); +} + +- (void)audioscrobblerUpdateWithSong:(NSString *)track artist:(NSString *)artist album:(NSString *)album length:(unsigned)length +{ + WOAudioscrobblerLog(@"Received notification of playing status"); + NSParameterAssert(track != nil); + length = length / 1000; // convert from milliseconds to seconds + NSParameterAssert(length >= 30); + + // new in 3.5.1a: can temporarily disable submissions, had better check + if (![self audioscrobblerEnabled]) + { + WOAudioscrobblerLog(@"Skipping submission ('Send track information updates to last.fm' checkbox is unchecked)"); + return; + } + + // "Each song should be posted to the server when it is 50% or 240 seconds complete, whichever comes first." + // "If a user seeks (i.e. manually changes position) within a song before the song is due to be submitted, do not submit that song." + // "Songs with a duration of less than 30 seconds should not be submitted." + + NSMutableDictionary *info = [NSMutableDictionary dictionary]; + [info setObject:track forKey:WO_AUDIOSCROBBLER_TRACK]; + [info setObject:[NSNumber numberWithUnsignedInt:length] forKey:WO_AUDIOSCROBBLER_LENGTH]; + if (artist) [info setObject:artist forKey:WO_AUDIOSCROBBLER_ARTIST]; + if (album) [info setObject:album forKey:WO_AUDIOSCROBBLER_ALBUM]; + WOAudioscrobblerLog(@"Track information: %@", info); + + // check to see if this is the same track/artist/album/length combination as previously submitted; if so, resume + if ([info isEqualToDictionary:audioscrobblerCurrentTrack]) + { + // must check "isPaused" here in case iTunes posts duplicate notifications for the same song + // (can occur when adding album art to a playing track, for example) + if (audioscrobblerTimer && [audioscrobblerTimer isPaused]) + { + // looks like this was submitted previously, and we have a paused timer: resuming timer + WOAudioscrobblerLog(@"This track was previously submitted for consideration and we have a paused timer; resuming it"); + [audioscrobblerTimer resume]; + } + else if (playerPosition == 0) + { + // this is a track we've seen before, but it looks like it's on repeat + // see: https://wincent.com/issues/1365 + WOAudioscrobblerLog(@"This track was previously submitted for consideration but player position is 0, so treating like a new submission"); + [self audioscrobblerSetUpSubmissionTimer:info length:length]; + } + else + // getting here is either a programming error, or iTunes is getting funky about what it's submitting (duplicate sub) + WOAudioscrobblerLog(@"This track was previously submitted for consideration but no paused timer found; doing nothing"); + } + else // looks like this is a new submission, will set up timer + { + WOAudioscrobblerLog(@"This track appears to be a new submission"); + [self audioscrobblerSetUpSubmissionTimer:info length:length]; + } +} + +- (void)audioscrobblerSetUpSubmissionTimer:(NSDictionary *)info length:(unsigned)length +{ + // clean up existing timer, if any + [self audioscrobblerCancelSubmission]; + audioscrobblerCurrentTrack = [info copy]; + + // set up timer for 240 secs or 50%, which is smaller + NSTimeInterval interval = MIN((unsigned)240, (length / 2)); + WOAudioscrobblerLog(@"Setting up new submission timer (%d seconds)", (int)interval); + audioscrobblerTimer = [NSTimer scheduledTimerWithTimeInterval:interval + target:self + selector:@selector(audioscrobblerTimerFired:) + userInfo:info + repeats:NO]; +} + +// keep informed of causes for non-submission: this might be ok +// if it is the same track/artist/album/length, and state goes to paused, pause the timer +// if the same track then re-enters playing state them restart the timer +- (void)audioscrobblerNotPlaying:(NSString *)track artist:(NSString *)artist album:(NSString *)album length:(unsigned)length +{ + WOAudioscrobblerLog(@"Received notification of non-playing status"); + length = length / 1000; // convert from milliseconds to seconds + NSMutableDictionary *info = [NSMutableDictionary dictionary]; + [info setObject:track forKey:WO_AUDIOSCROBBLER_TRACK]; + [info setObject:[NSNumber numberWithUnsignedInt:length] forKey:WO_AUDIOSCROBBLER_LENGTH]; + if (artist) [info setObject:artist forKey:WO_AUDIOSCROBBLER_ARTIST]; + if (album) [info setObject:album forKey:WO_AUDIOSCROBBLER_ALBUM]; + WOAudioscrobblerLog(@"Track information: %@", info); + + if ([info isEqualToDictionary:audioscrobblerCurrentTrack]) + { + WOAudioscrobblerLog(@"This track was previously submitted for consideration; pausing timer"); + [audioscrobblerTimer pause]; // not playing but looks like this is a previous submission; pausing timer + } + else + { + WOAudioscrobblerLog(@"This track was not previously submitted for consideration; cancelling timer"); + [self audioscrobblerCancelSubmission]; // not playing and doesn't look like a previous submission; cancelling timer + } +} + +// keep informed of causes for non-submission: this is cause for immediate cancellation of the timer +- (void)audioscrobblerCurrentTrackIsTooShort +{ + WOAudioscrobblerLog(@"Current track is too short for submission"); + [self audioscrobblerCancelSubmission]; +} + +// keep informed of causes for non-submission: this is cause for immediate cancellation of the timer +// "If a user is playing a stream instead of a regular file, do not submit that stream/song." +- (void)audioscrobblerCurrentTrackIsNotRegularFile +{ + WOAudioscrobblerLog(@"Current track cannot be submitted because it is not a regular file"); + [self audioscrobblerCancelSubmission]; +} + +// grounds for immediate cancellation +- (void)audioscrobblerUserDidSkip +{ + WOAudioscrobblerLog(@"Current track cannot be submitted because the user performed a skip"); + [self audioscrobblerCancelSubmission]; +} + +@end diff --git a/SynergyApp/Classes/SynergyController.h b/SynergyApp/Classes/SynergyController.h new file mode 100644 index 0000000..0deaca8 --- /dev/null +++ b/SynergyApp/Classes/SynergyController.h @@ -0,0 +1,373 @@ +// SynergyController.h +// Synergy +// +// Copyright 2002-2010 Wincent Colaiuta. All rights reserved. + +#import +#import + +#import "Growl/Growl.h" + +// used to register Synergy Help with system +@class WOSynergyView, WOPreferences, +WODistributedNotification, WOSynergyFloaterController, +WOFeedbackController, WOAudioscrobblerController, WOAudioscrobbler; + +// presets for internal iTunes state variable +#define ITUNES_PAUSED 0 +#define ITUNES_PLAYING 1 +#define ITUNES_STOPPED 2 +#define ITUNES_NOT_RUNNING 3 +#define ITUNES_ERROR 4 +#define ITUNES_UNKNOWN 5 + +typedef enum WORepeatMode { + + WORepeatOff = 0, + WORepeatOne = 1, + WORepeatAll = 2, + WORepeatUnknown = 3 + +} WORepeatMode; + +typedef enum WOShuffleState { + + WOShuffleOff = 0, + WOShuffleOn = 1, + WOShuffleUnknown = 2 + +} WOShuffleState; + +// This "singleton-like" class does all the real work: we instantiate it once at +// launch from the main nib +@interface SynergyController : NSObject +{ + BOOL waitingForITunesToLaunch; + // custom view for display of control buttons in menu bar + IBOutlet WOSynergyView *synergyMenuView; + + // + WOSynergyFloaterController *floaterController; + + WOFeedbackController *feedbackController; + + // last known state of iTunes + int iTunesState; + int playerPosition; + + // last known shuffle state of iTunes + WOShuffleState shuffleState; + + // last known repeat mode of iTunes + WORepeatMode repeatMode; + + // state variable for shown/hidden menu bar controls: YES or NO + NSNumber *controlButtonsHidden; + + // status item that contains all controls + NSStatusItem *controlsStatusItem; + + // status item for the global menu + NSStatusItem *globalMenuStatusItem; + + //image for "next track" button + NSImage *nextImage; + + //image for "play/pause" button + NSImage *playPauseImage; + + //image for "previous track" button + NSImage *prevImage; + + //the menu attached to the status item + IBOutlet NSMenu *synergyGlobalMenu; + + // convenience pointer for updating contents of iTunes submenu + IBOutlet NSMenu *iTunesSubmenu; + + // convenience pointer for updating contents of playlists submenu + IBOutlet NSMenu *playlistsSubmenu; + + // for updating "clear recent tracks" + IBOutlet NSMenuItem *clearRecentTracksMenuItem; + + // convenience pointer, so we can insert submenus relative to this item + IBOutlet NSMenuItem *synergyPreferencesMenuItem; + + IBOutlet NSMenuItem *turnFloaterOnOffMenuItem; + IBOutlet NSMenuItem *toggleAudioscrobblerMenuItem; + + IBOutlet NSMenuItem *buyFromAmazonMenuItem; + + IBOutlet NSMenuItem *transferCoverArtMenuItem; + + // iTunes submenu + IBOutlet NSMenuItem *shuffleMenuItem; + IBOutlet NSMenuItem *repeatOffMenuItem; + IBOutlet NSMenuItem *repeatAllMenuItem; + IBOutlet NSMenuItem *repeatOneMenuItem; + IBOutlet NSMenuItem *activateITunesMenuItem; + IBOutlet NSMenuItem *launchQuitITunesMenuItem; + + //a timer which will let us check iTunes every 10 seconds + NSTimer *mainTimer; + + //the script to get information from iTunes + NSAppleScript *getSongInfoScript; + + WOPreferences *synergyPreferences; + + // distributed notifications object so we can communicate with prefPane + WODistributedNotification *synergyPrefPane; + + //stores info about the songs iTunes has played + NSMutableArray *songList; + + // used in timer routine to determine if timer loop is a user-generated one + // eg. due to a button click/hotkey press. or a course-of-nature timer- + // generated one + BOOL buttonClickOccurred; + + // used in timer routine -- when YES, send messages to floater (an easy + // way to turn off floater when requested by prefPane is just to set this + // to NO). + BOOL sendMessagesToFloater; + + // used when floater is in "always" on mode to ensure that floater stays + // off (or stays on) when user presses show/hide hot key + // set ON/OFF as user presses show/hide hot key + BOOL floaterActive; + + // internal record of how many segments are "lit" in the volume feedback + int segmentCount; + int iTunesVolume; + + // number of seconds between polling iTunes + float communicationInterval; // use a float here because that's what NSTimer wants + + // temporary storage if user tries to play a song and iTunes not running + NSAppleEventDescriptor *songToPlayOnceLaunched; + + // true for iTunes 4.7 and up + BOOL iTunesSendsNotifications; + + NSString *lastKnownTrackIdentifier; + + NSArray *trackChangeLaunchItems; + + //! Set to YES when user double-clicks a button set in the Finder + BOOL switchToNewSet; + + //! handles reading in account creditials from disk etc + WOAudioscrobblerController *audioscrobblerController; + + //! object that serves as a proxy for communicating with last.fm + WOAudioscrobbler *audioscrobbler; + + // new in 4.2b + BOOL hitAmazon; + + // new in 4.4b + BOOL extraFeedback; +} + +// returns a pointer to our instantiation (created in Interface Builder) ++ (id)sharedInstance; + +- (void)processMessageFromPrefPane:(NSNotification *)message; + +//called when mainTimer fires, handles periodic communication with iTunes +- (void)timer:(NSTimer *)timer; + +// methods for telling iTunes to perform an action +- (void)tellITunesPlayPause; +- (void)tellITunesNext; +- (void)tellITunesFastForward; +- (void)tellITunesRewind; +- (void)tellITunesPrev; +- (void)tellITunesResume; + +// tell iTunes to up the volume by 10% +- (void)tellITunesVolumeUp; + +// tell iTunes to reduce the volume by 10% +- (void)tellITunesVolumeDown; + +- (void)updateTooltip:(NSString *)tooltipString; + +- (void)switchToPauseImage; +- (void)switchToPlayImage; +- (void)switchToPlayPauseImage; + +- (void)hidePlayPauseButton; +- (void)showPlayPauseButton; + +- (void)hidePrevButton; +- (void)showPrevButton; + +- (void)hideNextButton; +- (void)showNextButton; + +- (void)hideGlobalMenu; +- (void)showGlobalMenu; + +- (void)showHideHotKeyPressed; + +- (void)volumeUpHotKeyPressed; + +- (void)volumeDownHotKeyPressed; + +// called when user presses "Show floater" Hot Key +- (void)showHideFloaterHotKeyPressed; + +- (void)hideControlsStatusItem; +- (void)showControlsStatusItem; +- (void)updateAndResizeControlsStatusItem; + +- (void)showPlayPauseButtonImage; +- (void)hidePlayPauseButtonImage; + +- (void)showPrevButtonImage; +- (void)hidePrevButtonImage; + +- (void)showNextButtonImage; +- (void)hideNextButtonImage; + +//sync songList and theMenu +- (void)updateMenu; + +- (void)addGlobalMenu; +- (void)removeGlobalMenu; + +//tells iTunes to play song that the user selected from the menu +- (IBAction)playSong:(id)sender; + +//clears songs from theMenu and songList +- (IBAction)clearRecentSongsMenuItem:(id)sender; + +//opens the preferences window (System Preferences app) +- (IBAction)openPrefsMenuItem:(id)sender; + +- (IBAction)shuffleMenuItem:(id)sender; +- (IBAction)repeatOffMenuItem:(id)sender; +- (IBAction)repeatAllMenuItem:(id)sender; +- (IBAction)repeatOneMenuItem:(id)sender; + +- (IBAction)activateITunesMenuItem:(id)sender; + +- (IBAction)quitLaunchITunesMenuItem:(id)sender; +- (IBAction)refreshPlaylistsSubmenu:(id)sender; +- (IBAction)quitSynergyMenuItem:(id)sender; + +- (IBAction)turnFloaterOnOff:(id)sender; +- (IBAction)toggleAudioscrobbler:(id)sender; +- (IBAction)buyFromAmazon:(id)sender; + +- (IBAction)showAlbumCoversFolder:(id)sender; + +- (void)playPause:(id)sender; + +//tell iTunes to play the next track +- (void)nextTrack:(id)sender; + +//tell iTunes to play the previous track +- (void)prevTrack:(id)sender; + +// after reading preferences, tell floater how we want it to appear +- (void)configureFloater; + +// House-keeping prior to exit +- (void)cleanupBeforeExit; + +- (void)launchITunes; + +- (void)iTunesDidLaunchNowPlay:(NSNotification *)notification; + +- (void)updateFloaterStrings:(NSString *)songTitle + album:(NSString *)albumName + artist:(NSString *)artistName + composer:(NSString *)composerName; + +// these methods called so that we have a distinction between a button press +// and a hot key click +- (void)playPauseHotKeyPressed; +- (void)nextHotKeyPressed; +- (void)prevHotKeyPressed; + +- (void)fastForwardHotKeyPressed; +- (void)fastForwardHotKeyReleased; +- (void)rewindHotKeyPressed; +- (void)rewindHotKeyReleased; + +- (void)rateAs0HotKeyPressed; +- (void)rateAs1HotKeyPressed; +- (void)rateAs2HotKeyPressed; +- (void)rateAs3HotKeyPressed; +- (void)rateAs4HotKeyPressed; +- (void)rateAs5HotKeyPressed; + +- (void)toggleMuteHotKeyPressed; +- (void)toggleShuffleHotKeyPressed; +- (void)setRepeatModeHotKeyPressed; + +- (void)activateITunesHotKeyPressed; + +- (void)decreaseRatingHotKeyPressed; +- (void)increaseRatingHotKeyPressed; + +// slave method that does all the heavy lifting for setting song ratings +- (BOOL)setRating:(int)newRating; + +- (void)tellITunesToPlaySong:(NSAppleEventDescriptor *)descriptor; + +- (void)tellITunesToggleMute; +- (void)tellITunesToggleShuffle; +- (void)tellITunesSetRepeatMode; + +// for bringing iTunes to the front or hiding it +- (void)tellITunesActivate; +- (void)hideITunes; + +// make sure iTunes is running and ready to process Apple Events before firing +// off our status script +- (BOOL)iTunesReadyToReceiveAppleScript; + +- (NSString *)chooseRandomButtonSet; + +// called when download is completed in separate thread +- (void)coverDownloadDone:(NSNotification *)notification; + +// true for iTunes 4.7 and up +- (BOOL)iTunesSendsNotifications; + +// track change launch items support +- (NSString *)applicationSupportPath:(int)domain; +- (NSArray *)getTrackChangeItems:(int)domain; +- (NSArray *)getTrackChangeItems; +- (void)launchTrackChangeItems:(NSArray *)paths; + +- (IBAction)transferCoverArtToITunes:(id)sender; + +// accessors + +- (BOOL)hitAmazon; + +// temporary storage for song id (used while waiting for iTunes to launch) +- (void)setSongToPlayOnceLaunched:(NSAppleEventDescriptor *)songId; +- (NSAppleEventDescriptor *)songToPlayOnceLaunched; + +- (NSArray *)trackChangeLaunchItems; +- (void)setTrackChangeLaunchItems:(NSArray *)aTrackChangeLaunchItems; + +// refactoring for sending simple Apple Events to iTunes +- (void)sendAppleEventClass:(AEEventClass)eventClass ID:(AEEventID)eventID; + +#pragma mark GrowlApplicationBridgeDelegate protocol + +- (NSDictionary *)registrationDictionaryForGrowl; + +#pragma mark - + +@end + + diff --git a/SynergyApp/Classes/SynergyController.m b/SynergyApp/Classes/SynergyController.m new file mode 100644 index 0000000..a2da61b --- /dev/null +++ b/SynergyApp/Classes/SynergyController.m @@ -0,0 +1,4852 @@ +// SynergyController.m +// Copyright 2002-2010 Wincent Colaiuta. All rights reserved. + +// system headers +#import +#import +#import +#import + +// other headers +#import "iTunes.h" +#import "WOSynergyGlobal.h" +#import "SynergyController.h" +#import "HotkeyCapableApplication.h" +#import "WODebug.h" +#import "WODistributedNotification.h" +#import "WOPreferences.h" +#import "WOSynergyView.h" +#import "WOCarbonWrappers.h" +#import "WOSynergyFloaterController.h" +#import "WOFeedbackController.h" +#import "WOProcessManager.h" +#import "WOCoverDownloader.h" +#import "WOExceptions.h" +#import "WOSongInfo.h" +#import "WOAudioscrobblerController.h" + +// categories +#import "NSAppleScript+WOAdditions.h" +#import "NSImage+WOAdditions.h" +#import "WONSFileManagerExtensions.h" +#import "WONSStringExtensions.h" +#import "SynergyController+WOAudioscrobbler.h" + +#import "WOButtonSet.h" + +// WOPublic macro headers +#import "WOPublic/WOConvenienceMacros.h" + +// WOPublic category headers +#import "WOPublic/NSDictionary+WOCreation.h" + +#pragma mark - +#pragma mark Macros + +// macros for keys in a "songDictionary" (identifying info for a song) +#define WO_SONG_DICTIONARY_ID @"_woSongDictionaryId" +#define WO_SONG_DICTIONARY_TITLE @"_woSongDictionaryTitle" +#define WO_SONG_DICTIONARY_ARTIST @"_woSongDictionaryArtist" + +// added support for amazon.com "Buy now" link storage +#define WO_SONG_DICTIONARY_BUY_NOW @"_woSongDictionaryBuyNow" +#define WO_SONG_DICTIONARY_SONGINFO @"_woSongDictionarySongInfo" + +// preferences managed via Cocoa Bindings +#define WO_EXTRA_VISUAL_FEEDBACK_OTHER CFSTR("ExtraVisualFeedbackForOtherHotKeys") + +// these are all stored in SynergyPreferences domain (corresponding to wrapper app) +#define WO_SYNERGY_PREFERENCES_DOMAIN CFSTR("com.wincent.SynergyPreferences") + +#pragma mark - +#pragma mark Global variables + +static id _sharedSynergyController = nil; +NSString *applicationOpenFileValueReceivedEarly = nil; + +// and the variables used internally to hold the numbers +UInt32 quitCode; +UInt32 playCode; +UInt32 prevCode; +UInt32 nextCode; +UInt32 widthCode; + +#pragma mark Private methods + +@interface SynergyController () + +- (NSString *)audioscrobblerMenuTitleForState:(BOOL)enabled; + +@end + +#pragma mark - + +// This "singleton-like" class does all the real work: we instantiate it once at +// launch-time from the main nib +@implementation SynergyController + +// returns a pointer to our instantiation ++ (id) sharedInstance +{ + if (_sharedSynergyController != nil) + // We should have already been allocated in the +initialize method + return _sharedSynergyController; + else + // if not allocated, self-allocate now + return [self init]; +} + +// This method called on startup after "initialize", next comes "awakeFromNib" +- (id) init +{ + if (_sharedSynergyController == nil) + { + // First entry into init + self = [super init]; + _sharedSynergyController = self; + + NSURL *url = [NSURL fileURLWithPath: + [[[NSBundle mainBundle] resourcePath] + stringByAppendingPathComponent:@"Scripts/getSongInfo.scpt"] ]; + + getSongInfoScript = [[NSAppleScript alloc] initWithContentsOfURL:url error:nil]; + + songList = [[NSMutableArray alloc] init]; + + controlButtonsHidden = [NSNumber numberWithBool:YES]; + + floaterController = [[WOSynergyFloaterController alloc] init]; + + // this next line calls awakeFromNib on floaterController + if(![NSBundle loadNibNamed:@"synergyFloater" owner:floaterController]) + { + ELOG(@"An error occurred while trying to load the nib file for the " + @"floating notification window"); + }; + // so we now have an instance called floaterController to which we can + // send messages + + feedbackController = [[WOFeedbackController alloc] init]; + + // this next line calls awakeFromNib on feedbackController + if (![NSBundle loadNibNamed:@"feedback" owner:feedbackController]) + { + ELOG(@"An error occurred while trying to load the nib file for the " + @"feedback window"); + } + + // set iTunes state variable to "unknown" before firing timer for first time: + iTunesState = ITUNES_UNKNOWN; + shuffleState = WOShuffleUnknown; + repeatMode = WORepeatUnknown; + + Boolean keyExistsAndHasValidFormat; + Boolean boolCF = CFPreferencesGetAppBooleanValue((CFStringRef)@"hitAmazon", + WO_SYNERGY_PREFERENCES_DOMAIN, + &keyExistsAndHasValidFormat); + hitAmazon = !(keyExistsAndHasValidFormat && !boolCF); + boolCF = CFPreferencesGetAppBooleanValue(WO_EXTRA_VISUAL_FEEDBACK_OTHER, + WO_SYNERGY_PREFERENCES_DOMAIN, + &keyExistsAndHasValidFormat); + extraFeedback = !(keyExistsAndHasValidFormat && !boolCF); + } + else + // init has been called more than once + self = _sharedSynergyController; + + return self; +} + +- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename +{ + NSString *baseName = [[filename lastPathComponent] stringByDeletingPathExtension]; + + switchToNewSet = YES; + + // check if button set of same name already in place + if (![[WOButtonSet availableButtonSets] containsObject:baseName]) + { + // attempt to copy to ~/Application Support/Synergy/Button Sets + NSString *installPath = [WOButtonSet installPath]; + + if (!installPath) return NO; // target dir couldn't be created + + NSString *destination = [installPath stringByAppendingPathComponent:[filename lastPathComponent]]; + switchToNewSet = [[NSFileManager defaultManager] copyItemAtPath:filename + toPath:destination + error:NULL]; + } + + if (switchToNewSet) + { + // try to set as current button set + if (synergyPreferences) // have already gone thorough "awakeFromNib" + { + [synergyPreferences setObject:baseName forKey:_woButtonStylePrefKey flushImmediately:YES]; + + // trick self into re-reading prefs + [self processMessageFromPrefPane: + [NSNotification notificationWithName:WODistributedNotificationIdentifier + object:[NSString stringWithFormat:@"%d", WODNAppReadPrefs]]]; + + [[NSDistributedNotificationCenter + defaultCenter] postNotificationName:WODistributedNotificationIdentifier + object:[NSString stringWithFormat:@"%d", WODNPrefNoteButtonSetLoaded] + userInfo:[NSDictionary dictionaryWithObject:baseName forKey:@"setName"]]; + } + else + // store this in a global var for later + applicationOpenFileValueReceivedEarly = [baseName copy]; + } + + return switchToNewSet; +} + +- (void)awakeFromNib +{ + [self setTrackChangeLaunchItems:[self getTrackChangeItems]]; + + // my testing shows that this method will be called before the corresponding method in HotKeyCapableApplication: + // but shouldn't rely on that + // this WOPreferences class is really just a dumb wrapper for NSUserDefaults; I'd be better off without it + synergyPreferences = [WOPreferences sharedInstance]; + + [synergyPreferences readPrefsFromWithinAppBundle]; + + // now that we've read the prefs... make sure the menu bar controls are + // going to use the correct button style + NSString *newButtonSet; + + if ([[synergyPreferences objectOnDiskForKey:_woRandomButtonStylePrefKey] boolValue] == NO) + // make sure we're using the correct button set + newButtonSet = [synergyPreferences objectOnDiskForKey:_woButtonStylePrefKey]; + else + // choose a random button set + newButtonSet = [self chooseRandomButtonSet]; + + // but check to see if a user double-clicked on a set in the Finder + if (applicationOpenFileValueReceivedEarly) + { + newButtonSet = applicationOpenFileValueReceivedEarly; + applicationOpenFileValueReceivedEarly = nil; + [synergyPreferences setObject:newButtonSet forKey:_woButtonStylePrefKey flushImmediately:YES]; + [synergyPrefPane notifyPrefPane:WODNPrefNoteButtonSetLoaded]; + } + + if (newButtonSet != nil) + [synergyMenuView setButtonSet:newButtonSet]; + else + [synergyMenuView setButtonSet:WO_DEFAULT_BUTTON_SET]; + + // set to starting values + buttonClickOccurred = NO; + sendMessagesToFloater = YES; + segmentCount = 0; + iTunesVolume = 0; + [self setSongToPlayOnceLaunched:nil]; + floaterActive = YES; + + [self refreshPlaylistsSubmenu:nil]; + + // use prefs read from disk to configure floater appearance + [self configureFloater]; + + // making ourself the NSApplication delegate enables us to receive + // NSApplicationDidChangeScreenParametersNotification notifications + [NSApp setDelegate:self]; + + // coerce int to float + communicationInterval = + [[synergyPreferences objectOnDiskForKey:_woCommunicationIntervalPrefKey] floatValue]; + + if (communicationInterval < WO_MIN_POLLING_INTERVAL) + communicationInterval = WO_MIN_POLLING_INTERVAL; + + if (communicationInterval > WO_MAX_POLLING_INTERVAL) + communicationInterval = WO_MAX_POLLING_INTERVAL; + + if ([self iTunesSendsNotifications]) + { + [GrowlApplicationBridge setGrowlDelegate:self]; + + // watch for launch/quit events of iTunes, seeing as we won't be polling + NSNotificationCenter *center = + [[NSWorkspace sharedWorkspace] notificationCenter]; + [center addObserver:self + selector:@selector(handleWorkspaceNotification:) + name:@"NSWorkspaceDidLaunchApplicationNotification" + object:nil]; + [center addObserver:self + selector:@selector(handleWorkspaceNotification:) + name:@"NSWorkspaceDidTerminateApplicationNotification" + object:nil]; + + // keep timer intact, but with a ridiculously long interval + communicationInterval = 60.0 * 60.0 * 24.0 * 30.0 ; // once per month + } + + // register for notifications regardless + [[NSDistributedNotificationCenter + defaultCenter] addObserver:self + selector:@selector(handleNotification:) + name:@"com.apple.iTunes.playerInfo" + object:nil]; + + mainTimer = [NSTimer scheduledTimerWithTimeInterval:communicationInterval + target:self + selector:@selector(timer:) + userInfo:nil + repeats:YES]; + + + + globalMenuStatusItem = nil; + + +// } + + // send this message regardless of whether global menu is a separate + // NSStatusItem or integrated into play/pause button + [synergyGlobalMenu setAutoenablesItems:NO]; + + // always ghost the "Recent tracks" header; again this is ugly, will fix it later + int indexOfRecentTracks = [synergyGlobalMenu indexOfItem:clearRecentTracksMenuItem]; + if (indexOfRecentTracks == 1) + [[synergyGlobalMenu itemAtIndex:0] setEnabled:NO]; + + [toggleAudioscrobblerMenuItem setTitle:[self audioscrobblerMenuTitleForState:[self audioscrobblerEnabled]]]; + + if(([[synergyPreferences objectOnDiskForKey:_woPlayButtonInMenuPrefKey] boolValue] == NO) && + ([[synergyPreferences objectOnDiskForKey:_woPrevButtonInMenuPrefKey] boolValue] == NO) && + ([[synergyPreferences objectOnDiskForKey:_woNextButtonInMenuPrefKey] boolValue] == NO)) + { + // user doesn't want to show any buttons + if ([[synergyPreferences objectOnDiskForKey:_woGlobalMenuPrefKey] boolValue]) + // but they do want to show the global menu + [self addGlobalMenu]; + } + else + { + // user may wish to show global menu anyway: + if (([[synergyPreferences objectOnDiskForKey:_woGlobalMenuPrefKey] boolValue]) && + ([[synergyPreferences objectOnDiskForKey:_woGlobalMenuOnlyWhenHiddenPrefKey] boolValue] == NO)) + [self addGlobalMenu]; + + [self showControlsStatusItem]; + } + + // set up link with prefPane + synergyPrefPane = [WODistributedNotification makePrefPaneObserver:self selector:@selector(processMessageFromPrefPane:)]; + + // tell prefPane we're running (also happens when we fire timer) + [synergyPrefPane notifyPrefPane:WODNAppLaunched]; + + // update auto-connect setting + [WOCoverDownloader setConnectOnDemand:[[synergyPreferences objectOnDiskForKey:_woAutoConnectTogglePrefKey] boolValue]]; + + // update "pre-process" setting + [WOCoverDownloader setPreprocess:[[synergyPreferences objectOnDiskForKey:_woPreprocessTogglePrefKey] boolValue]]; + + // register for notifications of cover download completions + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(coverDownloadDone:) + name:WO_DOWNLOAD_DONE_NOTIFICATION + object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(coverDownloadDone:) + name:WO_BUY_NOW_LINK_NOTIFICATION + object:nil]; + + [mainTimer fire]; + + // let the category handle this + [self audioscrobblerReadPreferences]; +} + +/* + This is one hell of an ugly and long routine: + + to fix: + + make a category for NSMenu and/or NSMenuItem which enables easy: + (or a subclass if necessary) + + hide/show of submenus + hide/show of menuitems (as opposed to removing and destroying them, or ghosting them) + + "chaining" of items and separators + eg. separator S, items X, Y, Z + chain S to X-Y-Z + so S will appear before X-Y-Z, or Y-Z, or X etc + instead of all those complicated tests about whether stuff should be added... + + */ +- (void)addGlobalMenu +{ + [synergyGlobalMenu setMenuChangedMessagesEnabled:NO]; + + globalMenuStatusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength]; + + // may have to turn highlighting off and do own highlighting (because + // currently the graphic remains black, instead of turning white like + // NSMenuExtras do) + [globalMenuStatusItem setHighlightMode:YES]; + +#ifdef WO_GLOBAL_MENU_USES_UNICODE_CHAR + + [globalMenuStatusItem setTitle:[NSString stringWithFormat:@"%C",WO_GLOBAL_MENU_UNICODE_CHAR]]; + +#else + + [globalMenuStatusItem setImage:[NSImage imageNamed:@"musicGlyph.png"]]; + + // doesn't work! (yet another NSStatusItem/NSMenuExtra distinction) + //[globalMenuStatusItem setAlternateImage: + // [NSImage imageNamed:@"musicGlyphSelected.png"]]; + + // We have an (unpleasant) choice here: + // 1. Do some nasty, undocumented hackery of the kind described here: + // http://cocoa.mamasam.com/COCOADEV/2003/02/1/56710.php + // (posing as NSStatusBarButtonCell) + // + // 2. Break Apple's stuff and kludge Synergy to use NSMenuExtra instead + // of NSStatusItem + + + + +#endif + + [globalMenuStatusItem setMenu:synergyGlobalMenu]; + [globalMenuStatusItem setEnabled:YES]; + + // try this to make our disable/enabled changes stick: + [synergyGlobalMenu setAutoenablesItems:NO]; + /* + + If autoenablesItems is left at the default value of YES, then Cocoa ignores + any values set using setEnabled; an IB connection is enough to force an + item to be always enabled. + + */ + + // make sure "Recent tracks" label is disabled: + [[[synergyGlobalMenu itemArray] objectAtIndex:0] setEnabled:NO]; + + // find out the index of the playlists submenu (will be -1 if removed) + int playlistsSubmenuIndex = [synergyGlobalMenu indexOfItemWithSubmenu:playlistsSubmenu]; + + // pointer to the NSMenuItem containing our submenu; static because we want to use it across multiple invocations of this method + static id playlistsMenuItem; + + // hide playlists Submenu if necessary + if ([[synergyPreferences objectOnDiskForKey:_woPlaylistsSubmenuPrefKey] boolValue] == NO) + { + // test to see if we've already been removed on a previous pass + if (playlistsSubmenuIndex != -1) + { + // we've been removed previously -- ok to proceed + + // this is a pointer to the NSMenu object that contains the submenu + playlistsMenuItem = [synergyGlobalMenu itemAtIndex:playlistsSubmenuIndex]; + + [synergyGlobalMenu removeItem:playlistsMenuItem]; + + // remove separator above submenu if there is one there (and there always will be) + if ([[synergyGlobalMenu itemAtIndex:(playlistsSubmenuIndex -1)] isSeparatorItem]) + [synergyGlobalMenu removeItem:[synergyGlobalMenu itemAtIndex:(playlistsSubmenuIndex -1)]]; + } + } + else + { + // prefs tell us to add submenu back in if it's been removed... + + // test to see if we've already been removed on a previous pass + if (playlistsSubmenuIndex == -1) + { + // appear two places above prefs item if iTunes menu is present or one place above it if not + int destinationIndex; + + if ([synergyGlobalMenu indexOfItemWithSubmenu:iTunesSubmenu] != -1) + // bumped upwards by 1 for 2.9 ("Transfer cover to iTunes" menu item) + destinationIndex = [synergyGlobalMenu indexOfItem:synergyPreferencesMenuItem] - 8; + else + // bumped upwards by 1 for 2.9 ("Transfer cover to iTunes" menu item) + destinationIndex = [synergyGlobalMenu indexOfItem:synergyPreferencesMenuItem] - 7; + + // because of removal on previous pass, playlistsMenuItem will contain a pointer to the removed item + [synergyGlobalMenu insertItem:playlistsMenuItem atIndex:destinationIndex]; + + // restore separator (above submenu) as well if it is missing + // note that we can always assume destinationIndex - 1 to be positive + // because the first couple of slots are always the recent tracks and clear recent tracks items + if([[synergyGlobalMenu itemAtIndex:(destinationIndex - 1)] isSeparatorItem] == NO) + [synergyGlobalMenu insertItem:[NSMenuItem separatorItem] atIndex:destinationIndex]; + } + } + + // find out the index of the iTunes submenu (will be -1 if removed) + int iTunesSubmenuIndex = [synergyGlobalMenu indexOfItemWithSubmenu:iTunesSubmenu]; + + // pointer to the NSMenuItem containing our submenu; static because we want to use it across multiple invocations of this method + static id iTunesMenuItem; + + // hide iTunes submenu if necessary + if ([[synergyPreferences objectOnDiskForKey:_woLaunchQuitItemsPrefKey] boolValue] == NO) + { + // test to see if we've already been removed on a previous pass + if (iTunesSubmenuIndex != -1) + { + // we've been removed previously -- ok to proceed + + // this is a pointer to the NSMenu object that contains the submenu + iTunesMenuItem = [synergyGlobalMenu itemAtIndex:iTunesSubmenuIndex]; + [synergyGlobalMenu removeItem:iTunesMenuItem]; + + // remove separator above submenu if there is one there + if ([[synergyGlobalMenu itemAtIndex:(iTunesSubmenuIndex - 1)] isSeparatorItem]) + [synergyGlobalMenu removeItem:[synergyGlobalMenu itemAtIndex:(iTunesSubmenuIndex - 1)]]; + /* + + or: + + action for removing either submenus: + remove sep if there's a sep above + + action for adding playlists submenu: + add sep if there isn't one above (there might be if iTunes add one before!) + + action called whenever iTunes sub is visible eg. when added, or when left on: + add sep if there isn't one above & playlists sub is active + + */ + } + } + else + { + // prefs tell us to add submenu back in if it's been removed... + + // test to see if we've already been removed on a previous pass + if (iTunesSubmenuIndex == - 1) + { + // appear one places above prefs item + // bumped upwards by 1 for 2.9 ("Transfer cover to iTunes" menu item) + int destinationIndex = ([synergyGlobalMenu indexOfItem:synergyPreferencesMenuItem] - 7); + + // because of removal on previous pass, iTunesMenuItem will contain a pointer to the removed item + [synergyGlobalMenu insertItem:iTunesMenuItem atIndex:destinationIndex]; + } + + // update value of iTunesSubmenuIndex (will be different if we just re-inserted the menu) + iTunesSubmenuIndex = + [synergyGlobalMenu indexOfItemWithSubmenu:iTunesSubmenu]; + + // make sure there's a separator above if one is required + if (iTunesSubmenuIndex != -1) + { + // iTunes submenu is now present (either re-enabled, or still enabled) + if (([synergyGlobalMenu indexOfItemWithSubmenu:playlistsSubmenu] == -1) && + ([[synergyGlobalMenu itemAtIndex:(iTunesSubmenuIndex - 1)] isSeparatorItem] == NO)) + { + // iTunes menu is there, playlists submenu not there, and there is + // no sep above, so add one + [synergyGlobalMenu insertItem:[NSMenuItem separatorItem] + atIndex:iTunesSubmenuIndex]; + + } + } + } + + // need to make sure that changing the title of iTunes launched/not launched + // doesn't crash us when the menu is in its removed state + [synergyGlobalMenu setMenuChangedMessagesEnabled:YES]; + + // this ensure things like our preferences for display/non-display of + // recently-played tracks are picked up immediately. + [self updateMenu]; +} + +- (void)removeGlobalMenu +{ + [[NSStatusBar systemStatusBar] removeStatusItem:globalMenuStatusItem]; + globalMenuStatusItem = nil; +} + +- (void)processMessageFromPrefPane:(NSNotification *)message +{ + if ([[message object] isEqualToString:[NSString stringWithFormat:@"%d", WODNAppStatus]]) + { + // tell prefPane that we are running + [synergyPrefPane notifyPrefPane:WODNAppIsRunning]; + + // update state variable to reflect that the prefPane is also running + [synergyPrefPane setPrefPaneState:WODNRunning]; + } + + if ([[message object] isEqualToString:[NSString stringWithFormat:@"%d", WODNPaneLaunched]]) + { + // tell prefPane that we are running + [synergyPrefPane notifyPrefPane:WODNAppIsRunning]; + + // update state variable to reflect that the prefPane is also running + [synergyPrefPane setPrefPaneState:WODNRunning]; + } + + if ([[message object] isEqualToString:[NSString stringWithFormat:@"%d", WODNPaneIsRunning]]) + { + // update state variable to reflect that the prefPane is also running + [synergyPrefPane setPrefPaneState:WODNRunning]; + } + + if ([[message object] isEqualToString:[NSString stringWithFormat:@"%d", WODNPaneWillQuit]]) + { + // update state variable to reflect that the prefPane has quit + [synergyPrefPane setPrefPaneState:WODNStopped]; + } + + if ([[message object] isEqualToString:[NSString stringWithFormat:@"%d", WODNAppReadPrefs]]) + { + // tell prefPane that we are (still) running + [synergyPrefPane notifyPrefPane:WODNAppIsRunning]; + + // update state variable to reflect that the prefPane is also running + [synergyPrefPane setPrefPaneState:WODNRunning]; + + [NSApp unregisterHotkeys]; + + // let the category handle this + [self audioscrobblerReadPreferences]; + + [self hideControlsStatusItem]; + + if (globalMenuStatusItem != nil) + [self removeGlobalMenu]; + + /* + I can write the new prefs out to disk and see them change by looking at the + plist file; but when I look at the log output I see that the app + rereads them only the first time and not subsequent times. + */ + [synergyPreferences resetStandardUserDefaults]; // try to beat the cache problem (works) + + [synergyPreferences readPrefsFromWithinAppBundle]; // this class + + NSString *newButtonSet; + + // new: test switchToNewSet; fixes bug: http://wincent.com/a/support/bugs/show_bug.cgi?id=442 + if (([[synergyPreferences objectOnDiskForKey:_woRandomButtonStylePrefKey] boolValue] == NO) || switchToNewSet) + { + // make sure we're using the correct button set + newButtonSet = [synergyPreferences objectOnDiskForKey:_woButtonStylePrefKey]; + switchToNewSet = NO; + } + else // choose a random button set + newButtonSet = [self chooseRandomButtonSet]; + + [synergyMenuView setButtonSet:newButtonSet ? newButtonSet : WO_DEFAULT_BUTTON_SET]; + [WOCoverDownloader setConnectOnDemand:[[synergyPreferences objectOnDiskForKey:_woAutoConnectTogglePrefKey] boolValue]]; + [WOCoverDownloader setPreprocess:[[synergyPreferences objectOnDiskForKey:_woPreprocessTogglePrefKey] boolValue]]; + + // only bother to show the status item if at least one button is enabled + if(([[synergyPreferences objectOnDiskForKey:_woPlayButtonInMenuPrefKey] boolValue]) || + ([[synergyPreferences objectOnDiskForKey:_woPrevButtonInMenuPrefKey] boolValue]) || + ([[synergyPreferences objectOnDiskForKey:_woNextButtonInMenuPrefKey] boolValue])) + { + // if user wants the global menu at all times, show it + if ([[synergyPreferences objectOnDiskForKey:_woGlobalMenuPrefKey] boolValue] && + ([[synergyPreferences objectOnDiskForKey:_woGlobalMenuOnlyWhenHiddenPrefKey] boolValue] == NO)) + [self addGlobalMenu]; + + [self showControlsStatusItem]; + } + else + { + // only show the global menu if the user wants it + if ([[synergyPreferences objectOnDiskForKey:_woGlobalMenuPrefKey] boolValue]) + [self addGlobalMenu]; + } + + // reconfigure floater with current prefs vals + [self configureFloater]; + + // coerce int to float + communicationInterval = [[synergyPreferences objectOnDiskForKey:_woCommunicationIntervalPrefKey] floatValue]; + + if (communicationInterval < WO_MIN_POLLING_INTERVAL) + communicationInterval = WO_MIN_POLLING_INTERVAL; + + if (communicationInterval > WO_MAX_POLLING_INTERVAL) + communicationInterval = WO_MAX_POLLING_INTERVAL; + + if ([self iTunesSendsNotifications]) + // keep timer intact, but with a ridiculously long interval + communicationInterval = 60.0 * 60.0 * 24.0 * 30.0 ; // once/month + + // change timer interval if necessary + if ([mainTimer timeInterval] != communicationInterval) + { + // destroy the old timer + [mainTimer invalidate]; + + // and create a new one + mainTimer = [NSTimer scheduledTimerWithTimeInterval:communicationInterval + target:self + selector:@selector(timer:) + userInfo:nil + repeats:YES]; + } + + [NSApp registerHotkeys]; + } + + if ([[message object] isEqualToString:[NSString stringWithFormat:@"%d", WODNAppQuit]]) + { + [self cleanupBeforeExit]; // this will work + exit(0); // this ugly old way... + } + + if ([[message object] isEqualToString:[NSString stringWithFormat:@"%d", WODNAppUnregisterHotkeys]]) + { + // tell prefPane that we are (still) running + [synergyPrefPane notifyPrefPane:WODNAppIsRunning]; + + // update state variable to reflect that the prefPane is also running + [synergyPrefPane setPrefPaneState:WODNRunning]; + + // unregister hotkeys + [NSApp unregisterHotkeys]; + } + + if ([[message object] isEqualToString:[NSString stringWithFormat:@"%d", WODNAppRegisterHotkeys]]) + { + // tell prefPane that we are (still) running + [synergyPrefPane notifyPrefPane:WODNAppIsRunning]; + + // update state variable to reflect that the prefPane is also running + [synergyPrefPane setPrefPaneState:WODNRunning]; + + // register hotkeys + [NSApp registerHotkeys]; + } + + if ([[message object] isEqualToString:[NSString stringWithFormat:@"%d", WODNAppNoFloater]]) + { + // tell prefPane that we are (still) running + [synergyPrefPane notifyPrefPane:WODNAppIsRunning]; + + // update state variable to reflect that prefPane is also running + [synergyPrefPane setPrefPaneState:WODNRunning]; + + // suspend floater use... + sendMessagesToFloater = NO; + + // and remove any floater currently on screen + [floaterController fadeWindowOut:self]; + + } + + if ([[message object] isEqualToString:[NSString stringWithFormat:@"%d", WODNAppFloaterOK]]) + { + // tell prefPane that we are (still) running + [synergyPrefPane notifyPrefPane:WODNAppIsRunning]; + + // update state variable to reflect that prefPane is also running + [synergyPrefPane setPrefPaneState:WODNRunning]; + + // reinstate floater use... + sendMessagesToFloater = YES; + } + + if ([[message name] isEqualToString:WO_NEW_PREFS_FROM_PREFS_TO_APP]) + { + NSString *serializedPreferences = [message object]; + NSData *data = [serializedPreferences dataUsingEncoding:NSUTF8StringEncoding]; + NSString *error = nil; + NSDictionary *newPrefs = [NSPropertyListSerialization propertyListFromData:data + mutabilityOption:NSPropertyListImmutable + format:NULL + errorDescription:&error]; + if (error) + { + NSLog(@"+[NSPropertyListSerialization propertyListFromData:mutabilityOption:format:errorDescription:] reported error: %@", error); + return; + } + if (!newPrefs) + { + NSLog(@"-[SynergyController processMessageFromPrefPane:] deserialization failed"); + return; + } + if ([newPrefs isKindOfClass:[NSDictionary class]]) + { + // update menu item etc + NSNumber *e = [newPrefs objectForKey:@"enableLastFm"]; + if (e) + { + BOOL newValue = [e boolValue]; + [self audioscrobblerUpdate:newValue]; + [toggleAudioscrobblerMenuItem setTitle:[self audioscrobblerMenuTitleForState:newValue]]; + } + e = [newPrefs objectForKey:@"hitAmazon"]; + if (e) + hitAmazon = [e boolValue]; + e = [newPrefs objectForKey:@"ExtraVisualFeedbackForOtherHotKeys"]; + if (e) + extraFeedback = [e boolValue]; + } + } +} + +- (void)showHideHotKeyPressed +{ + /* + + It is necessary to keep two state variables for the showing/hiding of the + control buttons. One for the user preference and one for the current + state of the buttons. + + The buttons will show/hide as the result of two possible events: + + 1. User activates the toggle hotkey + 2. iTunes state changes (eg. launches, quits etc) + + user shows and hides controls at will, + and iTunes state changes will only trigger a show/hide operation if the + state change is to/from a ITUNES_NOT_RUNNING state... + + */ + + if ([controlButtonsHidden boolValue]) + { + // controls are currently hidden: unhide them + [self showControlsStatusItem]; + + // does the user actually want to show any buttons? + if(([[synergyPreferences objectOnDiskForKey:_woPlayButtonInMenuPrefKey] boolValue]) || + ([[synergyPreferences objectOnDiskForKey:_woPrevButtonInMenuPrefKey] boolValue]) || + ([[synergyPreferences objectOnDiskForKey:_woNextButtonInMenuPrefKey] boolValue])) + { + // could roll this into the if statement above, but leaving it here for readability + if (globalMenuStatusItem && + [[synergyPreferences objectOnDiskForKey:_woGlobalMenuOnlyWhenHiddenPrefKey] boolValue]) + { + // we were showing the global menu, but user wants to only show it when controls are hidden + [self removeGlobalMenu]; + } + } + } + else + { + // controls are currently visible: hide them + [self hideControlsStatusItem]; + + // if not already showing the global menu AND the user wants us to show it, show it + if (!globalMenuStatusItem && + [[synergyPreferences objectOnDiskForKey:_woGlobalMenuPrefKey] boolValue]) + [self addGlobalMenu]; + } + + [mainTimer fire]; +} + +- (void)hideControlsStatusItem +{ + if (![controlButtonsHidden boolValue]) + { + controlButtonsHidden = [NSNumber numberWithBool:YES]; + [[HotkeyCapableApplication sharedApplication] setNextResponder:nil]; + [[NSStatusBar systemStatusBar] removeStatusItem:controlsStatusItem]; + [synergyMenuView removeFromSuperview]; + controlsStatusItem = nil; + } +} + +- (void)showControlsStatusItem +{ + // only bother to show the status item if at least one button is enabled + if([synergyPreferences objectOnDiskForKey:_woPlayButtonInMenuPrefKey] || + [synergyPreferences objectOnDiskForKey:_woPrevButtonInMenuPrefKey] || + [synergyPreferences objectOnDiskForKey:_woNextButtonInMenuPrefKey]) + { + if ([controlButtonsHidden boolValue]) + { + controlButtonsHidden = [NSNumber numberWithBool:NO]; + + // adjust size of frame depending which buttons are displayed + int totalWidth = [synergyMenuView calculateControlsStatusItemWidth]; + + // it appears that when assigning a length to this statusItem we need to compensate for an + // Apple bug. When I ask for a length of 100, I seem to only get something about 90 pixels + // long! + + // the solution might be to let it auto-assign the length (with NSVariableStatusItemLength) + // and just adjust the size of the view.... + + controlsStatusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength]; + [controlsStatusItem setHighlightMode:NO]; + NSSize menuViewFrameSize = NSMakeSize(totalWidth,controlViewHeight); + [synergyMenuView setFrameSize:menuViewFrameSize]; + if ([[synergyPreferences objectOnDiskForKey:_woNextButtonInMenuPrefKey] intValue]) + [self showNextButtonImage]; + else + [self hideNextButtonImage]; + + if ([[synergyPreferences objectOnDiskForKey:_woPrevButtonInMenuPrefKey] intValue]) + [self showPrevButtonImage]; + else + [self hidePrevButtonImage]; + + if ([[synergyPreferences objectOnDiskForKey:_woPlayButtonInMenuPrefKey] intValue]) + [self showPlayPauseButtonImage]; + else + [self hidePlayPauseButtonImage]; + [controlsStatusItem setView:synergyMenuView]; + } + } +} + +// This routine called whenever a button added-to/removed-from the controlsStatusItem (verified works) +- (void) updateAndResizeControlsStatusItem +{ + int totalWidth = [synergyMenuView calculateControlsStatusItemWidth]; + + NSSize menuViewFrameSize = NSMakeSize(totalWidth,controlViewHeight); + [synergyMenuView setFrameSize:menuViewFrameSize]; + [controlsStatusItem setView:synergyMenuView]; + + if ([[synergyPreferences objectOnDiskForKey:_woNextButtonInMenuPrefKey] intValue]) + [self showNextButtonImage]; + else + [self hideNextButtonImage]; + + if ([[synergyPreferences objectOnDiskForKey:_woPrevButtonInMenuPrefKey] intValue]) + [self showPrevButtonImage]; + else + [self hidePrevButtonImage]; + + if ([[synergyPreferences objectOnDiskForKey:_woPlayButtonInMenuPrefKey] intValue]) + [self showPlayPauseButtonImage]; + else + [self hidePlayPauseButtonImage]; +} + +- (void) showPlayPauseButtonImage +{ + [synergyMenuView setGlobalMenu:synergyGlobalMenu]; + [synergyMenuView showPlayButton]; +} + +- (void) hidePlayPauseButtonImage +{ + [synergyMenuView hidePlayButton]; +} + +- (void) showPrevButtonImage +{ + [synergyMenuView showPrevButton]; + [synergyMenuView setPrevTooltip:NSLocalizedString(@"Previous track in iTunes",@"Previous track button tool-tip")]; +} + +- (void) hidePrevButtonImage +{ + [synergyMenuView hidePrevButton]; +} + +- (void) showNextButtonImage +{ + [synergyMenuView showNextButton]; + [synergyMenuView setNextTooltip:NSLocalizedString(@"Next track in iTunes",@"Next track button tool-tip")]; +} + +- (void) hideNextButtonImage +{ + [synergyMenuView hideNextButton]; +} + +- (void)updateMenu +{ + // has the effect of "batching" multiple changes to menu + [synergyGlobalMenu setMenuChangedMessagesEnabled:NO]; + + NSDictionary *song; + /* + + We will remove the old list of songs from the menu (all of them) first and + then re-add them. We do this because we do not know if any were deleted + (nor where they were in the list). + + I could easily do that in the timer method to make it more efficient, only + adding one song (and potentially deleting one song) per invocation. + + I think this might partially fix the glitch I see when iTunes changes + tracks while the menu is down. + + */ + + //remove songs from menu + for (NSMenuItem *item in [synergyGlobalMenu itemArray]) + { + if ([item action] == @selector(playSong:)) // ensures we only remove songs + [synergyGlobalMenu removeItem:item]; + } + + + //add songs from songList array to menu + + // make sure we don't exceed number of songs specified in + // _woNumberOfRecentlyPlayedTracksPrefKey + int permittedSongs = [[synergyPreferences objectOnDiskForKey:_woNumberOfRecentlyPlayedTracksPrefKey] intValue]; + + // cast to int safe because count always < 50 + if ((int)[songList count] > permittedSongs) + { + // remove songs that exceed the number specified in the prefs + NSRange objectsToDelete = NSMakeRange(permittedSongs, ([songList count] - permittedSongs)); + [songList removeObjectsInRange:objectsToDelete]; + } + + // enable "clear recent tracks" menu item if appropriate + if ([songList count] > 0 && [synergyPreferences objectOnDiskForKey:_woRecentlyPlayedSubmenuPrefKey]) + // we have at least one "recent track" + [clearRecentTracksMenuItem setEnabled:YES]; + else if (![[synergyPreferences objectOnDiskForKey:_woRecentlyPlayedSubmenuPrefKey] boolValue]) + // user doesn't want recent tracks menu -- hide those items? + // ugly! this will require us to move everything else... + [clearRecentTracksMenuItem setEnabled:NO]; + else + // no recent tracks (although user does want recent tracks menu) + [clearRecentTracksMenuItem setEnabled:NO]; + + // if and only if user wants to keep list of recently played songs... + if ([[synergyPreferences objectOnDiskForKey:_woRecentlyPlayedSubmenuPrefKey] boolValue]) + { + // add songs back into menu + NSEnumerator *enumerator = [songList reverseObjectEnumerator]; + NSMutableString *tempString = [NSMutableString string]; + while ((song = [enumerator nextObject])) + { + // start with two spaces for indenting + [tempString setString:@" "]; + + // add track title + [tempString appendString:[song objectForKey:WO_SONG_DICTIONARY_TITLE]]; + + // add artist if present and the preferences require it + if ([song objectForKey:WO_SONG_DICTIONARY_ARTIST] && + [[synergyPreferences objectOnDiskForKey:_woIncludeArtistInRecentTracksPrefKey] boolValue] && + ![[song objectForKey:WO_SONG_DICTIONARY_ARTIST] isEqualToString:@""]) + { + [tempString appendString:@" - "]; + [tempString appendString:[song objectForKey:WO_SONG_DICTIONARY_ARTIST]]; + } + + // add the menu item + NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:tempString action:@selector(playSong:) keyEquivalent:@""]; + [item setTarget:self]; + [synergyGlobalMenu insertItem:item atIndex:1]; + } + } + + // heaps of redundancy between here and the addGlobal method... + + // find out the index of the playlists submenu (will be -1 if removed) + int playlistsSubmenuIndex = [synergyGlobalMenu indexOfItemWithSubmenu:playlistsSubmenu]; + + // pointer to the NSMenuItem containing our submenu -- static because we + // want to use it across multiple invocations of this method + static id playlistsMenuItem; + + // hide playlists Submenu if necessary + if ([[synergyPreferences objectOnDiskForKey:_woPlaylistsSubmenuPrefKey] boolValue] == NO) + { + // test to see if we've already been removed on a previous pass + if (playlistsSubmenuIndex != -1) + { + // we've been removed previously -- ok to proceed + + // this is a pointer to the NSMenu object that contains the submenu + playlistsMenuItem = [synergyGlobalMenu itemAtIndex:playlistsSubmenuIndex]; + [synergyGlobalMenu removeItem:playlistsMenuItem]; + + // remove separator above submenu if there is one there (and there always will be) + if ([[synergyGlobalMenu itemAtIndex:(playlistsSubmenuIndex -1)] isSeparatorItem]) + [synergyGlobalMenu removeItem:[synergyGlobalMenu itemAtIndex:(playlistsSubmenuIndex -1)]]; + } + } + else + { + // prefs tell us to add submenu back in if it's been removed... + + // test to see if we've already been removed on a previous pass + if (playlistsSubmenuIndex == -1) + { + // appear two places above prefs item if iTunes menu is present + // or one place above it if not + int destinationIndex; + + if ([synergyGlobalMenu indexOfItemWithSubmenu:iTunesSubmenu] != -1) + // bumped upwards by 1 for 2.9 ("Transfer cover to iTunes" menu item) + destinationIndex = [synergyGlobalMenu indexOfItem:synergyPreferencesMenuItem] - 8; + else + // bumped upwards by 1 for 2.9 ("Transfer cover to iTunes" menu item) + destinationIndex = [synergyGlobalMenu indexOfItem:synergyPreferencesMenuItem] - 7; + + // because of removal on previous pass, playlistsMenuItem will + // contain a pointer to the removed item + [synergyGlobalMenu insertItem:playlistsMenuItem atIndex:destinationIndex]; + + // restore separator (above submenu) as well if it is missing + // note that we can always assume destinationIndex - 1 to be positive + // because the first couple of slots are always the recent tracks and clear recent tracks items + if(![[synergyGlobalMenu itemAtIndex:(destinationIndex - 1)] isSeparatorItem]) + [synergyGlobalMenu insertItem:[NSMenuItem separatorItem] atIndex:destinationIndex]; + } + } + + // find out the index of the iTunes submenu (will be -1 if removed) + int iTunesSubmenuIndex = [synergyGlobalMenu indexOfItemWithSubmenu:iTunesSubmenu]; + + // pointer to the NSMenuItem containing our submenu -- static because we + // want to use it across multiple invocations of this method + static id iTunesMenuItem; + + // hide iTunes submenu if necessary + if (![[synergyPreferences objectOnDiskForKey:_woLaunchQuitItemsPrefKey] boolValue]) + { + // test to see if we've already been removed on a previous pass + if (iTunesSubmenuIndex != -1) + { + // we've been removed previously -- ok to proceed + // this is a pointer to the NSMenu object that contains the submenu + iTunesMenuItem = [synergyGlobalMenu itemAtIndex:iTunesSubmenuIndex]; + [synergyGlobalMenu removeItem:iTunesMenuItem]; + + // remove separator above submenu if there is one there + if ([[synergyGlobalMenu itemAtIndex:(iTunesSubmenuIndex - 1)] isSeparatorItem]) + [synergyGlobalMenu removeItem:[synergyGlobalMenu itemAtIndex:(iTunesSubmenuIndex - 1)]]; + } + } + else + { + // prefs tell us to add submenu back in if it's been removed... + // test to see if we've already been removed on a previous pass + if (iTunesSubmenuIndex == - 1) + { + // appear one places above prefs item + // bumped upwards by 1 for 2.9 ("Transfer cover to iTunes" menu item) + int destinationIndex = [synergyGlobalMenu indexOfItem:synergyPreferencesMenuItem] - 7; + + // because of removal on previous pass, iTunesMenuItem will + // contain a pointer to the removed item + [synergyGlobalMenu insertItem:iTunesMenuItem atIndex:destinationIndex]; + } + + // update value of iTunesSubmenuIndex (will be different if we just re-inserted the menu) + iTunesSubmenuIndex = [synergyGlobalMenu indexOfItemWithSubmenu:iTunesSubmenu]; + + // make sure there's a separator above if one is required + if (iTunesSubmenuIndex != -1) + { + // iTunes submenu is now present (either re-enabled, or still enabled) + if ([synergyGlobalMenu indexOfItemWithSubmenu:playlistsSubmenu] == -1 && + ![[synergyGlobalMenu itemAtIndex:(iTunesSubmenuIndex - 1)] isSeparatorItem]) + // iTunes menu is there, playlists submenu not there, and there is no sep above, so add one + [synergyGlobalMenu insertItem:[NSMenuItem separatorItem] atIndex:iTunesSubmenuIndex]; + } + } + + // "Transfer cover to iTunes" menu + [transferCoverArtMenuItem setEnabled:([floaterController coverImage] ? YES : NO)]; + + // update iTunes submenu if it is visible + if ([synergyGlobalMenu indexOfItemWithSubmenu:iTunesSubmenu] != -1) + { + // menu is enabled so perform update + + switch (shuffleState) + { + case WOShuffleOn: + [shuffleMenuItem setState:NSOnState]; + break; + + case WOShuffleOff: + [shuffleMenuItem setState:NSOffState]; + break; + + case WOShuffleUnknown: + // do nothing + break; + + default: + // do nothing + break; + } + + switch (repeatMode) + { + case WORepeatAll: + + [repeatAllMenuItem setState:NSOnState]; // only this one is "on" + [repeatOffMenuItem setState:NSOffState]; + [repeatOneMenuItem setState:NSOffState]; + + break; + + case WORepeatOne: + + [repeatAllMenuItem setState:NSOffState]; + [repeatOffMenuItem setState:NSOffState]; + [repeatOneMenuItem setState:NSOnState]; // only this one is "on" + + break; + + case WORepeatOff: + + [repeatAllMenuItem setState:NSOffState]; + [repeatOffMenuItem setState:NSOnState]; // only this one is "on" + [repeatOneMenuItem setState:NSOffState]; + + break; + + case WORepeatUnknown: + // do nothing + break; + + default: + // do nothing + break; + } + + if ((iTunesState == ITUNES_NOT_RUNNING) || + (iTunesState == ITUNES_UNKNOWN)) + { + [launchQuitITunesMenuItem setTitle: + NSLocalizedString(@"Launch iTunes",@"Launch iTunes menu command")]; + + [activateITunesMenuItem setEnabled:NO]; + [shuffleMenuItem setEnabled:NO]; + [repeatOneMenuItem setEnabled:NO]; + [repeatAllMenuItem setEnabled:NO]; + [repeatOffMenuItem setEnabled:NO]; + } + else + { + [launchQuitITunesMenuItem setTitle: + NSLocalizedString(@"Quit iTunes",@"Quit iTunes menu command")]; + + [activateITunesMenuItem setEnabled:YES]; + [shuffleMenuItem setEnabled:YES]; + [repeatOneMenuItem setEnabled:YES]; + [repeatAllMenuItem setEnabled:YES]; + [repeatOffMenuItem setEnabled:YES]; + } + } + + [synergyGlobalMenu setMenuChangedMessagesEnabled:YES]; +} + + +// Append passed text to Play/Pause button's Tool-tip, in brackets: ( ) +- (void)updateTooltip:(NSString *)tooltipString +{ + NSString *beginTrackinfo = @"\n("; + NSString *endTrackinfo = @")"; + + NSString *trackinfoTooltip = [beginTrackinfo stringByAppendingString:tooltipString]; + NSString *completeTrackinfoTooltip = [trackinfoTooltip stringByAppendingString:endTrackinfo]; + + //NSString *playPauseTooltip; + + //if ([iTunesState intValue] == ITUNES_PLAYING) + if (iTunesState == ITUNES_PLAYING) + { + NSString *playPauseTooltip = [ + NSLocalizedString(@"Pause iTunes",@"Pause button tool-tip") + stringByAppendingString:completeTrackinfoTooltip]; + //[playPauseStatusItem setToolTip:playPauseTooltip]; + [synergyMenuView setPlayPauseTooltip:playPauseTooltip]; + } + //else if ([iTunesState intValue] == ITUNES_PAUSED) + else if (iTunesState == ITUNES_PAUSED) + { + NSString *playPauseTooltip = [ + NSLocalizedString(@"Resume iTunes playback",@"Resume playback (play button) tool-tip") + stringByAppendingString:completeTrackinfoTooltip]; + //[playPauseStatusItem setToolTip:playPauseTooltip]; + [synergyMenuView setPlayPauseTooltip:playPauseTooltip]; + } + //else if ([iTunesState intValue] == ITUNES_STOPPED) + else if (iTunesState == ITUNES_STOPPED) + { + NSString *playPauseTooltip = [ + NSLocalizedString(@"Play current iTunes track",@"Play button tool-tip") + stringByAppendingString:completeTrackinfoTooltip]; + //[playPauseStatusItem setToolTip:playPauseTooltip]; + [synergyMenuView setPlayPauseTooltip:playPauseTooltip]; + } + //else if ([iTunesState intValue] == ITUNES_UNKNOWN) + else if (iTunesState == ITUNES_UNKNOWN) + { + NSString *playPauseTooltip = [ + NSLocalizedString(@"Play/Pause iTunes",@"Play/pause button tool-tip") + stringByAppendingString:completeTrackinfoTooltip]; + //[playPauseStatusItem setToolTip:playPauseTooltip]; + [synergyMenuView setPlayPauseTooltip:playPauseTooltip]; + } + //else if ([iTunesState intValue] == ITUNES_ERROR) + else if (iTunesState == ITUNES_ERROR) + { + NSString *playPauseTooltip = [ + NSLocalizedString(@"Play/Pause iTunes",@"Play/pause button tool-tip") + stringByAppendingString:completeTrackinfoTooltip]; + //[playPauseStatusItem setToolTip:playPauseTooltip]; + [synergyMenuView setPlayPauseTooltip:playPauseTooltip]; + } + else + { + NSString *playPauseTooltip = [ + NSLocalizedString(@"Play/Pause iTunes",@"Play/pause button tool-tip") + stringByAppendingString:completeTrackinfoTooltip]; + [synergyMenuView setPlayPauseTooltip:playPauseTooltip]; + } + +} + +- (void) switchToPlayImage +{ +} + +- (void) switchToPauseImage +{ +} + +- (void) switchToPlayPauseImage +{ +} + +- (void) hidePlayPauseButton +{ +} + +- (void) showPlayPauseButton +{ +} + +- (void) hidePrevButton +{ +} + +- (void) showPrevButton +{ +} + +- (void) hideNextButton +{ +} + +- (void) showNextButton +{ +} + +- (void) hideGlobalMenu +{ +} + +- (void) showGlobalMenu +{ +} + +/* + + This method is called on when control hiding is activated. It checks which + buttons are active and disables them + + */ +- (void)hideActiveControls +{ + if ([[synergyPreferences objectOnDiskForKey:_woPrevButtonInMenuPrefKey] boolValue]) + [self hidePrevButtonImage]; + + if ([[synergyPreferences objectOnDiskForKey:_woPlayButtonInMenuPrefKey] boolValue]) + [self hidePlayPauseButtonImage]; + + if ([[synergyPreferences objectOnDiskForKey:_woNextButtonInMenuPrefKey] boolValue]) + [self hideNextButtonImage]; +} + +/* + + This method is called on when control hiding is activated. It checks which + buttons are active and enables (shows) them + + */ +- (void)showActiveControls +{ + if ([[synergyPreferences objectOnDiskForKey:_woPrevButtonInMenuPrefKey] boolValue] == NO) + [self showPrevButtonImage]; + + if ([[synergyPreferences objectOnDiskForKey:_woPlayButtonInMenuPrefKey] boolValue] == NO) + [self showPlayPauseButtonImage]; + + if ([[synergyPreferences objectOnDiskForKey:_woNextButtonInMenuPrefKey] boolValue] == NO) + [self showNextButtonImage]; +} + +- (void) timer:(NSTimer *)timer +{ + // this variable used as shorthand for floater "always on" status + BOOL floaterAlways = (BOOL)([[synergyPreferences objectOnDiskForKey:_woFloaterDurationPrefKey] floatValue] > 21.0); + + // let prefPane know that we're still running + [synergyPrefPane notifyPrefPane:WODNAppIsRunning]; + + // this is what we are going to try and retrieve from iTunes + NSString *playerState = nil; + NSAppleEventDescriptor *songId = nil; + NSString *songTitle = nil; + NSString *albumName = nil; + NSString *artistName = nil; + NSString *composerName = nil; + NSString *songDuration = nil; + NSString *year = nil; + + WORatingCode songRating = WO0StarRating; + + NSMutableDictionary *songDictionary = nil; + + // check if iTunes is running + if ([WOProcessManager processRunningWithSignature:'hook']) + { + /* + + 1. that it takes a second or two before the process manager sees that + iTunes isn't running + 2. that Apple events sent to a non-running app have no effect -- presumably + because the PSN isn't in use + 2b. when it's not running, no error is produced, so i can't test for + error status in order to ascertain if it's working + 3. that events sent to a just-launched app will be queued until the app + is eventually ready, and they get processed + 4. that apple events will never cause a respawn + 5. that apple script is the friggin culprit, because only the applescript + cause the respawn + 6. that i should reimplement the getInfoScript as an AppleEvent request + + */ + + if ([self iTunesReadyToReceiveAppleScript]) + { + NSAppleEventDescriptor *descriptor = [getSongInfoScript executeAndReturnError:NULL]; + + if (descriptor && [descriptor numberOfItems] == 1) + { + // result will be "error", "not running" or "not playing" + playerState = [descriptor stringValue]; + } + else if (descriptor && [descriptor numberOfItems] == 12) + { + // result should be "stopped", "playing" or "paused" + + // experimentation shows that if no selection the time the script + // is first run, it will from then on return a human-readable string + // + // in the reverse case, it returns the non-human-readable version! + + // hack for inconsistent AppleScript behaviour: + + // GCC 3.3. warnings ("non-ASCII character in CFString literal") + NSString *playingString = // build string: "«constant ****kPSP»" + [NSString stringWithFormat:@"%Cconstant ****kPSP%C", + WO_LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK_UNICODE_CHAR, + WO_RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK_UNICODE_CHAR]; + + NSString *pausedString = // build string: "«constant ****kPSp»" + [NSString stringWithFormat:@"%Cconstant ****kPSp%C", + WO_LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK_UNICODE_CHAR, + WO_RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK_UNICODE_CHAR]; + + NSString *stoppedString = // build string: "«constant ****kPSS»" + [NSString stringWithFormat:@"%Cconstant ****kPSS%C", + WO_LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK_UNICODE_CHAR, + WO_RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK_UNICODE_CHAR]; + + if ([[[descriptor descriptorAtIndex:1] stringValue] isEqualToString:playingString]) + playerState = @"playing"; + + // for now, consider "stopped" to be equivalent to "paused" + else if (([[[descriptor descriptorAtIndex:1] stringValue] isEqualToString:pausedString]) || + ([[[descriptor descriptorAtIndex:1] stringValue] isEqualToString:stoppedString]) || + ([[[descriptor descriptorAtIndex:1] stringValue] isEqualToString:@"stopped"])) + playerState = @"paused"; + + else + // we got a literal string like "stopped", "playing" or "paused" + playerState = [[descriptor descriptorAtIndex:1] stringValue]; + + + // this is a descriptor containing a parameter of a form like: + // file track id 9227 of user playlist id 9213 of source id 33 + // of application "iTunes" + songId = [descriptor descriptorAtIndex:2]; + songTitle = [[descriptor descriptorAtIndex:3] stringValue]; + + // even songTitle may be nil on Snow Leopard it would seem + // see: https://wincent.com/issues/1381 + if (!songTitle) + songTitle = @""; + + // check for nil values here (eg. Internet radio with missing tags) + + albumName = + [[descriptor descriptorAtIndex:4] stringValue]; + + if (!albumName) + albumName = @""; + + artistName = + [[descriptor descriptorAtIndex:5] stringValue]; + + if (!artistName) + artistName = @""; + + composerName = [[descriptor descriptorAtIndex:6] stringValue]; + if (!composerName) composerName = @""; + + songDuration = + [[descriptor descriptorAtIndex:7] stringValue]; + + if (!songDuration) + songDuration = @""; + + // year: equals "0" if not set + if([[[descriptor descriptorAtIndex:8] stringValue] isEqualToString:@"0"]) + year = @""; + else + { + year = [[descriptor descriptorAtIndex:8] stringValue]; + + if (year == nil) + year = @""; + } + + // song rating: convert it from a 0-100 integer into a 0-5 star + // rating + NSString *unconvertedSongRating = + [NSString stringWithString: + [[descriptor descriptorAtIndex:9] stringValue]]; + int convertedRating = [unconvertedSongRating intValue]; + + // http://wincent.com/a/support/bugs/show_bug.cgi?id=366 + if (convertedRating > 80) songRating = WO5StarRating; + else if (convertedRating > 60) songRating = WO4StarRating; + else if (convertedRating > 40) songRating = WO3StarRating; + else if (convertedRating > 20) songRating = WO2StarRating; + else if (convertedRating > 0) songRating = WO1StarRating; + else songRating = WO0StarRating; + + // repeat mode: should be "all", "one" or "off" + NSString *tempRepeatMode = + [[descriptor descriptorAtIndex:10] stringValue]; + + if ([tempRepeatMode isEqualToString:@"all"]) + // just write it straight to our ivar + repeatMode = WORepeatAll; + else if ([tempRepeatMode isEqualToString:@"one"]) + repeatMode = WORepeatOne; + else if ([tempRepeatMode isEqualToString:@"off"]) + repeatMode = WORepeatOff; + else + // temporarily commenting out this error msg (it is spewing + // out every time through the loop until I change the repeat + // mode once, then it stops) + repeatMode = WORepeatUnknown; + + // shuffle state: should be "true" or "false" + NSString *tempShuffleState = + [[descriptor descriptorAtIndex:11] stringValue]; + + if ([tempShuffleState isEqualToString:@"true"]) + shuffleState = WOShuffleOn; + else if ([tempShuffleState isEqualToString:@"false"]) + shuffleState = WOShuffleOff; + else + shuffleState = WOShuffleUnknown; + + // player position: integer 0, 1, 2 etc seconds + playerPosition = [[descriptor descriptorAtIndex:12] int32Value]; + } + else + { + playerState = [NSString stringWithString:@"error"]; + + songId = + [NSAppleEventDescriptor descriptorWithString:@"error"]; + + // set other variables to reasonable defaults + songTitle = @""; + albumName = @""; + artistName = @""; + composerName = @""; + songDuration = @""; + year = @""; + songRating = WO0StarRating; + repeatMode = WORepeatUnknown; + shuffleState = WOShuffleUnknown; + } + + } + else + { + // iTunes is NOT ready to receive the AppleScript! + + /* + + This is not considered grounds to classify iTunes as "not running" + because there can be transitory failures in which iTunes will not + respond to the Apple Event within the timeout simply because the + user is dragging the window (or something similar). + + So in these cases we do NOT classify things as "not running" or + because this could cause the Synergy + controls to be removed from the menu bar which might not be the + desired effect... + + */ + playerState = [NSString stringWithString:@"unknown"]; + } + + } + else + { + // well, it's not running, so let's try and avoid triggering an + // unwanted re-launch by pre-setting "result" as follows + playerState = [NSString stringWithString:@"not running"]; + } + + // now we have all the info we need from iTunes, so time to start processing + + if([playerState isEqualToString:@"not running"]) + { + NSString *errorMessage = [NSLocalizedString(@"Not running", + @"Not running tool-tip") + stringByAppendingString:@""]; + + // the new way... made in an effort to prevent these crashes + + // ALWAYS set state variable, not only on the first time we notice we're not running + // the hideControlsStatusItem method is smart enough not to re-hide the controls, + // even if we call it every single time the timer fires + iTunesState = ITUNES_NOT_RUNNING; + // but now we add another layer of conditionality to the removal of the controls + // only do it if control hiding is on AND we didn't get here by a button click + if (buttonClickOccurred == NO) + { + if ([[synergyPreferences objectOnDiskForKey: + _woControlHidingPrefKey] intValue]) + { + [self hideControlsStatusItem]; + + if (!globalMenuStatusItem && + [[synergyPreferences objectOnDiskForKey:_woGlobalMenuPrefKey] boolValue]) + [self addGlobalMenu]; + } + } + + [self updateTooltip:errorMessage]; + + // and in the custom view: + [synergyMenuView makePlayButtonShowPlayImage]; + + // if there is a floater showing... fade it out immediately + [floaterController fadeWindowOut:self]; + + // apply ghosting to forward and back buttons + //[synergyMenuView disableNextButton]; + //[synergyMenuView disablePrevButton]; + // I have decided to disable this feature because it could introduce a + // nasty and annoying lag into the system: iTunes is quit; user launches + // iTunes; buttons are ghosted and unusable for up to 10 seconds while + // waiting for next run of this timer method. + } + else if([playerState isEqualToString:@"not playing"]) + { + NSString *errorMessage = [NSLocalizedString( + @"Not playing", + @"iTunes not playing tool-tip") + stringByAppendingString:@""]; + + // update iTunes state variable + if (iTunesState != ITUNES_STOPPED) + { + // if appropriate, show controls: + if ([[synergyPreferences objectOnDiskForKey: + _woControlHidingPrefKey] intValue] + && ((iTunesState == ITUNES_NOT_RUNNING) || (iTunesState == ITUNES_UNKNOWN))) + { + // only show the controls if the user preferences dictate + // AND the transition is from the "not running" state, or the "unknown" state + // this method is smart enough not to re-show already shown controls... + + + [self showControlsStatusItem]; + + if(([[synergyPreferences objectOnDiskForKey:_woPlayButtonInMenuPrefKey] boolValue]) || + ([[synergyPreferences objectOnDiskForKey:_woPrevButtonInMenuPrefKey] boolValue]) || + ([[synergyPreferences objectOnDiskForKey:_woNextButtonInMenuPrefKey] boolValue])) + { + // could roll this into the if statement above, but leaving it here for readability + if (globalMenuStatusItem && + [[synergyPreferences objectOnDiskForKey:_woGlobalMenuOnlyWhenHiddenPrefKey] boolValue]) + { + // we were showing the global menu, but user wants to only show it when controls are hidden + [self removeGlobalMenu]; + } + } + + } + + // if appropriate, update playlists submenu + if ((iTunesState == ITUNES_NOT_RUNNING) || + (iTunesState == ITUNES_UNKNOWN)) + [self refreshPlaylistsSubmenu:nil]; + + // only do this if floater is set to appear "always", and furthermore + // by putting it in here, we make sure it only happens as we + // transition into the ITUNES_STOPPED state, instead of every time + // the timer fires + // problem with this is that if i pause playback, and then skip tracks, + // floater won't resize (? or does that occur in further down?) + if (floaterAlways) + { + + // make floater show: "Not playing" (will do same below for error condition) + + [floaterController setCurrentRating:WONoStarRatingDisplay]; + + [self updateFloaterStrings: + NSLocalizedString(@"iTunes status: not playing", + @"iTunes not playing floater status message") + album:@" " + artist:@" " + composer:@" "]; + + [floaterController setAlbumImagePath:nil]; + + // special case to handle bug 45 (floater not resizing when turned off) + // http://bugs.wincent.org/bugs/bug.php?op=show&bugid=45&pos=18 + if ([[synergyPreferences objectOnDiskForKey:_woShowNotificationWindowPrefKey] boolValue] == NO) + { + // not sure I need this here, because it should always get + // resized below in the clickDrivenUpdate/timerDrivenUpdate + // calls below + [floaterController resizeInstantly]; + } + + // this will update/resize the floater, but it won't put it onscreen + // if it is not already + [floaterController tellViewItNeedsToDisplay:self]; + + // make sure communications with floater aren't suspended + if (sendMessagesToFloater && floaterActive == YES) + { + // now send it all off to the notification window + if(buttonClickOccurred) + [floaterController clickDrivenUpdate]; + else + [floaterController timerDrivenUpdate]; + } + } + + iTunesState = ITUNES_STOPPED; + } + + [self updateTooltip:errorMessage]; + + // and in the custom view: + [synergyMenuView makePlayButtonShowPlayImage]; + + // remove ghosting from forward and back buttons + //[synergyMenuView enableNextButton]; + //[synergyMenuView enablePrevButton]; + + } + else if([playerState isEqualToString:@"error"]) + { + // we can get here when iTunes music store previews are playing, for example + NSString *errorMessage = [NSLocalizedString( + @"Error", + @"Error talking to iTunes tool-tip") + stringByAppendingString:@""]; + + // update iTunes state variable + + if (iTunesState != ITUNES_ERROR) + { + + + iTunesState = ITUNES_ERROR; + } + [self updateTooltip:errorMessage]; + + // and in the custom view: + [synergyMenuView makePlayButtonShowPlayPauseImage]; + + // apply ghosting to forward and back buttons + //[synergyMenuView disableNextButton]; + //[synergyMenuView disablePrevButton]; + + + } + else if (([playerState isEqualToString:@"playing"]) || + ([playerState isEqualToString:@"paused"])) + { + + // // this flag is used later on to determine if we are newly transitioning + // // into this state + // BOOL newTransitionToState; + // + // if ((iTunesState != ITUNES_PAUSED) && (iTunesState != ITUNES_PLAYING)) + // { + // newTransitionToState = YES; + // } + // else + // { + // newTransitionToState = NO; + // } + + // parts specific to playing and paused states: + + if ([playerState isEqualToString:@"paused"]) + { + // update iTunes state variable + if (iTunesState != ITUNES_PAUSED) + { + // if appropriate, show controls: + if ([[synergyPreferences objectOnDiskForKey: + _woControlHidingPrefKey] intValue] + && ((iTunesState == ITUNES_NOT_RUNNING)||(iTunesState == ITUNES_UNKNOWN))) + { + [self showControlsStatusItem]; + + if(([[synergyPreferences objectOnDiskForKey:_woPlayButtonInMenuPrefKey] boolValue]) || + ([[synergyPreferences objectOnDiskForKey:_woPrevButtonInMenuPrefKey] boolValue]) || + ([[synergyPreferences objectOnDiskForKey:_woNextButtonInMenuPrefKey] boolValue])) + { + // could roll this into the if statement above, but leaving it here for readability + if (globalMenuStatusItem && + [[synergyPreferences objectOnDiskForKey:_woGlobalMenuOnlyWhenHiddenPrefKey] boolValue]) + { + // we were showing the global menu, but user wants to only show it when controls are hidden + [self removeGlobalMenu]; + } + } + } + + // if appropriate, update playlists submenu + if ((iTunesState == ITUNES_NOT_RUNNING) || + (iTunesState == ITUNES_UNKNOWN)) + [self refreshPlaylistsSubmenu:nil]; + + iTunesState = ITUNES_PAUSED; + } + // and in the custom view: + [synergyMenuView makePlayButtonShowPlayImage]; + } + else + { + // update iTunes state variable + if (iTunesState != ITUNES_PLAYING) + { + // if appropriate, show controls: + if ([[synergyPreferences objectOnDiskForKey: + _woControlHidingPrefKey] intValue] + && ((iTunesState == ITUNES_NOT_RUNNING)||(iTunesState == ITUNES_UNKNOWN))) + { + [self showControlsStatusItem]; + + if(([[synergyPreferences objectOnDiskForKey:_woPlayButtonInMenuPrefKey] boolValue]) || + ([[synergyPreferences objectOnDiskForKey:_woPrevButtonInMenuPrefKey] boolValue]) || + ([[synergyPreferences objectOnDiskForKey:_woNextButtonInMenuPrefKey] boolValue])) + { + // could roll this into the if statement above, but leaving it here for readability + if (globalMenuStatusItem && + [[synergyPreferences objectOnDiskForKey:_woGlobalMenuOnlyWhenHiddenPrefKey] boolValue]) + { + // we were showing the global menu, but user wants to only show it when controls are hidden + [self removeGlobalMenu]; + } + } + } + + // if appropriate, update playlists submenu + if ((iTunesState == ITUNES_NOT_RUNNING) || + (iTunesState == ITUNES_UNKNOWN)) + [self refreshPlaylistsSubmenu:nil]; + + iTunesState = ITUNES_PLAYING; + } + // and in the custom view: + [synergyMenuView makePlayButtonShowPauseImage]; + } + + // parts shared between paused and playing states + + // construct songDictionary from components -- store just enough to + // uniquely identify the song + + songDictionary = + [NSMutableDictionary dictionaryWithCapacity:5]; + + // track identifier + [songDictionary setObject:songId + forKey:WO_SONG_DICTIONARY_ID]; + + // title + // was crashing here: https://wincent.com/issues/1381 + // should never be nil because I've added a check above, + // but double-check it here to be defensive + [songDictionary setObject:(songTitle ? songTitle : NSLocalizedString(@"Untitled", @"Untitled")) + forKey:WO_SONG_DICTIONARY_TITLE]; + + // artist + [songDictionary setObject:artistName + forKey:WO_SONG_DICTIONARY_ARTIST]; + + // for now, shoehorning support for album cover downloads into place + // by jamming in a WOSongInfo object + + // first, create the object + WOSongInfo *songInfo = [[WOSongInfo alloc] init]; + + // populate it + [songInfo setSong:songTitle]; + [songInfo setArtist:artistName]; + [songInfo setAlbum:albumName]; + + // store it in songDictionary + [songDictionary setObject:songInfo forKey:WO_SONG_DICTIONARY_SONGINFO]; + + NSFileManager *manager = [NSFileManager defaultManager]; + + // only attempt download if user preferences specify + if ([[synergyPreferences objectOnDiskForKey:_woFloaterGraphicType] intValue] == WOFloaterIconAlbumCover) + { + BOOL artSentToFloater = NO; + + // check disk first + NSString *filename = [songInfo filename]; + NSString *tempCoverPath = [[WOCoverDownloader tempAlbumCoversPath] stringByAppendingPathComponent:filename]; + NSString *coverPath = [[WOCoverDownloader albumCoversPath] stringByAppendingPathComponent:filename]; + + if (filename) // only do this if filename non-nil + { + if ([manager fileExistsAtPath:tempCoverPath]) + { + // notify floater + [floaterController setAlbumImagePath:tempCoverPath]; + artSentToFloater = YES; + } + else if ([manager fileExistsAtPath:coverPath]) + { + // notify floater + [floaterController setAlbumImagePath:coverPath]; + artSentToFloater = YES; + } + } + + // check if iTunes supplied cover art + if (artSentToFloater == NO) + { + // enclose this in an exception handling block because I've heard reports that this can crash + NS_DURING + + // old implementation actually stored the cover art in + // the descriptor every time through the loop + // new implemenation only tries grabbing it on demand: + + NSAppleEventDescriptor *coverDescriptor = nil; + NSAppleScript *coverScript; + static NSString *coverScriptSource = + @"tell application \"iTunes\"\n" + @" try\n" + @" if data of the artworks of the current track exists then\n" + @" return data of artwork 1 of current track as picture\n" + @" else\n" + @" return \"NO COVER\"\n" + @" end if\n" + @" on error\n" + @" return \"NO COVER\"\n" + @" end try\n" + @"end tell\n"; + + coverScript = [[NSAppleScript alloc] initWithSource:coverScriptSource]; + coverDescriptor = [coverScript executeAndReturnError:NULL]; + if (coverDescriptor && ![[coverDescriptor stringValue] isEqualToString:@"NO COVER"]) + { + NSData *coverData = [coverDescriptor data]; + NSImage *coverImage = nil; + if (coverData) + coverImage = [[NSImage alloc] initWithData:coverData]; + if (!coverImage) + [NSException raise:WO_ITUNES_ALBUM_COVER_TRANSFER_FAILURE + format:WO_ITUNES_ALBUM_COVER_TRANSFER_FAILURE_TEXT]; + + // save a copy to disk + NSData *coverDataAsTIFF = [coverImage TIFFRepresentationUsingCompression:NSTIFFCompressionLZW factor:1.0]; + NSBitmapImageRep *rep = [NSBitmapImageRep imageRepWithData:coverDataAsTIFF]; + NSNumber *quality = [NSNumber numberWithFloat:0.80]; + NSDictionary *properties = [NSDictionary dictionaryWithObject:quality + forKey:NSImageCompressionFactor]; + NSData *coverDataAsJPEG = [rep representationUsingType:NSJPEGFileType properties:properties]; + + // actually write out to disk + if (![coverDataAsJPEG writeToFile:tempCoverPath atomically:YES]) + [NSException raise:WO_ITUNES_ALBUM_COVER_TRANSFER_FAILURE + format:WO_ITUNES_ALBUM_COVER_TRANSFER_FAILURE_TEXT]; + + // notify floater + [floaterController setAlbumImagePath:tempCoverPath]; + artSentToFloater = YES; + } + NS_HANDLER + ELOG(@"Warning: Exception caught while attempting to process cover art data from iTunes"); + NS_ENDHANDLER + + } + + // alternate method: try downloading from amazon.com + if ((artSentToFloater == NO) && [WOCoverDownloader albumCoverExists:songInfo]) + { + // notify floater + [floaterController setAlbumImagePath:coverPath]; + artSentToFloater = YES; + } + + if (artSentToFloater == NO) + // notify floater + [floaterController setAlbumImagePath:nil]; + } + else + // don't display album image (user doesn't want it) + [floaterController setAlbumImagePath:nil]; + + BOOL enableMenu = NO; + + // do we have a "buy now" link? + if ([songInfo buyNowURL]) + // unghost the menu + enableMenu = YES; + else + { + // check to see if we have a "buy now" link stored on the disk for this song + NSString *buyNowFile = + [[[[WOCoverDownloader + albumCoversPath] stringByAppendingPathComponent:[songInfo filename]] stringByDeletingPathExtension] stringByAppendingPathExtension:@"plist"]; + + NSDictionary *buyNowInfo = [[NSDictionary alloc] initWithContentsOfFile:buyNowFile]; + if (buyNowInfo) + { + // we have the info from the disk + NSString *URLString = [buyNowInfo objectForKey:@"BuyNowLink"]; + + // stick it in the songInfo obj + if (URLString) + { + [songInfo setBuyNowURL:[NSURL URLWithString:URLString]]; + + // unghost the menu + enableMenu = YES; + } + } + } + + // somehow this wasn't getting set... + [buyFromAmazonMenuItem setEnabled:enableMenu]; + + NSString *extendedTitle; + NSString *extendedAlbum; + + // if prefs say so, add duration after song title + if ([[synergyPreferences objectOnDiskForKey:_woIncludeDurationInFloaterPrefKey] boolValue]) + extendedTitle = [[songTitle stringByAppendingString:@" - "] stringByAppendingString:songDuration]; + else + extendedTitle = songTitle; + + // if prefs say so, add year after album + if ([[synergyPreferences objectOnDiskForKey:_woIncludeYearInFloaterPrefKey] boolValue]) + { + NSMutableString *bracketedYear; + + if ([year isEqualToString:@""]) + bracketedYear = [NSMutableString stringWithString:@""]; + else + { + bracketedYear = [NSMutableString stringWithString:@" ("]; + [bracketedYear appendString:year]; + [bracketedYear appendString:@")"]; + } + extendedAlbum = [albumName stringByAppendingString:bracketedYear]; + } + else + extendedAlbum = albumName; + + // update star rating if the prefs say we should do so... + if (![[synergyPreferences objectOnDiskForKey:_woIncludeStarRatingInFloaterPrefKey] boolValue]) + [floaterController setCurrentRating:WONoStarRatingDisplay]; + else + [floaterController setCurrentRating:songRating]; + + // necessary to update floater strings here, just in case we + // have just started running and user presses "Show floater" + // hotkey + + [self updateFloaterStrings:extendedTitle + album:extendedAlbum + artist:artistName + composer:composerName]; + + // special case to handle bug 45 (floater not resizing when turned off) + // http://bugs.wincent.org/bugs/bug.php?op=show&bugid=45&pos=18 + if (![[synergyPreferences objectOnDiskForKey:_woShowNotificationWindowPrefKey] boolValue]) + // doesn't show it... only resizes it + [floaterController resizeInstantly]; + + // that method will handle missing values and also user preferences + // for display/non-display of specific parts + + // need to add a check here to stop us from running this every single frickin time through the loop + if (floaterAlways) + { + // we're supposed to show floater "always"... so we'd best make sure that it's showing + // make sure communications with floater aren't suspended + if (sendMessagesToFloater && floaterActive) + { + // now send it all off to the notification window + if (buttonClickOccurred) + [floaterController clickDrivenUpdate]; + else + { + // if window is not yet fully faded in, fade it in... + // here's the problem: when the window is at full alpha, + // it never resizes... + if ([floaterController windowAlphaValue] < 1.0) + [floaterController timerDrivenUpdate]; + else + { + // this cures the bug + BOOL oldAnimateWhileResizingValue = [floaterController animateWhileResizing]; + [floaterController setAnimateWhileResizing:YES]; + [floaterController resizeInstantly]; + [floaterController setAnimateWhileResizing:oldAnimateWhileResizingValue]; + } + } + } + } + + // this will update/resize the floater, but it won't put it onscreen + // if it is not already + [floaterController tellViewItNeedsToDisplay:self]; + + // use songDictionary to update songList array + if ([songList count] == 0) + // add first item to songlist + [songList addObject:songDictionary]; + else + { + /* + Check if title, artist or album have changed and update floater if necessary. This check is separate from the AppleEvent descriptor comparison that's used in the menu check immediately below, because otherwise we don't pick up track changes for Internet radio. + */ + NSDictionary *previousTrack = [songList objectAtIndex:0]; + WOSongInfo *previousTrackInfo = [previousTrack objectForKey:WO_SONG_DICTIONARY_SONGINFO]; + + if (![[songInfo song] isEqualToString:[previousTrackInfo song]] || + ![[songInfo artist] isEqualToString:[previousTrackInfo artist]] || + ![[songInfo album] isEqualToString:[previousTrackInfo album]]) + { + // at least one of song, artist or album have changed + + // if we're supposed to show the floater AND it's not set to show "always" then + if ([[synergyPreferences objectOnDiskForKey:_woShowNotificationWindowPrefKey] boolValue] && !floaterAlways) + // (the "always" case is handled above) + { + // make sure communications with floater aren't suspended + if (sendMessagesToFloater && floaterActive) + { + if (buttonClickOccurred) + [floaterController clickDrivenUpdate]; + else + [floaterController timerDrivenUpdate]; + } + } + } + + // add item to songlist, checking for duplicates + if (![[[[songDictionary objectForKey:WO_SONG_DICTIONARY_ID] data] description] isEqualToString: + [[[previousTrack objectForKey:WO_SONG_DICTIONARY_ID] data] description]]) + { + // Looks like it's not a dupe + BOOL duplicateFound = NO; + + // cast to int safe because count always < 50 + for (int i = 0; i < (int)[songList count]; i++) + { + if ([[[[[songList objectAtIndex:i] objectForKey:WO_SONG_DICTIONARY_ID] data] description] isEqualToString: + [[[songDictionary objectForKey:WO_SONG_DICTIONARY_ID] data] description]]) + { + duplicateFound = YES; + id moveSong = [songList objectAtIndex:i]; + + // pull it from list + [songList removeObjectAtIndex:i]; + + // re-insert same object at head of list + [songList insertObject:moveSong atIndex:0]; + + // optimisation: can safely assume that never more than + // one duplicate here + break; + } + } + + // if duplicate not found, add new entry + if (!duplicateFound) + [songList insertObject:songDictionary atIndex:0]; + } + } + + NSMutableString *tooltipString = [NSMutableString string]; + + if ([songTitle length] > 0) + [tooltipString setString:songTitle]; + + if ([albumName length] > 0) + { + // append separator first if necessary + if ([tooltipString length] > 0) + [tooltipString appendString:@" - "]; + + [tooltipString appendString:albumName]; + } + + if ([artistName length] > 0) + { + // append separator first if necessary + if ([tooltipString length] > 0) + [tooltipString appendString:@" - "]; + + [tooltipString appendString:artistName]; + } + + // a test which should never really succeed + if ([tooltipString length] == 0) + // test if length of entire string is zero, so we have no song name, + // and nothing for tool tip + [tooltipString setString:NSLocalizedString(@"No track name available",@"No track name available")]; + + [self updateTooltip:tooltipString]; + } + else + { + // update iTunes state variable + if (iTunesState != ITUNES_UNKNOWN) + iTunesState = ITUNES_UNKNOWN; + // do not update control hiding status here because app is in an unknown state + + [self updateTooltip:playerState]; + } + + [self updateMenu]; + + // reset buttondriven flag + buttonClickOccurred = NO; +} + +- (void)updateFloaterStrings:(NSString *)songTitle + album:(NSString *)albumName + artist:(NSString *)artistName + composer:(NSString *)composerName +{ + NSString *tempAlbumName = @""; + NSString *tempArtistName = @""; + NSString *tempComposerName = @""; + + BOOL album = [[synergyPreferences objectOnDiskForKey:_woIncludeAlbumInFloaterPrefKey] boolValue]; + BOOL artist = [[synergyPreferences objectOnDiskForKey:_woIncludeArtistInFloaterPrefKey] boolValue]; + BOOL composer = [[synergyPreferences objectOnDiskForKey:_woIncludeComposerInFloaterPrefKey] boolValue]; + + if (album && albumName) + tempAlbumName = [NSString stringWithString:albumName]; + + if (artist && artistName) + tempArtistName = [NSString stringWithString:artistName]; + + if (composer && composerName) + tempComposerName = [NSString stringWithString:composerName]; + + [floaterController setStrings:songTitle + album:tempAlbumName + artist:tempArtistName + composer:tempComposerName]; +} + +- (void) playPause:(id)sender +{ + [self tellITunesPlayPause]; +} + +- (void) nextTrack:(id)sender +{ + [self tellITunesNext]; +} + +/* + Consider putting a mod in here to instantly update the image... I know that when the main + timer fires it will get updated anyway, but this could make it appear more responsive. + + If do this then should also probably put a status check in the timer method so that it + doesn't redundantly set the image... eg. if already set, then don't bother to set + */ + +- (void) tellITunesPlayPause; +{ + // tell iTunes to play using Apple Events + + ProcessSerialNumber iTunesPSN = [WOProcessManager PSNForSignature:'hook']; + + if ([WOProcessManager PSNEqualsNoProcess:iTunesPSN]) + { + // open iTunes using workspace; specifying .app here ensures that OS 9 + // iTunes doesn't get opened -- fixes: + // http://bugs.wincent.org/bugs/bug.php?op=show&bugid=27 + if ([[NSWorkspace + sharedWorkspace] launchApplication:@"iTunes.app"] == NO) + ELOG(@"Error attempting to launch iTunes"); + else + { + // ask to be notified when iTunes finishes launching, and THEN tell + // it to play + waitingForITunesToLaunch = YES; + + if (![self iTunesSendsNotifications]) + [[[NSWorkspace sharedWorkspace] + notificationCenter] addObserver:self + selector:@selector(iTunesDidLaunchNowPlay:) + name:@"NSWorkspaceDidLaunchApplicationNotification" + object:nil]; + } + } + else + // Equivalent to: tell application "iTunes" to playpause + [self sendAppleEventClass:'hook' ID:'PlPs']; + + buttonClickOccurred = YES; // a control button clicked? + + // but only fire main timer if iTunes was found in process list? + [self timer:nil]; +} + +- (void)iTunesDidLaunchNowPlay:(NSNotification *)notification +{ + // an application has launched... not necessarily iTunes, so check to make + // sure that it has indeed launched + + // we could check this the using Carbon (via our WOProcessManager wrapper) + // but I prefer to use NSWorkspace here because it is NSWorkspace that + // posted the notification! (I have a suspicion that Carbon might be able + // to "see" processes sooner than NSWorkspace, perhaps too soon for iTunes + // to be actually ready to receive Apple Events) + + NSArray *launchedApplications = + [[NSWorkspace sharedWorkspace] launchedApplications]; + + NSEnumerator *appEnumerator = [launchedApplications objectEnumerator]; + id application; + + while ((application = [appEnumerator nextObject])) + { + if ([[application objectForKey:@"NSApplicationName"] isEqualToString:@"iTunes"]) + { + if (![self iTunesSendsNotifications]) + [[[NSWorkspace sharedWorkspace] + notificationCenter] removeObserver:self + name:@"NSWorkspaceDidLaunchApplicationNotification" + object:nil]; + + if ([self songToPlayOnceLaunched]) + { + // use special method (user probably selected from global menu) + [self tellITunesToPlaySong:[self songToPlayOnceLaunched]]; + [self setSongToPlayOnceLaunched:nil]; + } + else + { + // use generic method + [self tellITunesPlayPause]; + } + + break; + } + } +} + +// use: [self sendAppleEventClass:'hook' ID:'Next']; +- (void)sendAppleEventClass:(AEEventClass)eventClass ID:(AEEventID)eventID +{ + ProcessSerialNumber iTunesPSN = [WOProcessManager PSNForSignature:'hook']; + if ([WOProcessManager PSNEqualsNoProcess:iTunesPSN] == NO) + { + AppleEvent event, reply; + AEDesc descriptor; + if (AECreateDesc(typeProcessSerialNumber, &iTunesPSN, sizeof(iTunesPSN), &descriptor) == noErr) + { + if (AECreateAppleEvent(eventClass, eventID, &descriptor, kAutoGenerateReturnID, kAnyTransactionID, &event) == noErr) + { + if (AESend(&event, &reply, kAENoReply, kAENormalPriority, kAEDefaultTimeout, nil, nil) == noErr) + AEDisposeDesc(&reply); + else + ELOG(@"Error (%d) sending Apple Event", noErr); + + AEDisposeDesc(&event); + } + AEDisposeDesc(&descriptor); + } + } +} + +// tell iTunes to go to "next" +- (void)tellITunesNext +{ + // Equivalent to: tell application "iTunes" next track + [self sendAppleEventClass:'hook' ID:'Next']; + buttonClickOccurred = YES; // a hot-key was pressed + + if (![self iTunesSendsNotifications]) + [mainTimer fire]; +} + +// this code almost identical to the previous method; should re-factor +- (void) tellITunesFastForward +{ + // Equivalent to: tell application "iTunes" fast forward + // (opposite Apple Event is 'Rwnd') + [self sendAppleEventClass:'hook' ID:'Fast']; +} + +- (void)tellITunesPrev; +{ + // depending on user prefs, end either "back" or "prev" + if ([[synergyPreferences objectOnDiskForKey:_woPrevActionSameAsITunesPrefKey] boolValue]) + // Equivalent to: tell application "iTunes" back track + [self sendAppleEventClass:'hook' ID:'Back']; + else + // Equivalent to: tell application "iTunes" prev track + [self sendAppleEventClass:'hook' ID:'Prev']; + + buttonClickOccurred = YES; // a control button clicked? + if (![self iTunesSendsNotifications]) + [mainTimer fire]; +} + +// this code almost identical to the tellITunesNext method +- (void)tellITunesRewind +{ + [self sendAppleEventClass:'hook' ID:'Rwnd']; +} + +//- (IBAction) prevTrack:(id)sender +- (void) prevTrack:(id)sender +{ + [self tellITunesPrev]; +} + +- (IBAction)clearRecentSongs:(id)sender +{ + [songList removeAllObjects]; + for (NSMenuItem *item in [synergyGlobalMenu itemArray]) + { + if ([item action] == @selector(playSong:)) + [synergyGlobalMenu removeItem:item]; + } + [mainTimer fire]; +} + +- (void) cleanupBeforeExit +{ + // break down link with app + [synergyPrefPane notifyPrefPane:WODNAppWillQuit]; + [synergyPrefPane removePrefPaneObserver]; + floaterController = nil; + + if (controlsStatusItem != nil) + { + [self hideControlsStatusItem]; + controlsStatusItem = nil; + } + + if (globalMenuStatusItem != nil) + [self removeGlobalMenu]; + + songList = nil; + + if (getSongInfoScript != nil) + getSongInfoScript = nil; + + if (mainTimer != nil) + { + [mainTimer invalidate]; + mainTimer = nil; + } + + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + // clean out "Temporary Album Covers" + if (![[NSFileManager defaultManager] removeItemAtPath:[WOCoverDownloader tempAlbumCoversPath] + error:NULL]) + NSLog(@"Error while cleaning out Synergy \"Temporary Album Covers\" folder"); +} + +-(IBAction)playSong:(id)sender +{ + NSAppleEventDescriptor *songId; + + // adjust this value by one because the first item is just the "Recent + // tracks" label + int index = ([[sender menu] indexOfItem:sender] - 1); + + songId = [[songList objectAtIndex:index] objectForKey:WO_SONG_DICTIONARY_ID]; + + ProcessSerialNumber iTunesPSN = [WOProcessManager PSNForSignature:'hook']; + + //we'll have to launch iTunes if it's not running + if ([WOProcessManager PSNEqualsNoProcess:iTunesPSN]) + { + // open iTunes using workspace; specifying .app here ensures that OS 9 + // iTunes doesn't get opened -- attempted fix for: + // http://bugs.wincent.org/bugs/bug.php?op=show&bugid=27 + if ([[NSWorkspace sharedWorkspace] launchApplication:@"iTunes.app"] == NO) + ELOG(@"Error attempting to launch iTunes"); + else + { + // store song descriptor + [self setSongToPlayOnceLaunched:songId]; + + // ask to be notified when iTunes finishes launching, and THEN tell + // it to play + waitingForITunesToLaunch = YES; + + if (![self iTunesSendsNotifications]) + [[[NSWorkspace sharedWorkspace] + notificationCenter] addObserver:self + selector:@selector(iTunesDidLaunchNowPlay:) + name:@"NSWorkspaceDidLaunchApplicationNotification" + object:nil]; + + } + } + else + { + + AppleEvent event, reply; + AEDesc theDescriptor; + + // this method does no testing to see if iTunes is running -- that should + // be done before calling! + + if (AECreateDesc(typeProcessSerialNumber, &iTunesPSN, sizeof(iTunesPSN), + &theDescriptor) == noErr) + { + // Equivalent to: tell application "iTunes" to play + if (AECreateAppleEvent('hook', 'Play', &theDescriptor, + kAutoGenerateReturnID, kAnyTransactionID, + &event) == noErr) + { + const DescType keyword = keyDirectObject; // 'form'; was 'obj ' + const AEDesc *thePointer = [songId aeDesc]; + + if ((AEPutParamDesc(&event, keyword, thePointer)) != noErr) + ELOG(@"Error putting parameter in Apple Event"); + else + { + if (AESend(&event, &reply, kAENoReply, kAENormalPriority, + kAEDefaultTimeout, nil, nil) == noErr) + AEDisposeDesc(&reply); + else + ELOG(@"Error sending Apple Event"); + } + AEDisposeDesc(&event); + } + AEDisposeDesc(&theDescriptor); + } + + buttonClickOccurred = YES; // a menu item was chosen + if (![self iTunesSendsNotifications]) + [mainTimer fire]; + } +} + +- (void)tellITunesToPlaySong:(NSAppleEventDescriptor *)descriptor +{ + ProcessSerialNumber iTunesPSN = [WOProcessManager PSNForSignature:'hook']; + + if ([WOProcessManager PSNEqualsNoProcess:iTunesPSN] == NO) + { + // iTunes is running + AppleEvent event, reply; + AEDesc theDescriptor; + + // this method does no testing to see if iTunes is running -- that should + // be done before calling! + + if (AECreateDesc(typeProcessSerialNumber, &iTunesPSN, sizeof(iTunesPSN), + &theDescriptor) == noErr) + { + // Equivalent to: tell application "iTunes" to play + if (AECreateAppleEvent('hook', 'Play', &theDescriptor, + kAutoGenerateReturnID, + kAnyTransactionID, &event) == noErr) + { + const DescType keyword = keyDirectObject; // 'form'; was 'obj ' + const AEDesc *thePointer = [descriptor aeDesc]; + + if (AEPutParamDesc(&event, keyword, thePointer) != noErr) + ELOG(@"Error putting parameter in Apple Event"); + + if (AESend(&event, &reply, kAENoReply, kAENormalPriority, + kAEDefaultTimeout, nil, nil) == noErr) + AEDisposeDesc(&reply); + else + ELOG(@"Error sending Apple Event"); + + AEDisposeDesc(&event); + } + AEDisposeDesc(&theDescriptor); + } + + buttonClickOccurred = YES; // a menu item was chosen + if (![self iTunesSendsNotifications]) + [mainTimer fire]; + } +} + +-(IBAction)clearRecentSongsMenuItem:(id)sender +{ + [songList removeAllObjects]; + [self updateMenu]; +} + +-(IBAction)openPrefsMenuItem:(id)sender +{ + // we start at something like: + // /Applications/ + // Synergy Preferences.app/ + // Contents/ + // PreferencePanes/ + // Synergy.prefPane/ + // Contents/ + // Helpers/ + // Synergy.app + // so want to strip off the last 6 path components to get the path to the + // preferences app (/Applications/Synergy Preferences.app) + NSString *path = [[NSBundle mainBundle] bundlePath]; + for (unsigned i = 0; i < 6; i++) + path = [path stringByDeletingLastPathComponent]; + if (!path || [path length] == 0) + NSLog(@"Unable to get path to preferences application"); + else if (![[NSWorkspace sharedWorkspace] openFile:path]) + NSLog(@"Error while launching path: %@", path); +} + +- (void)launchITunes +{ + // open iTunes using workspace + if ([[NSWorkspace sharedWorkspace] launchApplication:@"iTunes.app"] == NO) + ELOG(@"Error attempting to launch iTunes"); +} + +- (IBAction)shuffleMenuItem:(id)sender +{ + // (double) check if iTunes is running + if ([WOProcessManager processRunningWithSignature:'hook']) + { + NSAppleScript *script; + NSString *result; + + if ([shuffleMenuItem state] == NSOffState) + { + // shuffle (menu) was off: turn it on + + static NSString *scriptSource = + @"tell application \"iTunes\"\n" + @" try\n" + @" -- this will fail if iTunes has no current selection\n" + @" set currentPlaylist to the container of the current track\n" + @" set shuffle of currentPlaylist to true\n" + @" return \"SUCCESS\"\n" + @" on error\n" + @" return \"ERROR\"\n" + @" end try\n" + @"end tell"; + + script = [[NSAppleScript alloc] initWithSource:scriptSource]; + result = [[NSString alloc] initWithString:[[script executeAndReturnError:NULL] stringValue]]; + + if (result && [result isEqualToString:@"SUCCESS"]) + { + [shuffleMenuItem setState:NSOnState]; + } + else if (result && [result isEqualToString:@"ERROR"]) + ELOG(@"Error setting iTunes shuffle setting to ON"); + else + ELOG(@"Unknown error while setting iTunes shuffle setting to ON"); + } + else + { + // shuffle (menu) was on: turn it off + + static NSString *scriptSource = + @"tell application \"iTunes\"\n" + @" try\n" + @" -- this will fail if iTunes has no current selection\n" + @" set currentPlaylist to the container of the current track\n" + @" set shuffle of currentPlaylist to false\n" + @" return \"SUCCESS\"\n" + @" on error\n" + @" return \"ERROR\"\n" + @" end try\n" + @"end tell"; + + script = [[NSAppleScript alloc] initWithSource:scriptSource]; + result = [[NSString alloc] initWithString:[[script executeAndReturnError:NULL] stringValue]]; + if (result && [result isEqualToString:@"SUCCESS"]) + [shuffleMenuItem setState:NSOffState]; + else if (result && [result isEqualToString:@"ERROR"]) + ELOG(@"Error setting iTunes shuffle setting to OFF"); + else + ELOG(@"Unknown error while setting iTunes shuffle setting to OFF"); + } + } +} + +- (IBAction)repeatOffMenuItem:(id)sender +{ + // (double) check if iTunes is running + if ([WOProcessManager processRunningWithSignature:'hook']) + { + static NSString *scriptSource = + @"tell application \"iTunes\"\n" + @" try\n" + @" -- this will fail if iTunes has no current selection\n" + @" set currentPlaylist to the container of the current track\n" + @" set song repeat of currentPlaylist to off\n" + @" return \"SUCCESS\"\n" + @" on error\n" + @" return \"ERROR\"\n" + @" end try\n" + @"end tell"; + + NSAppleScript *script = [[NSAppleScript alloc] initWithSource:scriptSource]; + NSString *result = [[NSString alloc] initWithString:[[script executeAndReturnError:NULL] stringValue]]; + + if (result && [result isEqualToString:@"SUCCESS"]) + { + [repeatAllMenuItem setState:NSOffState]; + [repeatOffMenuItem setState:NSOnState]; // only this one is "on" + [repeatOneMenuItem setState:NSOffState]; + } + else if (result && [result isEqualToString:@"ERROR"]) + ELOG(@"Error setting repeat mode to OFF"); + else + ELOG(@"Unknown error while setting repeat mode to OFF"); + } +} + +- (IBAction)repeatAllMenuItem:(id)sender +{ + // (double) check if iTunes is running + if ([WOProcessManager processRunningWithSignature:'hook']) + { + NSAppleScript *script; + NSString *result; + + static NSString *scriptSource = + @"tell application \"iTunes\"\n" + @" try\n" + @" -- this will fail if iTunes has no current selection\n" + @" set currentPlaylist to the container of the current track\n" + @" set song repeat of currentPlaylist to all\n" + @" return \"SUCCESS\"\n" + @" on error\n" + @" return \"ERROR\"\n" + @" end try\n" + @"end tell"; + + script = [[NSAppleScript alloc] initWithSource:scriptSource]; + result = [[NSString alloc] initWithString: + [[script executeAndReturnError:NULL] stringValue]]; + + if (result && [result isEqualToString:@"SUCCESS"]) + { + [repeatAllMenuItem setState:NSOnState]; // only this one is "on" + [repeatOffMenuItem setState:NSOffState]; + [repeatOneMenuItem setState:NSOffState]; + } + else if (result && [result isEqualToString:@"ERROR"]) + ELOG(@"Error setting repeat mode to ALL"); + else + ELOG(@"Unknown error while setting repeat mode to ALL"); + } +} + +- (IBAction)repeatOneMenuItem:(id)sender +{ + if ([WOProcessManager processRunningWithSignature:'hook']) // (double) check if iTunes is running + { + NSAppleScript *script; + NSString *result; + + static NSString *scriptSource = + @"tell application \"iTunes\"\n" + @" try\n" + @" -- this will fail if iTunes has no current selection\n" + @" set currentPlaylist to the container of the current track\n" + @" set song repeat of currentPlaylist to one\n" + @" return \"SUCCESS\"\n" + @" on error\n" + @" return \"ERROR\"\n" + @" end try\n" + @"end tell"; + + script = [[NSAppleScript alloc] initWithSource:scriptSource]; + result = [[NSString alloc] initWithString: + [[script executeAndReturnError:NULL] stringValue]]; + + if (result && [result isEqualToString:@"SUCCESS"]) + { + [repeatAllMenuItem setState:NSOffState]; + [repeatOffMenuItem setState:NSOffState]; + [repeatOneMenuItem setState:NSOnState]; // only this one is "on" + } + else if (result && [result isEqualToString:@"ERROR"]) + ELOG(@"Error setting repeat mode to ONE"); + else + ELOG(@"Unknown error while setting repeat mode to ONE"); + } +} + +- (IBAction)activateITunesMenuItem:(id)sender +{ + [self tellITunesActivate]; +} + +- (IBAction)quitLaunchITunesMenuItem:(id)sender +{ + // if iTunes is not launched, launch it + if((iTunesState == ITUNES_NOT_RUNNING) || (iTunesState == ITUNES_UNKNOWN)) + { + // open iTunes using workspace + [self launchITunes]; + + [launchQuitITunesMenuItem setTitle: + NSLocalizedString(@"Quit iTunes",@"Quit iTunes menu command")]; + + [activateITunesMenuItem setEnabled:YES]; + [shuffleMenuItem setEnabled:YES]; + [repeatOneMenuItem setEnabled:YES]; + [repeatAllMenuItem setEnabled:YES]; + [repeatOffMenuItem setEnabled:YES]; + } + else + { + // if iTunes is running, quit it + // (ie. STOPPED, PAUSED, PLAYING, ERROR) + + // tell iTunes to quit using Apple Events + [self sendAppleEventClass:'aevt' ID:'quit']; + [launchQuitITunesMenuItem setTitle: + NSLocalizedString(@"Launch iTunes",@"Launch iTunes menu command")]; + + [activateITunesMenuItem setEnabled:NO]; + [shuffleMenuItem setEnabled:NO]; + [repeatOneMenuItem setEnabled:NO]; + [repeatAllMenuItem setEnabled:NO]; + [repeatOffMenuItem setEnabled:NO]; + } + + + // must update menu item here (rather than firing mainTimer to do it), + // because if we call the timer straight away, iTunes will launch again + // immediately + + + // re-enable timer? -- or will it again fire too soon? +} + +- (IBAction)refreshPlaylistsSubmenu:(id)sender +{ + // only do this is iTunes is running + ProcessSerialNumber iTunesPSN = [WOProcessManager PSNForSignature:'hook']; + + // but allow user to force an update if the update is triggered by selecting a menu item + BOOL forceUpdate = sender ? [sender isKindOfClass:[NSMenuItem class]] : NO; + if ([WOProcessManager PSNEqualsNoProcess:iTunesPSN] && !forceUpdate) + { + // iTunes is not running + // + // effectively, this means that if the user launches Synergy and iTunes + // is not running, then the playlists menu will contain only a separator + // and a "Refresh" item + // + // if the menu has already been populated, then the pre-existing entries will remain + // + // if the menu is not populated, will remove the separator to make it look nice + if ([playlistsSubmenu numberOfItems] == 2 && [[playlistsSubmenu itemAtIndex:0] isSeparatorItem]) + // we have only a separator and "Refresh", remove the separator + [playlistsSubmenu removeItemAtIndex:0]; + } + else // iTunes is running: do a proper update + { + NSArray *names = nil; + @try + { + // do this the hard way -- [[iTunes sources] objectWithName:@"Library"] -- only works in English + iTunesApplication *iTunes = [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"]; + iTunesSource *library = nil; + for (iTunesSource *source in [iTunes sources]) + { + if ([source kind] == iTunesESrcLibrary) + { + library = source; + break; + } + } + names = [[library playlists] arrayByApplyingSelector:@selector(name)]; + } + @catch (id e) + { + // we don't want a mere Apple Event error like this one derailing the entire applicaton: + // *** Terminating app due to uncaught exception 'NSGenericException', + // reason: 'Apple event returned an error. Event = 'core'\'cnte'{ '----':'null'(), 'kocl':'cSrc' } + // Error info = { ErrorNumber = -609; } + // incidentally, error 609 may be "connection is invalid" or a timeout ("Apple Event timed out") + // "there's a glitch in Apple's APIs that cause timeouts to sometimes raise error -609 instead of the usual -1712" + // see: http://discussions.apple.com/thread.jspa?messageID=6925244 + // and: http://developer.apple.com/documentation/AppleScript/Conceptual/AppleScriptLangGuide/index.html + names = [NSArray array]; + } + + // clear out existing entries in playlist submenu + for (NSMenuItem *item in [playlistsSubmenu itemArray]) + { + // only remove playlist entries (not separators, "Refresh" etc) + if ([item action] == @selector(selectPlaylist:)) + [playlistsSubmenu removeItem:item]; + } + + // make sure we have a separator, but only if we need one + if ([playlistsSubmenu numberOfItems] == 1 && names.count > 0) + [playlistsSubmenu insertItem:[NSMenuItem separatorItem] atIndex:0]; + + // add back in new entries, building menu in reverse order (inserting items at the top of the menu) + for (NSUInteger i = names.count; i > 0; i--) + { + NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[names objectAtIndex:i - 1] + action:@selector(selectPlaylist:) + keyEquivalent:@""]; + [item setTarget:self]; + [playlistsSubmenu insertItem:item atIndex:0]; + } + } +} + +// switches to a given playlist and starts playing +- (IBAction)selectPlaylist:(id)sender +{ + // note to self: do I have to strip out quote characters? escape them i mean + // do I run into strife with foreign characters eg: + // "Canciones españolas" returned as "Canciones espa\\361olas" + + // ensure sender is an NSMenuItem object + if (![sender isKindOfClass:[NSMenuItem class]]) + return; + + NSAppleScript *script = nil; + NSAppleEventDescriptor *result = nil; + + NSMutableString *playlistName = + [NSMutableString stringWithString:[sender title]]; + + // unfortunately the escaping in the following wreaks havoc with + // Project Builder's auto-indenting: + [playlistName replaceOccurrencesOfString:@"\"" /* replace " */ + withString:@"\\\"" /* with \" */ + options:NSBackwardsSearch + range:NSMakeRange(0, [playlistName length])]; + +NSString *activate; + +// user prefs will dictate whether iTunes is brought to the front or not +if ([[synergyPreferences objectOnDiskForKey:_woBringITunesToFrontPrefKey] boolValue]) +activate = @"activate"; +else +activate = @"--activate"; + + NSString *source = + [NSString stringWithFormat: + @"tell application \"iTunes\"\n" + @" try\n" + @" stop\n" + @" set thePlaylist to playlist \"%@\"\n" + @" %@\n" + @" set visible of browser window 1 to true\n" + @" set view of browser window 1 to thePlaylist\n" + @" play\n" + @" return \"SUCCESS\"\n" + @" on error\n" + @" return \"ERROR\"\n" + @" end try\n" + @"end tell", + playlistName, + activate]; + + script = [[NSAppleScript alloc] initWithSource:source]; + + result = [script executeAndReturnError:NULL]; + + if (!result) + { + // error + } + else + { + if ([[result stringValue] isEqualToString:@"ERROR"]) + ELOG(@"Warning: AppleScript error while attempting to switch to " + @"playlist \"%@\"", playlistName); + } +} + +- (IBAction)showAlbumCoversFolder:(id)sender +{ + NSString *coversPath = [WOCoverDownloader albumCoversPath]; + NSURL *coversURL = [NSURL fileURLWithPath:coversPath]; + NSString *albumPath = [floaterController albumImagePath]; + + // if we have an album cover image, reveal and select that + if (albumPath) + if ([[NSWorkspace sharedWorkspace] selectFile:albumPath + inFileViewerRootedAtPath:coversPath]) + return; + + // fallback case: just open the Album Covers folder + if(![[NSWorkspace sharedWorkspace] openURL:coversURL]) + ELOG(@"Error: Could not open \"%@\"", coversPath); +} + +- (IBAction)quitSynergyMenuItem:(id)sender +{ + [self cleanupBeforeExit]; // this will work + exit(0); // this ugly old way... +} + +// after reading preferences, tell floater how we want it to appear +- (void)configureFloater +{ + // if user hand-edits preferences plist and inserts non-numeric value + // these calls won't work + [floaterController setFloaterIconType: + [[synergyPreferences objectOnDiskForKey:_woFloaterGraphicType] intValue]]; + + [floaterController setDelayBeforeFade: + [[synergyPreferences objectOnDiskForKey:_woFloaterDurationPrefKey] floatValue]]; + + [floaterController setTransparency: + [[synergyPreferences objectOnDiskForKey:_woFloaterTransparencyPrefKey] floatValue]]; + + [floaterController setFgColor:[[synergyPreferences objectOnDiskForKey:_woFloaterForegroundColorPrefKey] floatValue]]; + + [floaterController setBgColor:[[synergyPreferences objectOnDiskForKey:_woFloaterBackgroundColorPrefKey] floatValue]]; + + //[floaterController setBgOpacity:[[synergyPreferences objectOnDiskForKey:_woFloaterBackgroundOpacityPrefKey] floatValue]]; + + [floaterController setSize: + [[synergyPreferences objectOnDiskForKey:_woFloaterSizePrefKey] intValue]]; + + [floaterController setWindowOffset: + NSMakePoint([[synergyPreferences objectOnDiskForKey:_woFloaterHorizontalOffset] floatValue], + [[synergyPreferences objectOnDiskForKey:_woFloaterVerticalOffset] floatValue])]; + + [floaterController setXScreenSegment: + [[synergyPreferences objectOnDiskForKey:_woFloaterHorizontalSegment] intValue]]; + + [floaterController setYScreenSegment: + [[synergyPreferences objectOnDiskForKey:_woFloaterVerticalSegment] intValue]]; + + [floaterController setScreenNumber: + [[synergyPreferences objectOnDiskForKey:_woScreenIndex] intValue]]; + + // move the floater (doesn't display it, just moves it) + [floaterController moveGivenOffset:NSMakePoint([[synergyPreferences objectOnDiskForKey:_woFloaterHorizontalOffset] floatValue], + [[synergyPreferences objectOnDiskForKey:_woFloaterVerticalOffset] floatValue]) + xSegment:[[synergyPreferences objectOnDiskForKey:_woFloaterHorizontalSegment] intValue] + ySegment:[[synergyPreferences objectOnDiskForKey:_woFloaterVerticalSegment] intValue]]; + + // just in case floater was set to "always" and it's been changed to something + // less... + if ([[synergyPreferences objectOnDiskForKey:_woFloaterDurationPrefKey] floatValue] < 21.0) + { + [floaterController fadeWindowOut:self]; + } + + // if floater display is set to "forever"... make sure it's on screen... + [mainTimer fire]; // the timer routine will check +} + +- (void)tellITunesActivate +{ + // new way (Apple Events) + // based on "AEBuild*, AEPrint* and friends": + // http://developer.apple.com/technotes/tn/tn2045.html#dssyntaxcprgdb + // and "Lazy AppleScript sending": + // http://www.unsanity.org/archives/000107.php#000107 + + ProcessSerialNumber psn = [WOProcessManager PSNForSignature:'hook']; + AppleEvent event; + AEBuildError error; + + // provided iTunes is running proceed to send Apple event + if (![WOProcessManager PSNEqualsNoProcess:psn]) + { + + // the business part of the event + NSString* sendString=@"&subj:'null'()"; + + OSStatus err = AEBuildAppleEvent('misc', 'actv', + typeProcessSerialNumber, + &psn, sizeof(ProcessSerialNumber), + kAutoGenerateReturnID, + kAnyTransactionID, + &event, &error, + [sendString UTF8String]); + + if (err) + { + // print the error and where it occurs + ELOG(@"%lu:%lu error building \"%@\"", error.fError, error.fErrorPos, + [sendString substringToIndex:error.fErrorPos]); + } + else + { + AppleEvent reply; + + if (AESend(&event, &reply, kAENoReply, kAENormalPriority, + kAEDefaultTimeout, NULL, NULL) == noErr) + AEDisposeDesc(&reply); + + AEDisposeDesc(&event); + } + } + else + { + // iTunes is not running: do it the old way by firing off an AppleScript + // this will have the effect of launching iTunes + + // old way (Apple Script) + static NSString *scriptSource = @"tell application \"iTunes\" to activate"; + NSAppleScript *script = [[NSAppleScript alloc] initWithSource:scriptSource]; + (void)[[NSString alloc] initWithString:[[script executeAndReturnError:NULL] stringValue]]; + } +} + +// tell iTunes to up the volume by (approx) 6.25% +- (void)tellITunesVolumeUp +{ + // From the iTunes Scripting Dictionary: + // + // In "application" class: + // sound volume + // integer -- the sound output volume (0 = minimum, 100 = maximum) + // + + static NSString *scriptSource = + @"tell application \"iTunes\"\n" + @" set currentVol to the sound volume as real\n" + @" set segmentNumber to ((currentVol / 6.25) div 1) as integer\n" + @" if segmentNumber is less than 15 then\n" + @" set segmentNumber to (segmentNumber + 1)\n" + @" set the sound volume to ((segmentNumber * 6.25) + 2)\n" + @" else\n" + @" set the sound volume to 100\n" + @" end if\n" + @" if the sound volume is 100 then\n" + @" return \"16\"\n" + @" else\n" + @" return segmentNumber as string\n" + @" end if\n" + @"end tell"; + + NSAppleScript *script = [[NSAppleScript alloc] initWithSource:scriptSource]; + NSString *result = [[script executeAndReturnError:NULL] stringValue]; + + // update internal measure of which segments are "lit" + if (result) + segmentCount = [result intValue]; +} + +// tell iTunes to reduce the volume by 6.25% +- (void)tellITunesVolumeDown +{ + // Deduct 6 from the sound volume, down to a minimum of 6 + + // tell application "iTunes" + // set currentVol to the sound volume as real + // + // -- Special (boundary) cases + // -- 0 segments = 0 volume + // -- 16 segments = 100 volume + // + // -- Other cases + // -- 15 segments + // -- 0.0 - 6.25 = 1st segment + // -- etc + // -- 93.75 - 100.0 = 15th segment + // + // -- find out which segment we lie in + // set segmentNumber to ((currentVol / 6.25) div 1) as integer + // + // -- reduce segment number + // if segmentNumber is greater than 1 then + // set segmentNumber to (segmentNumber - 1) + // + // -- set sound volume according to new segment number + // -- add two to compensate for rounding down errors + // set the sound volume to ((segmentNumber * 6.25) + 2) + // else + // set the sound volume to 0 + // end if + // + // -- notify of new segment number, noting boundary cases + // if the sound volume is 0 then + // return 0 + // else + // return segmentNumber + // end if + // end tell + + NSAppleScript *script; + NSString *result; + + static NSString *scriptSource = + @"tell application \"iTunes\"\n" + @" set currentVol to the sound volume as real\n" + @" set segmentNumber to ((currentVol / 6.25) div 1) as integer\n" + @" if segmentNumber is greater than 1 then\n" + @" set segmentNumber to (segmentNumber - 1)\n" + @" set the sound volume to ((segmentNumber * 6.25) + 2)\n" + @" else\n" + @" set the sound volume to 0\n" + @" end if\n" + @" if the sound volume is 0 then\n" + @" return \"0\"\n" + @" else\n" + @" return segmentNumber as string\n" + @" end if\n" + @"end tell"; + + script = [[NSAppleScript alloc] initWithSource:scriptSource]; + result = [[NSString alloc] initWithString: + [[script executeAndReturnError:NULL] stringValue]]; + + // update internal measure of which segments are "lit" + if (result) + segmentCount = [result intValue]; +} + +- (void)volumeUpHotKeyPressed +{ + // check if iTunes is running + if ([WOProcessManager processRunningWithSignature:'hook']) + { + [feedbackController setBarEnabled:YES]; + [feedbackController setStarBarEnabled:NO]; + [feedbackController setIconType:WOFeedbackVolumeIcon]; + + [self tellITunesVolumeUp]; + + [feedbackController setEnabledSegments:segmentCount]; + if (extraFeedback) + { + [feedbackController showAtFullAlpha]; + [feedbackController delayedFadeOut]; + } + } +} + +- (void)volumeDownHotKeyPressed +{ + // check if iTunes is running + if ([WOProcessManager processRunningWithSignature:'hook']) + { + [feedbackController setBarEnabled:YES]; + [feedbackController setStarBarEnabled:NO]; + [feedbackController setIconType:WOFeedbackVolumeIcon]; + + [self tellITunesVolumeDown]; + + [feedbackController setEnabledSegments:segmentCount]; + if (extraFeedback) + { + [feedbackController showAtFullAlpha]; + [feedbackController delayedFadeOut]; + } + } +} + +// called when user presses "Show floater" Hot Key +- (void)showHideFloaterHotKeyPressed +{ + // updated 6 April 2003 to make use of floaterActive bool value + // http://bugs.wincent.org/bugs/bug.php?op=show&bugid=60&pos=21 + + // if window is not already on screen AND at full alpha, show it + if (!(([floaterController windowAlphaValue] == 1.0) && + ([floaterController windowIsVisible]))) + { + [floaterController kickItClickStyle:self]; + + if ([[synergyPreferences objectOnDiskForKey:_woFloaterDurationPrefKey] floatValue] > 21.0) + { + // if the floater is in "show always" mode, then pressing this + // hot key is enough to make the changes permanent (otherwise only + // way to permanently activate floater is to use Global menu) + floaterActive = YES; + + [turnFloaterOnOffMenuItem setTitle: + NSLocalizedString(@"Turn Floater off", @"'Turn Floater off' menuitem")]; + } + } + else + { + // if window is present and at full alpha, hide it (via a fade) + [floaterController fadeWindowOut:self]; + + // if floater is set to "show always", update menu item + if ([[synergyPreferences objectOnDiskForKey:_woFloaterDurationPrefKey] floatValue] > 21.0) + { + // if the floater is in "show always" mode, then pressing this + // hot key is enough to make the changes permanent (otherwise only + // way to permanently dectivate floater is to use Global menu) + floaterActive = NO; + + [turnFloaterOnOffMenuItem setTitle: + NSLocalizedString(@"Turn Floater on", @"'Turn Floater on' menuitem")]; + } + } +} + +// these methods called so that we have a distinction between a button press +// and a hot key click +- (void)playPauseHotKeyPressed +{ + // save this for really slow machines (eg. Michael Simmons'!) + // it might help us to guess the iTunes state if we can't get + // it in the timer loop (because iTunes is too busy to reply to our + // Apple Event)! + int prevITunesState = iTunesState; + + [self tellITunesPlayPause]; // main timer will fire at the end of this... + + // in the case of the play/pause key, we have to wait until AFTER we hear + // back from iTunes (and therefore know its state) before choosing the icon + + // only actually show the feedback window if user prefs say so + if ([[synergyPreferences objectOnDiskForKey:_woShowFeedbackWindowPrefKey] boolValue] == + YES) + { + [feedbackController setBarEnabled:NO]; + [feedbackController setStarBarEnabled:NO]; + + // special case for ITUNES_UNKNOWN (might be slow machines) + if (iTunesState == ITUNES_UNKNOWN) + { + if (prevITunesState == ITUNES_UNKNOWN) + // we didn't know before and we don't know now... great... + [feedbackController setIconType:WOFeedbackPlayPauseIcon]; + else if (prevITunesState == ITUNES_PLAYING) + // safe bet to show the pause icon: it's probably what the user + // expects to see! + [feedbackController setIconType:WOFeedbackPauseIcon]; + else if ((prevITunesState == ITUNES_PAUSED) || + (prevITunesState == ITUNES_STOPPED) || + (prevITunesState == ITUNES_NOT_RUNNING)) + // safe bet to show play icon: once again, it's probably what the + // user expects to see! + [feedbackController setIconType:WOFeedbackPlayIcon]; + else + // probably was ITUNES_ERROR, best to just show safe icon: + [feedbackController setIconType:WOFeedbackPlayPauseIcon]; + } + // now we are out of the realm of speculation and into certainty! + else if (iTunesState == ITUNES_PLAYING) + [feedbackController setIconType:WOFeedbackPlayIcon]; + else if (iTunesState == ITUNES_PAUSED) + [feedbackController setIconType:WOFeedbackPauseIcon]; + else if (iTunesState == ITUNES_STOPPED) + { + // now... why would the state be "stopped" if we just pressed play/ + // pause? + if (prevITunesState == ITUNES_NOT_RUNNING) + // safe bet that we were launched and aren't playing yet + // user probably expects to see play icon + [feedbackController setIconType:WOFeedbackPlayIcon]; + else + // I'm not game to take a guess at this + [feedbackController setIconType:WOFeedbackPlayPauseIcon]; + } + else // back into the world of uncertainty! + // if all else fails, fall back on this one! + [feedbackController setIconType:WOFeedbackPlayPauseIcon]; + + [feedbackController showAtFullAlpha]; + [feedbackController delayedFadeOut]; + } +} + +- (void)rewindHotKeyPressed +{ + // user is pressing+holding the "prev" hot key, so initiate a rewind + + // only actually show the feedback window if the user prefs say so + if ([[synergyPreferences objectOnDiskForKey:_woShowFeedbackWindowPrefKey] boolValue]) + { + [feedbackController setBarEnabled:NO]; + [feedbackController setStarBarEnabled:NO]; + [feedbackController setIconType:WOFeedbackPrevIcon]; + [feedbackController showAtFullAlpha]; + + // do not initiate fadeout until user releases the hot key! + } + [self audioscrobblerUserDidSkip]; + [self tellITunesRewind]; +} + +- (void)rewindHotKeyReleased +{ + // fade out the window if appropriate + if ([[synergyPreferences objectOnDiskForKey:_woShowFeedbackWindowPrefKey] boolValue]) + [feedbackController delayedFadeOut]; + + [self tellITunesResume]; +} + +- (void)fastForwardHotKeyPressed +{ + // user is pressing+holding the "next" hot key, so initiate a fast forward + + // only actually show the feedback window if the user prefs say so + if ([[synergyPreferences objectOnDiskForKey:_woShowFeedbackWindowPrefKey] boolValue]) + { + [feedbackController setBarEnabled:NO]; + [feedbackController setStarBarEnabled:NO]; + [feedbackController setIconType:WOFeedbackNextIcon]; + [feedbackController showAtFullAlpha]; + + // do not initiate fadeout until user releases the hot key! + } + [self audioscrobblerUserDidSkip]; + [self tellITunesFastForward]; +} + +- (void)fastForwardHotKeyReleased +{ + // fade out the window if appropriate + if ([[synergyPreferences objectOnDiskForKey:_woShowFeedbackWindowPrefKey] boolValue]) + [feedbackController delayedFadeOut]; + + [self tellITunesResume]; +} + +// this code almost identical to the tellITunesFastForward method +- (void)tellITunesResume +{ + [self sendAppleEventClass:'hook' ID:'Resu']; +} + +- (void)nextHotKeyPressed +{ + // only actually show the feedback window if the user prefs say so + if ([[synergyPreferences objectOnDiskForKey:_woShowFeedbackWindowPrefKey] boolValue]== + YES) + { + // additional layer of checking: show window only if iTunes is running + // (if not running, hot key will have no effect anyway) + ProcessSerialNumber iTunesPSN = [WOProcessManager PSNForSignature:'hook']; + + if ([WOProcessManager PSNEqualsNoProcess:iTunesPSN] == NO) + { + [feedbackController setBarEnabled:NO]; + [feedbackController setStarBarEnabled:NO]; + [feedbackController setIconType:WOFeedbackNextIcon]; + [feedbackController showAtFullAlpha]; + + [self tellITunesNext]; + + [feedbackController delayedFadeOut]; + } + } + else + [self tellITunesNext]; +} + +- (void)prevHotKeyPressed +{ + // only actually show the feedback window if the user prefs say so + if ([[synergyPreferences objectOnDiskForKey:_woShowFeedbackWindowPrefKey] boolValue] == + YES) + { + // additional layer of checking: show window only if iTunes is running + // (if not running, hot key will have no effect anyway) + ProcessSerialNumber iTunesPSN = [WOProcessManager PSNForSignature:'hook']; + + if ([WOProcessManager PSNEqualsNoProcess:iTunesPSN] == NO) + { + [feedbackController setBarEnabled:NO]; + [feedbackController setStarBarEnabled:NO]; + [feedbackController setIconType:WOFeedbackPrevIcon]; + [feedbackController showAtFullAlpha]; + + [self tellITunesPrev]; + + [feedbackController delayedFadeOut]; + } + } + else + [self tellITunesPrev]; +} + +- (void)decreaseRatingHotKeyPressed +{ + // check if iTunes is running + if ([WOProcessManager processRunningWithSignature:'hook']) + { + [feedbackController setBarEnabled:NO]; + [feedbackController setIconType:WOFeedbackVolumeIcon]; + [feedbackController setStarBarEnabled:YES]; + + NSAppleScript *script; + NSString *result; + + static NSString *scriptSource = + @"tell application \"iTunes\"\n" + @" try\n" + @" set oldRating to rating of current track\n" + @" if oldRating > 80 then\n" + @" set rating of current track to 80\n" + @" return \"80\"\n" + @" else if oldRating > 60 then\n" + @" set rating of current track to 60\n" + @" return \"60\"\n" + @" else if oldRating > 40 then\n" + @" set rating of current track to 40\n" + @" return \"40\"\n" + @" else if oldRating > 20 then\n" + @" set rating of current track to 20\n" + @" return \"20\"\n" + @" else\n" + @" set rating of current track to 0\n" + @" return \"0\"\n" + @" end if\n" + @" on error\n" + @" return \"ERROR\"\n" + @" end try\n" + @"end tell"; + + script = [[NSAppleScript alloc] initWithSource:scriptSource]; + result = [[NSString alloc] initWithString:[[script executeAndReturnError:NULL] stringValue]]; + + if (result && [result isEqualToString:@"80"]) + { + [feedbackController setEnabledStars:4]; + + if (extraFeedback) + { + [feedbackController showAtFullAlpha]; + [feedbackController delayedFadeOut]; + } + + // fire the timer here... this will have the effect of updating the floater + // if it is already on screen and user preferences are set to "include + // rating" + if (![self iTunesSendsNotifications]) + [mainTimer fire]; + } + else if (result && [result isEqualToString:@"60"]) + { + [feedbackController setEnabledStars:3]; + if (extraFeedback) + { + [feedbackController showAtFullAlpha]; + [feedbackController delayedFadeOut]; + } + if (![self iTunesSendsNotifications]) + [mainTimer fire]; + } + else if (result && [result isEqualToString:@"40"]) + { + [feedbackController setEnabledStars:2]; + if (extraFeedback) + { + [feedbackController showAtFullAlpha]; + [feedbackController delayedFadeOut]; + } + if (![self iTunesSendsNotifications]) + [mainTimer fire]; + } + else if (result && [result isEqualToString:@"20"]) + { + [feedbackController setEnabledStars:1]; + if (extraFeedback) + { + [feedbackController showAtFullAlpha]; + [feedbackController delayedFadeOut]; + } + if (![self iTunesSendsNotifications]) + [mainTimer fire]; + } + else if (result && [result isEqualToString:@"0"]) + { + [feedbackController setEnabledStars:0]; + if (extraFeedback) + { + [feedbackController showAtFullAlpha]; + [feedbackController delayedFadeOut]; + } + if (![self iTunesSendsNotifications]) + [mainTimer fire]; + } + else if (result && [result isEqualToString:@"ERROR"]) + // this usually means that iTunes is running but there is no current selection + LOG(@"Error while decreasing song rating"); + else + ELOG(@"Unknown error while decreasing song rating"); + } +} + +- (void)increaseRatingHotKeyPressed +{ + // check if iTunes is running + if ([WOProcessManager processRunningWithSignature:'hook']) + { + [feedbackController setBarEnabled:NO]; + [feedbackController setIconType:WOFeedbackVolumeIcon]; + [feedbackController setStarBarEnabled:YES]; + + NSAppleScript *script; + NSString *result; + + static NSString *scriptSource = + @"tell application \"iTunes\"\n" + @" try\n" + @" set oldRating to rating of current track\n" + @" if oldRating < 20 then\n" + @" set rating of current track to 20\n" + @" return \"20\"\n" + @" else if oldRating < 40 then\n" + @" set rating of current track to 40\n" + @" return \"40\"\n" + @" else if oldRating < 60 then\n" + @" set rating of current track to 60\n" + @" return \"60\"\n" + @" else if oldRating < 80 then\n" + @" set rating of current track to 80\n" + @" return \"80\"\n" + @" else\n" + @" set rating of current track to 100\n" + @" return \"100\"\n" + @" end if\n" + @" on error\n" + @" return \"ERROR\"\n" + @" end try\n" + @"end tell"; + + script = [[NSAppleScript alloc] initWithSource:scriptSource]; + result = [[NSString alloc] initWithString: + [[script executeAndReturnError:NULL] stringValue]]; + + if (result && [result isEqualToString:@"20"]) + { + [feedbackController setEnabledStars:1]; + if (extraFeedback) + { + [feedbackController showAtFullAlpha]; + [feedbackController delayedFadeOut]; + } + // fire the timer here... this will have the effect of updating the floater + // if it is already on screen and user preferences are set to "include + // rating" + if (![self iTunesSendsNotifications]) + [mainTimer fire]; + } + else if (result && [result isEqualToString:@"40"]) + { + [feedbackController setEnabledStars:2]; + if (extraFeedback) + { + [feedbackController showAtFullAlpha]; + [feedbackController delayedFadeOut]; + } + if (![self iTunesSendsNotifications]) + [mainTimer fire]; + } + else if (result && [result isEqualToString:@"60"]) + { + [feedbackController setEnabledStars:3]; + if (extraFeedback) + { + [feedbackController showAtFullAlpha]; + [feedbackController delayedFadeOut]; + } + if (![self iTunesSendsNotifications]) + [mainTimer fire]; + } + else if (result && [result isEqualToString:@"80"]) + { + [feedbackController setEnabledStars:4]; + if (extraFeedback) + { + [feedbackController showAtFullAlpha]; + [feedbackController delayedFadeOut]; + } + if (![self iTunesSendsNotifications]) + [mainTimer fire]; + } + else if (result && [result isEqualToString:@"100"]) + { + [feedbackController setEnabledStars:5]; + if (extraFeedback) + { + [feedbackController showAtFullAlpha]; + [feedbackController delayedFadeOut]; + } + if (![self iTunesSendsNotifications]) + [mainTimer fire]; + } + else if (result && [result isEqualToString:@"ERROR"]) + // this usually means that iTunes is running but there is no current selection + LOG(@"Error while increasing song rating"); + else + ELOG(@"Unknown error while increasing song rating"); + } +} + +- (void)rateAs0HotKeyPressed +{ + // check if iTunes is running + if ([WOProcessManager processRunningWithSignature:'hook']) + { + [feedbackController setBarEnabled:NO]; + [feedbackController setIconType:WOFeedbackVolumeIcon]; + [feedbackController setStarBarEnabled:YES]; + [feedbackController setEnabledStars:0]; + + // show feedback only if rating successfully set + if ([self setRating:0] && extraFeedback) + { + [feedbackController showAtFullAlpha]; + [feedbackController delayedFadeOut]; + } + + } +} + +- (void)rateAs1HotKeyPressed +{ + // check if iTunes is running + if ([WOProcessManager processRunningWithSignature:'hook']) + { + [feedbackController setBarEnabled:NO]; + [feedbackController setIconType:WOFeedbackVolumeIcon]; + [feedbackController setStarBarEnabled:YES]; + [feedbackController setEnabledStars:1]; + + // show feedback only if rating successfully set + if ([self setRating:20] && extraFeedback) + { + [feedbackController showAtFullAlpha]; + [feedbackController delayedFadeOut]; + } + + } +} + +- (BOOL)setRating:(int)newRating +{ + BOOL returnValue = NO; + // abort if we get an illegal parameter + if ((newRating < 0) || (newRating > 100)) + { + ELOG(@"Out-of-range parameter submitted while setting song rating"); + return returnValue; + } + + NSAppleScript *script; + NSString *result; + + NSString *scriptSource = [NSString stringWithFormat: + @"tell application \"iTunes\"\n" + @" try\n" + @" set rating of current track to %d\n" + @" return \"SUCCESS\"\n" + @" on error\n" + @" return \"ERROR\"\n" + @" end try\n" + @"end tell", + newRating]; + + script = [[NSAppleScript alloc] initWithSource:scriptSource]; + result = [[NSString alloc] initWithString: + [[script executeAndReturnError:NULL] stringValue]]; + + if (result && [result isEqualToString:@"SUCCESS"]) + { + // fire the timer here... this will have the effect of updating the floater + // if it is already on screen and user preferences are set to "include + // rating" + if (![self iTunesSendsNotifications]) + [mainTimer fire]; + + returnValue = YES; + } + else if (result && [result isEqualToString:@"ERROR"]) + { + // this usually means that iTunes is running but there is no current + // selection + + // return NO (don't show floater) + returnValue = NO; + + } + else + { + ELOG(@"Unknown error while setting song rating to %d", newRating); + + returnValue = NO; // (don't show floater) + } + return returnValue; +} + +- (void)tellITunesToggleMute +{ + static NSString *scriptSource = + @"tell application \"iTunes\"\n" + @" try\n" + @" if mute is true then\n" + @" set mute to false\n" + @" set currentVol to the sound volume as real\n" + @" set segmentNumber to ((currentVol / 6.25) div 1) as integer\n" + @" if the sound volume is 100 then\n" + @" return \"16\"\n" + @" else\n" + @" return segmentNumber as string\n" + @" end if\n" + @" else\n" + @" set mute to true\n" + @" return \"ON\"\n" + @" end if\n" + @" on error\n" + @" return \"ERROR\"\n" + @" end try\n" + @"end tell"; + + NSAppleScript *script = [[NSAppleScript alloc] initWithSource:scriptSource]; + NSString *result = [[NSString alloc] initWithString:[[script executeAndReturnError:NULL] stringValue]]; + + [feedbackController setBarEnabled:YES]; + [feedbackController setStarBarEnabled:NO]; + [feedbackController setIconType:WOFeedbackVolumeIcon]; + + if (result && [result isEqualToString:@"ON"]) + segmentCount = 0; + else if (result && [result isEqualToString:@"ERROR"]) + ELOG(@"Error while toggling iTunes mute setting"); + // don't update segment count! + else + // result supposedly contains the segment count! + segmentCount = [result intValue]; + + [feedbackController setEnabledSegments:segmentCount]; + if (extraFeedback) + { + [feedbackController showAtFullAlpha]; + [feedbackController delayedFadeOut]; + } +} + +- (void)tellITunesToggleShuffle +{ + BOOL error = NO; // let's start off optimistic + NSAppleScript *script; + NSString *result; + + static NSString *scriptSource = + @"tell application \"iTunes\"\n" + @" try\n" + @" -- this will fail if iTunes has no current selection\n" + @" set currentPlaylist to the container of the current track\n" + @" if shuffle of currentPlaylist is true then\n" + @" set shuffle of currentPlaylist to false\n" + @" return \"OFF\"\n" + @" else\n" + @" set shuffle of currentPlaylist to true\n" + @" return \"ON\"\n" + @" end if\n" + @" on error\n" + @" return \"ERROR\"\n" + @" end try\n" + @"end tell"; + + script = [[NSAppleScript alloc] initWithSource:scriptSource]; + result = [[NSString alloc] initWithString: + [[script executeAndReturnError:NULL] stringValue]]; + + [feedbackController setBarEnabled:NO]; + [feedbackController setStarBarEnabled:NO]; + + if (result && [result isEqualToString:@"ON"]) + { + [feedbackController setIconType:WOFeedbackShuffleOnIcon]; + [shuffleMenuItem setState:NSOnState]; + } + else if (result && [result isEqualToString:@"OFF"]) + { + [feedbackController setIconType:WOFeedbackShuffleOffIcon]; + [shuffleMenuItem setState:NSOffState]; + + } + else if (result && [result isEqualToString:@"ERROR"]) + { + ELOG(@"Error while toggling iTunes shuffle setting"); + error = YES; + } + else + { + ELOG(@"Unknown error while toggling iTunes shuffle setting"); + error = YES; + } + + if (!error && extraFeedback) + { + [feedbackController showAtFullAlpha]; + [feedbackController delayedFadeOut]; + } +} + +- (void)tellITunesSetRepeatMode +{ + BOOL error = NO; + NSAppleScript *script; + NSString *result; + + static NSString *scriptSource = + @"tell application \"iTunes\"\n" + @" try\n" + @" -- this will fail if iTunes has no current selection\n" + @" set currentPlaylist to the container of the current track\n" + @" -- cycle through in this order: off, all, one\n" + @" if song repeat of currentPlaylist is off then\n" + @" set song repeat of currentPlaylist to all\n" + @" return \"ALL\"\n" + @" else\n" + @" if song repeat of currentPlaylist is all then\n" + @" set song repeat of currentPlaylist to one\n" + @" return \"ONE\"\n" + @" else\n" + @" set song repeat of currentPlaylist to off\n" + @" return \"OFF\"\n" + @" end if\n" + @" end if\n" + @" on error\n" + @" return \"ERROR\"\n" + @" end try\n" + @"end tell"; + + script = [[NSAppleScript alloc] initWithSource:scriptSource]; + result = [[NSString alloc] initWithString: + [[script executeAndReturnError:NULL] stringValue]]; + + [feedbackController setBarEnabled:NO]; + [feedbackController setStarBarEnabled:NO]; + + if (result && [result isEqualToString:@"ALL"]) + { + [feedbackController setIconType:WOFeedbackRepeatAllIcon]; + + [repeatAllMenuItem setState:NSOnState]; // only this one is "on" + [repeatOffMenuItem setState:NSOffState]; + [repeatOneMenuItem setState:NSOffState]; + } + else if (result && [result isEqualToString:@"ONE"]) + { + [feedbackController setIconType:WOFeedbackRepeatOneIcon]; + + [repeatAllMenuItem setState:NSOffState]; + [repeatOffMenuItem setState:NSOffState]; + [repeatOneMenuItem setState:NSOnState]; // only this one is "on" + } + else if (result && [result isEqualToString:@"OFF"]) + { + [feedbackController setIconType:WOFeedbackRepeatOffIcon]; + + [repeatAllMenuItem setState:NSOffState]; + [repeatOffMenuItem setState:NSOnState]; // only this one is "on" + [repeatOneMenuItem setState:NSOffState]; + } + else if (result && [result isEqualToString:@"ERROR"]) + { + ELOG(@"Error while cycling to next iTunes repeat mode"); + + error = YES; + + // all off! + [repeatAllMenuItem setState:NSOffState]; + [repeatOffMenuItem setState:NSOffState]; + [repeatOneMenuItem setState:NSOffState]; + } + else + { + ELOG(@"Unknown error while cycling to next iTunes repeat mode"); + + error = YES; + + // all off! + [repeatAllMenuItem setState:NSOffState]; + [repeatOffMenuItem setState:NSOffState]; + [repeatOneMenuItem setState:NSOffState]; + } + + if (!error && extraFeedback) + { + [feedbackController showAtFullAlpha]; + [feedbackController delayedFadeOut]; + } +} + +- (void)toggleMuteHotKeyPressed +{ + if ([WOProcessManager processRunningWithSignature:'hook']) + [self tellITunesToggleMute]; +} + +- (void)toggleShuffleHotKeyPressed +{ + if ([WOProcessManager processRunningWithSignature:'hook']) + [self tellITunesToggleShuffle]; +} + +- (void)setRepeatModeHotKeyPressed +{ + if ([WOProcessManager processRunningWithSignature:'hook']) + [self tellITunesSetRepeatMode]; +} + +- (void)rateAs2HotKeyPressed +{ + if ([WOProcessManager processRunningWithSignature:'hook']) + { + [feedbackController setBarEnabled:NO]; + [feedbackController setIconType:WOFeedbackVolumeIcon]; + [feedbackController setStarBarEnabled:YES]; + [feedbackController setEnabledStars:2]; + + // show feedback only if rating successfully set + if ([self setRating:40] && extraFeedback) + { + [feedbackController showAtFullAlpha]; + [feedbackController delayedFadeOut]; + } + + } +} + +- (void)rateAs3HotKeyPressed +{ + if ([WOProcessManager processRunningWithSignature:'hook']) + { + [feedbackController setBarEnabled:NO]; + [feedbackController setIconType:WOFeedbackVolumeIcon]; + [feedbackController setStarBarEnabled:YES]; + [feedbackController setEnabledStars:3]; + + // show feedback only if rating successfully set + if ([self setRating:60] && extraFeedback) + { + [feedbackController showAtFullAlpha]; + [feedbackController delayedFadeOut]; + } + + } +} + +- (void)rateAs4HotKeyPressed +{ + if ([WOProcessManager processRunningWithSignature:'hook']) + { + [feedbackController setBarEnabled:NO]; + [feedbackController setIconType:WOFeedbackVolumeIcon]; + [feedbackController setStarBarEnabled:YES]; + [feedbackController setEnabledStars:4]; + + // show feedback only if rating successfully set + if ([self setRating:80] && extraFeedback) + { + [feedbackController showAtFullAlpha]; + [feedbackController delayedFadeOut]; + } + } +} + +- (void)rateAs5HotKeyPressed +{ + if ([WOProcessManager processRunningWithSignature:'hook']) + { + [feedbackController setBarEnabled:NO]; + [feedbackController setIconType:WOFeedbackVolumeIcon]; + [feedbackController setStarBarEnabled:YES]; + [feedbackController setEnabledStars:5]; + + // show feedback only if rating successfully set + if ([self setRating:100] && extraFeedback) + { + [feedbackController showAtFullAlpha]; + [feedbackController delayedFadeOut]; + } + + } +} + +- (void)activateITunesHotKeyPressed +{ + // if iTunes is running Carbon Process Manager AND frontmost (again, CPM), + // hide it + // else bring it to front... + // although we can't be sure iTunes is running, it's an acceptable to run + // the risk of being mistaken because this is a hot-key-initiated action, + // not a timer-driven one... + + // find out frontmost process: + ProcessSerialNumber frontPID; + OSErr errCode; + + errCode = GetFrontProcess(&frontPID); + + if (errCode == noErr) + { + // we have the PID for the frontmost process + + // get iTunes PID + ProcessSerialNumber iTunesPID; + iTunesPID = [WOProcessManager PSNForSignature:'hook']; + + if(([WOProcessManager processRunningWithSignature:'hook']) && + ([WOProcessManager process:iTunesPID + isSameAs:frontPID])) + { + // iTunes is running and it is frontmost + [self hideITunes]; + } + else + { + // iTunes is either not running, or not frontmost; in both cases, bring + // it to the front + [self tellITunesActivate]; + } + } + else + ELOG(@"Error getting frontmost process"); +} + +- (void)hideITunes +{ + NSAppleScript *script; + NSString *result; + + static NSString *scriptSource = + @"tell application \"System Events\"\n" + @" try\n" + @" set visible of process \"iTunes\" to false\n" + @" return \"SUCCESS\"\n" + @" on error\n" + @" return \"ERROR\"\n" + @" end try\n" + @"end tell"; + + script = [[NSAppleScript alloc] initWithSource:scriptSource]; + result = [[NSString alloc] initWithString: + [[script executeAndReturnError:NULL] stringValue]]; + + if (result && [result isEqualToString:@"SUCCESS"]) + { + //VLOG(@"Successfully issued \"hide iTunes\" directive"); + } + else if (result && [result isEqualToString:@"ERROR"]) + ELOG(@"Error while issuing \"hide iTunes\" directive"); + else + ELOG(@"Unknown error while issuing \"hide iTunes\" directive"); +} + +// make sure iTunes is running and ready to process Apple Events before firing +// off our status script +- (BOOL)iTunesReadyToReceiveAppleScript +/*" + This is the fix for the iTunes "unwanted respawn" bug in which iTunes lingers + on in the list of running processes for as long as 2 seconds after exiting, and + an unwanted respawn results on sending it an AppleScript because all "tell" + statements include an implicit "run". + + The workaround is to try to send an Apple Event which requires a reply, and + test to see if a timeout is exceeded. If the timeout is exceeded then we assume + that iTunes is not ready to receive the AppleScript, and in fact it is probably + not running. + + We send the 'doex' Apple Event (equivalent to the "exists" AppleScript keyword) + as this will have no undesired side-effects in the event that the event does + make it through. + + We ignore the Apple documentation which requires us to implement an "idle + handler" when using the kAEWaitReply mode; there do not appear to be any + harmful side-effects. +"*/ +{ + BOOL readyToReceiveAppleScript = NO; + + ProcessSerialNumber iTunesPSN = [WOProcessManager PSNForSignature:'hook']; + + // make sure iTunes is running + if ([WOProcessManager PSNEqualsNoProcess:iTunesPSN] == NO) + { + AppleEvent event, reply; + AEDesc AEdescriptor; + + if (AECreateDesc(typeProcessSerialNumber, &iTunesPSN, sizeof(iTunesPSN), + &AEdescriptor) == noErr) + { + if (AECreateAppleEvent('hook', 'doex', &AEdescriptor, + kAutoGenerateReturnID, kAnyTransactionID, + &event) == noErr) + { + if (AESend(&event, &reply, kAEWaitReply, kAEHighPriority, + kAEDefaultTimeout, nil, nil) == noErr) + { + AEDisposeDesc(&reply); // this was a leak + readyToReceiveAppleScript = YES; + } + + AEDisposeDesc(&event); + } + AEDisposeDesc(&AEdescriptor); + } + } + return readyToReceiveAppleScript; +} + +- (NSString *)chooseRandomButtonSet +{ + NSArray *buttonSets = [WOButtonSet availableButtonSets]; + int buttonSetIndex = [WOButtonSet randomButtonSetMin:0 max:([buttonSets count] - 1)]; + return [buttonSets objectAtIndex:buttonSetIndex]; +} + +- (IBAction)turnFloaterOnOff:(id)sender +{ + if (floaterActive) + { + // toggle floater off + floaterActive = NO; + + // fade immediately + [floaterController fadeWindowOut:self]; + + // update menu item + [turnFloaterOnOffMenuItem setTitle:NSLocalizedString(@"Turn Floater on", @"'Turn Floater on' menuitem")]; + } + else + { + // toggle floater back on + floaterActive = YES; + + // fade it in, provided it's not already onscreen + if (!(([floaterController windowAlphaValue] == 1.0) && ([floaterController windowIsVisible]))) + [floaterController kickItClickStyle:self]; + + // update menu item + [turnFloaterOnOffMenuItem setTitle:NSLocalizedString(@"Turn Floater off", @"'Turn Floater off' menuitem")]; + } +} + +- (NSString *)audioscrobblerMenuTitleForState:(BOOL)enabled +{ + if (enabled) + return NSLocalizedString(@"Disable last.fm submissions", + @"Disable last.fm submissions"); + else + return NSLocalizedString(@"Enable last.fm submissions", + @"Enable last.fm submissions"); +} + +- (IBAction)toggleAudioscrobbler:(id)sender +{ + BOOL enabled = [self audioscrobblerEnabled]; + if (enabled) + [self audioscrobblerDisable]; + else + [self audioscrobblerEnable]; + [toggleAudioscrobblerMenuItem setTitle:[self audioscrobblerMenuTitleForState:!enabled]]; + [synergyPrefPane sendUpdatedPrefsToPrefPane:WO_DICTIONARY(@"enableLastFm", WO_BOOL(!enabled))]; +} + +- (IBAction)buyFromAmazon:(id)sender +{ + // menu item should be ghosted when no result for currently playing song + // found on amazon... no.... + + // two options here: construct another query and just send that off to + // browser, or cache the URLs along with the songs (in the same way we + // cache info for the recently played list in the Global menu) + // and of course, the compromise case would be needed anyway: + // if no URL found in cache, do a search anyway + // if menu were ghosted out in this case, users would see it as a bug, + // better to allow them to always select it, even if the search results + // produce nothing + // this could happen relatively often because the URLs will only be in the + // cache according to the number of "recent tracks" allowed by user prefs. + // and if the album cover is already downloaded on a previous run, no + // connection will be made to amazon.com at all. + // alternative ideas: store url in file along with image in cache folder + // (I like it)... same filename, just with .plist extension + // downside: lots of disk access if skipping tracks fast + // workaround: combine on disk and in-memory caches ie. not only read info + // from disk, but store it in global song list as well, so if it is there + // that value is used first. only if not there do we look on disk, and if + // no value found in either place then we construct a search string for the + // link. + // + // Naturally, the cache idea is more elegant: and we can just add another + // key/value pair to the songInfo dictionary for each song. + // this will be tricky, because we'll need a thread-safe accessor here to + // the global songList array (stored here in main thread). Each thread + // will have to do a thread-safe implementation of NSObject's + // peformSelectorOnMainThread so as to update the array (if possible!) + + id currentSong = [songList objectAtIndex:0]; + + if (!currentSong) + { + ELOG(@"No current song information available!"); + + return; + } + + // get the URL + NSURL *buyURL = [[currentSong objectForKey:WO_SONG_DICTIONARY_SONGINFO] performSelector:@selector(buyNowURL)]; + + if ([[NSWorkspace sharedWorkspace] openURL:buyURL] == NO) + ELOG(@"Failed opening \"buy now\" link in default browser"); +} + +// called when download is completed in separate thread +- (void)coverDownloadDone:(NSNotification *)notification +{ + // make sure this is a type of notification we understand + if ([[notification name] isEqualToString:WO_DOWNLOAD_DONE_NOTIFICATION]) + { + WOSongInfo *completedSong = + [[notification userInfo] objectForKey:WO_DOWNLOADED_SONG_ID]; + + // make sure we can identify the song that was completed + if(!completedSong) + return; + + if ((id)[songList objectAtIndex:0] == (id)completedSong) + { + // append filename to Album Covers folder path + NSString *tempString = [[WOCoverDownloader albumCoversPath] stringByAppendingPathComponent:[completedSong filename]]; + + // notify floater + [floaterController setAlbumImagePath:tempString]; + } + } + else if ([[notification name] isEqualToString:WO_BUY_NOW_LINK_NOTIFICATION]) + { + WOSongInfo *buyNowLinkSong = + [[notification userInfo] objectForKey:WO_BUY_NOW_LINK_SONG_ID]; + + // make sure we can id the song for which we now have a link + if (!buyNowLinkSong) + return; + + // also: by now has written to a file in Album Covers folder (except in + // the event of an error) + + // just unghost the appropriate menu item if songId + // matches head of song list... + + if ((id)[songList objectAtIndex:0] == (id)buyNowLinkSong) + { + // unghost menu + [buyFromAmazonMenuItem setEnabled:YES]; + } + } +} + +#pragma mark - +#pragma mark NSApplication delegate methods +#pragma mark - + +- (void)applicationDidChangeScreenParameters:(NSNotification *)aNotification +{ + // must notify feedback floater so that it can re-calculate its geometry + [feedbackController applicationDidChangeScreenParameters:aNotification]; +} + +#pragma mark - +#pragma mark Methods written with some clue +#pragma mark - + +- (BOOL)iTunesSendsNotifications +{ + static BOOL firstRun = YES; + if (!firstRun) return iTunesSendsNotifications; + firstRun = NO; + + NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; + NSString *path = [workspace fullPathForApplication:@"iTunes.app"]; + if (path) + { + NSBundle *bundle = [NSBundle bundleWithPath:path]; + NSDictionary *info = [bundle infoDictionary]; + NSString *version = [info objectForKey:@"CFBundleVersion"]; + NSArray *components = [version componentsSeparatedByString:@"."]; + if (components && ([components count] >= 2)) + { + int majorVersion, minorVersion; + NSScanner *scanner1 = [NSScanner scannerWithString:[components objectAtIndex:0]]; + NSScanner *scanner2 = [NSScanner scannerWithString:[components objectAtIndex:1]]; + if ([scanner1 scanInt:&majorVersion] && [scanner2 scanInt:&minorVersion]) + { + if (((majorVersion == 4) && (minorVersion >= 7)) || (majorVersion > 4)) + iTunesSendsNotifications = YES; + } + } + } + + return iTunesSendsNotifications; +} + +- (void)handleNotification:(NSNotification *)aNotification +{ + if ([@"com.apple.iTunes.playerInfo" isEqual:[aNotification name]] || + [@"com.apple.iTunes.player" isEqual:[aNotification object]]) + { + [self launchTrackChangeItems:[self trackChangeLaunchItems]]; // new in 1.7 + + NSDictionary *userInfo = [aNotification userInfo]; + if (!userInfo || ![userInfo isKindOfClass:[NSDictionary class]]) + return; + + //NSString *grouping = [userInfo objectForKey:@"Grouping"]; + NSString *name = [userInfo objectForKey:@"Name"]; + + // "Playing", "Stopped", "Paused" + NSString *playerState = [userInfo objectForKey:@"Player State"]; + + if (!playerState || ![playerState isKindOfClass:[NSString class]]) + playerState = @"Unknown state"; + + // http://wincent.com/a/support/bugs/show_bug.cgi?id=142 + if ([playerState isEqualToString:@"Stopped"] && !name) + return; + + // hopefully fix this by moving this here (ie. don't update floater if iTunes has just exited; it will get updated in handleWorkspaceNotification) + // http://wincent.com/a/support/bugs/show_bug.cgi?id=188 + [self timer:nil]; // update the floater etc + + // int (milliseconds) + NSNumber *totalTime = [userInfo objectForKey:@"Total Time"]; + NSString *album = [userInfo objectForKey:@"Album"]; + NSString *artist = [userInfo objectForKey:@"Artist"]; + // "file://localhost..." (local file) + // "http://pri.kts-af.net/redir/index..." (Internet radio) + NSString *location = [userInfo objectForKey:@"Location"]; + + // Audioscrobbler support; new in 3.1 + WOAudioscrobblerLog(@"Received notification from iTunes"); + if (!totalTime || ([totalTime unsignedIntValue] < (30 * 1000))) + [self audioscrobblerCurrentTrackIsTooShort]; + else if (!location || ![location hasPrefix:@"file://"]) + [self audioscrobblerCurrentTrackIsNotRegularFile]; + else if ([playerState isEqualToString:@"Playing"]) + [self audioscrobblerUpdateWithSong:name artist:artist album:album length:[totalTime unsignedIntValue]]; + else + [self audioscrobblerNotPlaying:name artist:artist album:album length:[totalTime unsignedIntValue]]; + + // Growl support; also new in 1.7 + // might be able to save some cycles here by calling isGrowlInstalled + // and isGrowlRunning before proceeding + NSNumber *year = [userInfo objectForKey:@"Year"]; // int + NSString *composer = [userInfo objectForKey:@"Composer"]; + NSNumber *rating = [userInfo objectForKey:@"Rating"]; // int (0 - 100) + + // build description string + NSMutableString *workString = [NSMutableString string]; + NSString *timeString = @""; + + NSNumber *pref = [synergyPreferences objectOnDiskForKey:_woIncludeAlbumInFloaterPrefKey]; + if (pref && [pref boolValue] && album && [album isKindOfClass:[NSString class]]) + { + [workString appendFormat:@"%@", album]; + + pref = [synergyPreferences objectOnDiskForKey:_woIncludeYearInFloaterPrefKey]; + if (pref && [pref boolValue] && year && [year isKindOfClass:[NSNumber class]]) + [workString appendFormat:@" (%d)\n", [year intValue]]; + else + [workString appendString:@"\n"]; + } + + BOOL showArtist = NO; + BOOL showComposer = NO; + + pref = [synergyPreferences objectOnDiskForKey:_woIncludeArtistInFloaterPrefKey]; + if (pref && [pref boolValue]) + showArtist = YES; + + pref = [synergyPreferences objectOnDiskForKey:_woIncludeComposerInFloaterPrefKey]; + if (pref && [pref boolValue]) + showComposer = YES; + + NSString *artistOrComposer = @"Unknown artist"; + if (showArtist && showComposer) + { + if (artist && composer) + artistOrComposer = + [NSString stringWithFormat:@"%@ (%@)", artist, composer]; + else if (artist) + artistOrComposer = artist; + else if (composer) + artistOrComposer = composer; + [workString appendFormat:@"%@\n", artistOrComposer]; + } + else if (showArtist) + { + if (artist) + artistOrComposer = artist; + [workString appendFormat:@"%@\n", artistOrComposer]; + } + else if (showComposer) + { + if (composer) + artistOrComposer = composer; + [workString appendFormat:@"%@\n", artistOrComposer]; + } + + pref = [synergyPreferences objectOnDiskForKey:_woIncludeStarRatingInFloaterPrefKey]; + if (pref && [pref boolValue] && rating && [rating isKindOfClass:[NSNumber class]]) + { + unichar star = WO_ALT_RATING_STAR; + NSString *ratingString = @""; + int ratingNumber = [rating intValue]; + if (ratingNumber > 80) + ratingString = [NSString stringWithFormat:@"%C%C%C%C%C", + star, star, + star, star, + star]; + else if (ratingNumber > 60) + ratingString = [NSString stringWithFormat:@"%C%C%C%C", + star, star, + star, star]; + else if (ratingNumber > 40) + ratingString = [NSString stringWithFormat:@"%C%C%C", + star, star, + star]; + else if (ratingNumber > 20) + ratingString = [NSString stringWithFormat:@"%C%C", + star, star]; + else if (ratingNumber > 0) + ratingString = [NSString stringWithFormat:@"%C", star]; + + [workString appendFormat:@"%@\n", ratingString]; + } + + pref = [synergyPreferences objectOnDiskForKey:_woIncludeDurationInFloaterPrefKey]; + if (pref && [pref boolValue] && totalTime && [totalTime isKindOfClass:[NSNumber class]]) + { + int seconds = [totalTime intValue] / 1000; + int days = seconds / 86400; + int hours = (seconds - (days * 86400)) / 3600; + int minutes = (seconds - (days * 86400) - (hours * 3600)) / 60; + seconds = seconds - (days * 86400) - (hours * 3600) - (minutes * 60); + + if (days > 0) + timeString = [NSString stringWithFormat: + @" (%02d:%02d:%02d:%02d)", days, hours, minutes, seconds]; + else if (hours > 0) + timeString = + [NSString stringWithFormat:@" (%02d:%02d:%02d)", hours, minutes, seconds]; + else + timeString = + [NSString stringWithFormat:@" (%02d:%02d)", minutes, seconds]; + } + + // if iconData is nil, Growl will display Synergy icon instead + NSData *iconData = [[floaterController coverImage] TIFFRepresentation]; + NSString *growlTitle = [NSString stringWithFormat:@"%@: %@%@", playerState, name, timeString]; + NSString *growlDescription = [NSString stringWithString:workString]; + + // new for 2.0: coalesce Growl notications + NSDictionary *d = nil; + if (iconData) + d = [NSDictionary dictionaryWithObjectsAndKeys: + @"Synergy", GROWL_APP_NAME, + @"iTunes update", GROWL_NOTIFICATION_NAME, + growlTitle, GROWL_NOTIFICATION_TITLE, + growlDescription, GROWL_NOTIFICATION_DESCRIPTION, + iconData, GROWL_NOTIFICATION_ICON, + [NSNumber numberWithInt:0], GROWL_NOTIFICATION_PRIORITY, + [NSNumber numberWithBool:NO], GROWL_NOTIFICATION_STICKY, + @"Click", GROWL_NOTIFICATION_CLICK_CONTEXT, + @"CoalescedSynergyNotification", GROWL_NOTIFICATION_IDENTIFIER, + nil]; + else + d = [NSDictionary dictionaryWithObjectsAndKeys: + @"Synergy", GROWL_APP_NAME, + @"iTunes update", GROWL_NOTIFICATION_NAME, + growlTitle, GROWL_NOTIFICATION_TITLE, + growlDescription, GROWL_NOTIFICATION_DESCRIPTION, + [NSNumber numberWithInt:0], GROWL_NOTIFICATION_PRIORITY, + [NSNumber numberWithBool:NO], GROWL_NOTIFICATION_STICKY, + @"Click", GROWL_NOTIFICATION_CLICK_CONTEXT, + @"CoalescedSynergyNotification", GROWL_NOTIFICATION_IDENTIFIER, + nil]; + [GrowlApplicationBridge notifyWithDictionary:d]; + } +} + +// watch for iTunes launch/quit events +- (void)handleWorkspaceNotification:(NSNotification *)aNotification +{ + NSString *identifier = [[aNotification userInfo] objectForKey:@"NSApplicationBundleIdentifier"]; + + if ([identifier isEqualToString:@"com.apple.iTunes"]) + { + if ([[aNotification name] isEqualToString:@"NSWorkspaceDidLaunchApplicationNotification"]) + { + // even if iTunes isn't ready at this point, it will send a notification on the first status change + [self timer:nil]; + + if (waitingForITunesToLaunch) // jam in old code + { + waitingForITunesToLaunch = NO; + [self iTunesDidLaunchNowPlay:aNotification]; + } + } + else if ([[aNotification name] isEqualToString:@"NSWorkspaceDidTerminateApplicationNotification"]) + { + // taken straight out of timer method but rewritten with some "clue" + [self updateTooltip:NSLocalizedString(@"Not running", @"Not running tool-tip")]; + iTunesState = ITUNES_NOT_RUNNING; // update state variable + + if (!buttonClickOccurred) + { + if ([[synergyPreferences objectOnDiskForKey:_woControlHidingPrefKey] intValue]) + { + [self hideControlsStatusItem]; + if (!globalMenuStatusItem && [[synergyPreferences objectOnDiskForKey:_woGlobalMenuPrefKey] boolValue]) + [self addGlobalMenu]; + } + } + [synergyMenuView makePlayButtonShowPlayImage]; + [floaterController fadeWindowOut:self]; + } + } +} + +- (NSString *)applicationSupportPath:(int)domain +{ + // get path to "Application Support" + NSString *path = nil; + int folderType = kApplicationSupportFolderType; + Boolean createFlag = kDontCreateFolder; + FSRef folderRef; + + if (FSFindFolder(domain, folderType, createFlag, &folderRef) == noErr) + { + CFURLRef url = CFURLCreateFromFSRef(kCFAllocatorDefault, &folderRef); + if (url) + { + path = [(NSURL *)url path]; + CFMakeCollectable(url); + } + } + return path; +} + +- (NSArray *)getTrackChangeItems:(int)domain +{ + NSString *applicationSupport = [self applicationSupportPath:domain]; + NSString *path = [[applicationSupport stringByAppendingPathComponent:@"Synergy"] + stringByAppendingPathComponent:@"Track Change Items"]; + NSFileManager *defaultManager = [NSFileManager defaultManager]; + NSArray *items = [defaultManager contentsOfDirectoryAtPath:path + error:NULL]; + NSMutableArray *workingArray = [NSMutableArray array]; + NSEnumerator *enumerator = [items objectEnumerator]; + NSString *itemName; + + while ((itemName = [enumerator nextObject])) + { + if ([itemName hasPrefix:@"."]) continue; + NSString *itemPath = [path stringByAppendingPathComponent:itemName]; + [workingArray addObject:itemPath]; + } + return workingArray; +} + +- (NSArray *)getTrackChangeItems +{ + // first get items in home directory (~/Library/Application Support...) + NSMutableArray *workingArray = [NSMutableArray arrayWithArray:[self getTrackChangeItems:kUserDomain]]; + + // now add items from /Library/Application Support... + [workingArray addObjectsFromArray:[self getTrackChangeItems:kLocalDomain]]; + + return workingArray; +} + +- (void)launchTrackChangeItems:(NSArray *)paths +{ + NSWorkspace *sharedWorkspace = [NSWorkspace sharedWorkspace]; + NSEnumerator *enumerator = [paths objectEnumerator]; + NSString *path; + while ((path = [enumerator nextObject])) + { + // parent folder must be good + NSString *parent = [path stringByDeletingLastPathComponent]; + if (![parent pathIsOwnedByCurrentUser] || ![parent pathIsWritableOnlyByCurrentUser]) + { + NSLog(@"Warning: item \"%@\" not launched because parent path must be owned and writable only by the current user", + path); + continue; + } + + // item itself must be good + if (![path pathIsOwnedByCurrentUser] || ![path pathIsWritableOnlyByCurrentUser]) + { + NSLog(@"Warning: item \"%@\" not launched (must be owned and writable only by the current user)", path); + continue; + } + + if ([sharedWorkspace openFile:path]) + NSLog(@"Auto-launched item \"%@\"", path); + else + NSLog(@"Error auto-launching item \"%@\"", path); + } +} + +// new for Synergy 2.9 +- (IBAction)transferCoverArtToITunes:(id)sender +{ + if (![WOProcessManager processRunningWithSignature:'hook']) + return; // (double) check that iTunes is running + + // cannot just call coverImage because floater will return scaled art! + NSString *artPath = [floaterController albumImagePath]; + if (!artPath) + return; + NSImage *image = [[NSImage alloc] initWithContentsOfFile:artPath]; + NSData *PICTData = [image PICTRepresentation]; + if (!PICTData) + return; + + // was bug: had to change descriptor type from 'JFIF' to 'PICT' (iTunes 9?) + // error was -2003: QuickTime cantFindHandler + // see: https://wincent.com/issues/1412 + NSArray *parameters = [NSArray arrayWithObject:[NSAppleEventDescriptor descriptorWithDescriptorType:'PICT' data:PICTData]]; + + NSString *source = + @"on open args\n" + @" try\n" + @" set coverData to item 1 of args\n" + @" tell application \"iTunes\"\n" + @" with timeout of 15 seconds\n" + @" set data of front artwork of current track to coverData\n" + @" end timeout\n" + @" end tell\n" + @" return true\n" + @" on error\n" + @" return false\n" + @" end try\n" + @"end open"; + + NSAppleScript *script = [[NSAppleScript alloc] initWithSource:source]; + NSDictionary *error = nil; + NSAppleEventDescriptor *result = [script executeWithParameters:parameters error:&error]; + if (!result || ![result booleanValue]) + ELOG(@"Error transferring cover art"); + else if (error) + ELOG(@"Error transferring cover art: %@", error); +} + +#pragma mark GrowlApplicationBridgeDelegate protocol + +- (NSDictionary *)registrationDictionaryForGrowl +{ + return [NSDictionary dictionaryWithObjectsAndKeys: + [NSArray arrayWithObject:@"iTunes update"], GROWL_NOTIFICATIONS_ALL, + [NSArray arrayWithObject:@"iTunes update"], GROWL_NOTIFICATIONS_DEFAULT, + nil]; +} + +#pragma mark - + +#pragma mark Optional Growl delegate methods + +- (NSString *)applicationNameForGrowl +{ + return @"Synergy"; +} + +- (void) growlNotificationWasClicked:(id)clickContext +{ + [self tellITunesActivate]; // bring iTunes to the front +} + +#pragma mark - + +#pragma mark - +#pragma mark Accessors +#pragma mark - + +- (void)setSongToPlayOnceLaunched:(NSAppleEventDescriptor *)songId +{ + songToPlayOnceLaunched = songId; +} + +- (NSAppleEventDescriptor *)songToPlayOnceLaunched +{ + return songToPlayOnceLaunched; +} + +- (NSArray *)trackChangeLaunchItems +{ + return trackChangeLaunchItems; +} + +- (void)setTrackChangeLaunchItems:(NSArray *)aTrackChangeLaunchItems +{ + if (trackChangeLaunchItems != aTrackChangeLaunchItems) + trackChangeLaunchItems = aTrackChangeLaunchItems; +} + +- (BOOL)hitAmazon +{ + return hitAmazon; +} + +@end diff --git a/SynergyApp/Classes/WOAudioscrobbler.h b/SynergyApp/Classes/WOAudioscrobbler.h new file mode 100644 index 0000000..b5f77c3 --- /dev/null +++ b/SynergyApp/Classes/WOAudioscrobbler.h @@ -0,0 +1,95 @@ +// +// WOAudioscrobbler.h +// Synergy +// +// Created by Wincent Colaiuta on 31 October 2006. +// Copyright 2006-2008 Wincent Colaiuta. + +#import + +typedef enum { + + WOAudioscrobblerIdle, + WOAudioscrobblerWaitingForHandshake, + WOAudioscrobblerHandshakeFailed, + WOAudioscrobblerHandshakeSucceeded, + WOAudioscrobblerBadUser, + WOAudioscrobblerWaitingForSubmissionResponse, + WOAudioscrobblerSubmissionFailed, + WOAudioscrobblerSubmissionSucceeded, + WOAudioscrobblerWaitingToRetrySubmission, + WOAudioscrobblerBadAuth, + WOAudioscrobblerNoAuth //!< State when username and/or password is not set + +} WOAudioscrobblerState; + +//! \sa http://www.audioscrobbler.net/wiki/Protocol1.0_1.1 +//! \warn Not threadsafe; should only be called from a single thread (most likely the main thread) +@interface WOAudioscrobbler : NSObject { + + NSString *protocolVersion; + + WOAudioscrobblerState currentState; + + //! FIFO (first-in, first-out submissions queue) + NSMutableArray *queue; + + //! Keep count of submission failures. + unsigned submissionFailures; + + //! Keep count of "BADAUTH" retries. + unsigned badauthRetries; + + //! "INTERVAL commands can be at the end of any response block, but don't expect them to be. Always observe the latest INTERVAL you get." + unsigned lastKnownInterval; + + + //! Instance variables starting with underscores; Apple officially does it in Leopard (synthesized instance variables with properties) + NSURLConnection *connection; + + NSMutableData *receivedData; + + //! Passed in from last.fm during handshake + NSURL *submissionURL; + + //! Passed in from last.fm during handshake + NSString *challenge; + + //! User agent string passed with all new requests + NSString *userAgent; + + NSString *user; + + NSString *password; +} + + +#pragma mark - +#pragma mark Custom methods + +//! \warn Can only start a session when idle +- (void)startSession; + +//! Can be used to force a session to be renegotiated (a new handshake) +- (void)refreshSession; + +- (void)submitSong:(NSString *)track artist:(NSString *)artist album:(NSString *)album length:(unsigned)length; + +- (void)finalizeSession; + +#pragma mark - +#pragma mark Properties + +@property(copy) NSString *protocolVersion; +@property WOAudioscrobblerState currentState; +@property(copy) NSMutableArray *queue; +@property unsigned lastKnownInterval; +@property(assign) NSURLConnection *connection; +@property(copy) NSMutableData *receivedData; +@property(copy) NSURL *submissionURL; +@property(copy) NSString *challenge; +@property(copy) NSString *userAgent; +@property(copy) NSString *user; +@property(copy) NSString *password; + +@end diff --git a/SynergyApp/Classes/WOAudioscrobbler.m b/SynergyApp/Classes/WOAudioscrobbler.m new file mode 100644 index 0000000..1e11423 --- /dev/null +++ b/SynergyApp/Classes/WOAudioscrobbler.m @@ -0,0 +1,751 @@ +// WOAudioscrobbler.m +// Synergy +// +// Copyright 2006-2009 Wincent Colaiuta. All right reserved. + +// class header +#import "WOAudioscrobbler.h" + +// system headers +#import /* requires -lcrypto linker flag, TODO: in Synergy Advance, use CDSA instead */ + +// other headers +#import "WOAudioscrobblerController.h" /* WOAudioscrobblerLog */ + +// WOPublic headers +#import "WOPublic/WOConvenienceMacros.h" +#import "WOPublic/WODebugMacros.h" + +// TODO: wrap this up (or equivalent code) in a plug-in for Synergy Advance + +//! Default timeout in seconds as noted in the NSURLRequest documentation +#define WO_DEFAULT_URL_REQUEST_TIMEOUT 60 + +//! Handshake delay in seconds after repeated failures failure: "A Handshake should occur just once during a SESSION, e.g. when the APP first loads, or after the APP detects 3 catastrophic (i.e. DNS resolution or connection refused) failures in submitting. In this case, the APP should not handshake more than once every 30 minutes." +#define WO_HANDSHAKE_DELAY_ON_FAILURES (60 * 30) + +#ifdef WO_AUDIOSCROBBLER_TEST_MODE + +#define WO_ASSIGNED_PLUGIN_ID @"tst" +#define WO_ASSIGNED_PLUGIN_VERSION @"1.0" + +#else + +//! Synergy's Audioscrobbler plug-in ID as assigned by Russ of last.fm +#define WO_ASSIGNED_PLUGIN_ID @"syn" + +//! Synergy's Audioscrobbler plug-in version as assigned by Russ of last.fm +#define WO_ASSIGNED_PLUGIN_VERSION @"0.1" + +#endif /* WO_AUDIOSCROBBLER_TEST_MODE */ + +//! Base URL used for posting handshake requests +#define WO_HANDSHAKE_URL_BASE @"http://post.audioscrobbler.com/" + +//! Audioscrobbler protocol version +#define WO_PROTOCOL_VERSION @"1.1" + +//! Default delay between submissions in seconds +#define WO_DEFAULT_INTERVAL 1 + +//! Reply keywords from Audioscrobbler +#define WO_UP_TO_DATE @"UPTODATE" +#define WO_UPDATE @"UPDATE" +#define WO_FAILED @"FAILED" +#define WO_BADUSER @"BADUSER" +#define WO_INTERVAL @"INTERVAL" +#define WO_OK @"OK" +#define WO_BADAUTH @"BADAUTH" + +//! HTTP headers +#define WO_USER_AGENT @"User-Agent" + +#define WO_DEFAULT_USER_AGENT @"Synergy (NSURLConnection) $Rev: 338 $" + +//! Dictionary keys for items in queue +#define WO_TRACK_KEY @"WOTrack" +#define WO_ARTIST_KEY @"WOArtist" +#define WO_ALBUM_KEY @"WOAlbum" +#define WO_MBID_KEY @"WOMBID" +#define WO_LENGTH_KEY @"WOLength" +#define WO_DATE_KEY @"WODate" + +@interface WOAudioscrobbler () + +- (void)requestHandshake; +- (NSString *)dateString; +- (BOOL)queueIsEmpty; +- (void)enqueue:(id)object; +- (NSDictionary *)firstObjectInQueue; +- (void)doSubmission:(NSDictionary *)songInfo; + +@end + +@implementation WOAudioscrobbler + +#pragma mark - +#pragma mark NSObject overrides + +- (id)init +{ + if ((self = [super init])) + { + WOAudioscrobblerLog(@"Initializing WOAudioscrobbler object"); + self->protocolVersion = WO_PROTOCOL_VERSION; + self->currentState = WOAudioscrobblerIdle; + self->queue = [NSMutableArray array]; + self->userAgent = WO_DEFAULT_USER_AGENT; + self->lastKnownInterval = WO_DEFAULT_INTERVAL; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(willTerminate:) + name:NSApplicationWillTerminateNotification + object:nil]; + } + return self; +} + +- (void)finalize +{ + WOAudioscrobblerLog(@"Finalizing WOAudioscrobbler object"); + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [super finalize]; +} + +#pragma mark - +#pragma mark Custom methods + +- (void)startSession +{ + WOAudioscrobblerLog(@"Start Audioscrobbler session"); + WOAssert(self.currentState == WOAudioscrobblerIdle); + [self requestHandshake]; +} + +- (void)refreshSession +{ + // cancel any in-progress URL requests + if (self.connection) + { + [self.connection cancel]; + self.connection = nil; + self.receivedData = nil; + } + + // reset state and request the handshake again + self.currentState = WOAudioscrobblerIdle; + WOAudioscrobblerLog(@"Refresh Audioscrobbler session"); + [self requestHandshake]; +} + +- (void)submitSong:(NSString *)track artist:(NSString *)artist album:(NSString *)album length:(unsigned)length +{ + NSParameterAssert(length >= 30); + if (!track || [track isEqualToString:@""]) + { + // doubtful that this will ever happen as iTunes always seems to define a title, even if it is only the filename + NSLog(@"Cannot submit to last.fm if track has no title"); + return; + } + + // "all the post variables noted here MUST be supplied for each entry, even if they are blank." + if (!artist) artist = @""; + if (!album) album = @""; + NSString *mbid = @""; + + NSDictionary *song = [NSDictionary dictionaryWithObjectsAndKeys: + track, WO_TRACK_KEY, + artist, WO_ARTIST_KEY, + album, WO_ALBUM_KEY, + mbid, WO_MBID_KEY, + [NSNumber numberWithUnsignedInt:length], WO_LENGTH_KEY, + [self dateString], WO_DATE_KEY, nil]; + WOAudioscrobblerLog(@"Adding song to submission queue; song information: %@", song); + [self enqueue:song]; +} + +- (void)finalizeSession +{ + WOAudioscrobblerLog(@"Finalize Audioscrobbler session"); + [NSObject cancelPreviousPerformRequestsWithTarget:self]; + self.currentState = WOAudioscrobblerIdle; +} + +#pragma mark - +#pragma mark Low-level utility methods + +- (void)willTerminate:(NSNotification *)aNotification +{ + [self finalizeSession]; +} + +- (BOOL)startConnectionWithURL:(NSURL *)aURL body:(NSData *)aData isPost:(BOOL)post +{ + WOAudioscrobblerLog(@"Starting connection attempt"); + if (self.connection) + WOAudioscrobblerLog(@"warning: existing connection still active"); + NSParameterAssert(aURL != nil); + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:aURL + cachePolicy:NSURLRequestReloadIgnoringCacheData + timeoutInterval:WO_DEFAULT_URL_REQUEST_TIMEOUT]; + [request setValue:[self userAgent] forHTTPHeaderField:WO_USER_AGENT]; + + if (aData) + [request setHTTPBody:aData]; + + if (post) + // "Submissions MUST be sent using an HTTP POST request to the URL obtained from the HANDSHAKE process." + [request setHTTPMethod:@"POST"]; + + self.receivedData = [NSMutableData data]; + self.connection = [NSURLConnection connectionWithRequest:request delegate:self]; // deep-copies the request + if (!self.connection) + NSLog(@"NSURLConnection failed for URL %@", aURL); + return self.connection ? YES : NO; +} + +- (NSString *)escapedString:(NSString *)aString; +{ + // "UTF-8 encoding is used first, then URL encoding" + if (!aString) return nil; + + // also escape legal-but-reserved characters as defined in RFC 2396: + return NSMakeCollectable(CFURLCreateStringByAddingPercentEscapes + (NULL, (CFStringRef)aString, NULL, CFSTR(";/?:@&=+$,"), kCFStringEncodingUTF8)); +} + +- (NSString *)dateString +{ + // "The date format uses the ISO 8601 format except that the time zone specifier MUST NOT be used, the date/time separator MUST be a single space, and all values MUST be expressed with UTC times. For example, a time of 7AM, Pacific Standard Time (UTC + 8) would normally be expressed in ISO 8601 as 2006-02-12T07:00:00+0800. For submission it would be expressed as 2006-02-11 23:00:00." + return [[NSDate date] descriptionWithCalendarFormat:@"%Y-%m-%d %H:%M:%S" + timeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"] + locale:nil]; +} + +- (NSURL *)handshakeURL +{ + // http://post.audioscrobbler.com/?hs=true&p=1.1&c=osx&v=&u= + // hs = true : "a handshake is requested" + // p = 1.1 : "the Audioscrobbler protocol version" + // c = clientid = osx : "Applescriptable MacOS X Application (iTunes)" + // v = clientver : "is the version of the APP plugin" + // u = user : "the user name" + + NSURL *URL = [NSURL URLWithString:WO_STRING(@"%@?hs=true&p=%@&c=%@&v=%@&u=%@", WO_HANDSHAKE_URL_BASE, WO_PROTOCOL_VERSION, + WO_ASSIGNED_PLUGIN_ID, WO_ASSIGNED_PLUGIN_VERSION, [self escapedString:[self user]])]; + + WOAudioscrobblerLog(@"Handshake URL is %@", URL); + return URL; +} + +- (void)requestHandshake +{ + // don't even bother trying unless both username and password are non-blank + if ((self.user && ![self.user isEqualToString:@""]) && + (self.password && ![self.password isEqualToString:@""])) + { + WOAudioscrobblerLog(@"Will request handshake"); + NSURL *URL = [self handshakeURL]; + if ([self startConnectionWithURL:URL body:nil isPost:NO]) + { + WOAudioscrobblerLog(@"Waiting for handshake reply"); + self.currentState = WOAudioscrobblerWaitingForHandshake; + } + else + { + WOAudioscrobblerLog(@"Failed before handshake reply received"); + self.currentState = WOAudioscrobblerHandshakeFailed; + } + } + else + { + WOAudioscrobblerLog(@"Not proceeding with handshake (neither the username nor the password may be blank)"); + self.currentState = WOAudioscrobblerNoAuth; + } +} + +- (void)processHandshake:(NSArray *)lines; +{ + WOAudioscrobblerLog(@"Processing handshake reply"); + NSParameterAssert(lines != nil); + unsigned count = [lines count]; + NSParameterAssert(count >= 1); + NSString *firstLine = [lines objectAtIndex:0]; + if ([firstLine hasPrefix:WO_UP_TO_DATE]) + { + WOAudioscrobblerLog(@"Received UPTODATE message"); + // UPTODATE ("your version is up to date") + // + // + // INTERVAL n ("the number of seconds you must wait between sending updates", 0 or more) + + if (count < 3) + { + NSLog(@"Handshake response too short:\n%@", lines); + self.currentState = WOAudioscrobblerHandshakeFailed; + return; + } + + self.challenge = [lines objectAtIndex:1]; + self.submissionURL = [NSURL URLWithString:[lines objectAtIndex:2]]; + self.currentState = WOAudioscrobblerHandshakeSucceeded; + badauthRetries = 0; // reset + } + else if ([firstLine hasPrefix:WO_UPDATE]) + { + WOAudioscrobblerLog(@"Received UPDATE message"); + // UPDATE ("If you are using an outdated version of a plugin, you will see something like this, indicating an update is available") + // + // + // INTERVAL n (as above) + + if ([firstLine length] > [WO_UPDATE length]) + { + NSString *updateURL = [firstLine substringFromIndex:[WO_UPDATE length]]; + NSLog(@"last.fm reports new version available from %@", updateURL); + } + + if (count < 3) + { + NSLog(@"Handshake response too short:\n%@", lines); + self.currentState = WOAudioscrobblerHandshakeFailed; + return; + } + + self.challenge = [lines objectAtIndex:1]; + self.submissionURL = [NSURL URLWithString:[lines objectAtIndex:2]]; + self.currentState = WOAudioscrobblerHandshakeSucceeded; + badauthRetries = 0; // reset + } + else if ([firstLine hasPrefix:WO_FAILED]) + { + WOAudioscrobblerLog(@"Received FAILED message"); + // "If the request fails, you will get:" + // FAILED + // INTERVAL n + + if ([firstLine length] > [WO_FAILED length]) + { + NSString *failureReason = [firstLine substringFromIndex:[WO_FAILED length]]; + NSLog(@"last.fm handshake failed; reported reason: \"%@\"", failureReason); + } + else + NSLog(@"last.fm handshake failed"); + + self.currentState = WOAudioscrobblerHandshakeFailed; + } + else if ([firstLine hasPrefix:WO_BADUSER]) + { + WOAudioscrobblerLog(@"Received BADUSER message"); + // "If the user is invalid:" + // BADUSER + // INTERVAL n + + NSLog(@"last.fm handshake failed; returned result: %@", firstLine); + self.currentState = WOAudioscrobblerBadUser; + } + else + { + NSLog(@"Unrecognized handshake response:\n%@", lines); + self.currentState = WOAudioscrobblerHandshakeFailed; + } +} + +- (NSString *)challengeResponse +{ + // "The MD5 response is md5(md5(your_password) + challenge), where MD5 is the ascii-encoded, lowercase MD5 representation, and + represents concatenation. MD5 strings must be converted to their hex value before concatenation with the challenge string and before submission to the final MD5 response." + + // "The ascii-encoded, lowercase MD5 representation MUST be used, and MD5 strings MUST be converted to their hex value before concatenation with the challenge string and before submission to the final MD5 response. " + + // calculate password hash + NSData *passwordData = [self.password dataUsingEncoding:NSUTF8StringEncoding]; + unsigned char *digest = MD5([passwordData bytes], [passwordData length], NULL); + NSMutableString *string = [NSMutableString string]; + for (int i = 0; i < MD5_DIGEST_LENGTH; i++) + [string appendFormat:@"%02x", *digest++]; + + // append challenge (salt) + [string appendString:self.challenge]; + + // calculate and return digest of whole + NSData *response = [string dataUsingEncoding:NSUTF8StringEncoding]; + digest = MD5([response bytes], [response length], NULL); + [string setString:@""]; + for (int i = 0; i < MD5_DIGEST_LENGTH; i++) + [string appendFormat:@"%02x", *digest++]; + return string; +} + +- (void)next +{ + unsigned delay = self.lastKnownInterval; + WOAudioscrobblerLog(@"Will handle next item on the queue after delay (Audioscrobbler specified delay in seconds: %d)", delay); + + // handle the next item on the queue + [self performSelector:@selector(nextOperation:) withObject:nil afterDelay:delay]; +} + +// called when the Audioscrobbler-specified delay interval has passed and the next operation can be performed +- (void)nextOperation:(id)ignored +{ + if ([self queueIsEmpty]) + { + WOAudioscrobblerLog(@"Queue is empty, not proceeding"); + return; + } + + switch (self.currentState) + { + case WOAudioscrobblerIdle: + case WOAudioscrobblerHandshakeSucceeded: + case WOAudioscrobblerWaitingToRetrySubmission: + case WOAudioscrobblerSubmissionSucceeded: + WOAudioscrobblerLog(@"Will proceed with submission"); + [self doSubmission:[self firstObjectInQueue]]; + break; + case WOAudioscrobblerSubmissionFailed: + // "A Handshake should occur just once during a SESSION, e.g. when the APP first loads, or after the APP detects 3 catastrophic (i.e. DNS resolution or connection refused) failures in submitting. In this case, the APP should not handshake more than once every 30 minutes. If the APP fails to connect to the handshake URL, the user should be informed." + WOAudioscrobblerLog(@"Submission previously failed, will retry"); + [self doSubmission:[self firstObjectInQueue]]; + break; + case WOAudioscrobblerHandshakeFailed: + WOAudioscrobblerLog(@"Handshake previously failed, will retry"); + [self requestHandshake]; + break; + case WOAudioscrobblerNoAuth: + WOAudioscrobblerLog(@"Missing username or password; cannot proceed"); + break; + case WOAudioscrobblerBadAuth: + // "If it returns BADAUTH, you may need to re-handshake (you're likely to get temporarily blocked if you're attempting to resubmit at one-second intervals after getting repeated BADAUTH errors)." + if (badauthRetries < 3) // the limit of 3 is not specified in the protocol but it seems like a nice number + { + WOAudioscrobblerLog(@"Previously received a bad auth error; will try again to handshake"); + badauthRetries++; // will reset to 0 if handshake is successful + [self requestHandshake]; + } + else + WOAudioscrobblerLog(@"Previously received 3 bad auth errors; cannot proceed"); + break; + case WOAudioscrobblerBadUser: + WOAudioscrobblerLog(@"Previously received a bad user error; cannot proceed"); + // non-recoverable errors + break; + case WOAudioscrobblerWaitingForHandshake: + // should never get here, right? this is a programmer error + WOAudioscrobblerLog(@"Error: did not expect WOAudioscrobblerWaitingForHandshake status; please report"); + break; + case WOAudioscrobblerWaitingForSubmissionResponse: + // should never get here, right? this is a programmer error + WOAudioscrobblerLog(@"Error: did not expect WOAudioscrobblerWaitingForSubmissionResponse status; please report"); + break; + default: + // programmer error again + WOAudioscrobblerLog(@"Error: did not expect status %d; please report", [self currentState]); + break; + } +} + +#pragma mark - +#pragma mark Queue helper methods + +- (BOOL)queueIsEmpty +{ + return (self.queue.count == 0) ? YES : NO; +} + +- (void)enqueue:(id)object +{ + WOAudioscrobblerLog(@"Enqueuing object: %@", object); + NSParameterAssert(object != nil); + BOOL empty = [self queueIsEmpty]; + [self.queue addObject:object]; + WOAudioscrobblerLog(@"Number of items currently on the queue: %d", self.queue.count); + + // special case handling for items added to empty queues: process immediately + if (empty) + { + WOAudioscrobblerLog(@"Queue was empty: processing item"); + [self next]; + } + else + { + // some states are also worth submitting + switch (self.currentState) + { + case WOAudioscrobblerIdle: + case WOAudioscrobblerHandshakeSucceeded: + case WOAudioscrobblerSubmissionSucceeded: + case WOAudioscrobblerWaitingToRetrySubmission: + WOAudioscrobblerLog(@"Queue was non-empty, but ready to submit: processing item"); + [self next]; + break; + case WOAudioscrobblerSubmissionFailed: + case WOAudioscrobblerHandshakeFailed: + case WOAudioscrobblerBadAuth: + WOAudioscrobblerLog(@"Queue was non-empty, last attempt failed: processing item"); + [self next]; + break; + case WOAudioscrobblerBadUser: + case WOAudioscrobblerWaitingForHandshake: + case WOAudioscrobblerWaitingForSubmissionResponse: + case WOAudioscrobblerNoAuth: + default: + WOAudioscrobblerLog(@"Queue was non-empty, but not ready to submit: not processing item"); + break; + } + } +} + +- (void)dequeueFirstObject +{ + WOAudioscrobblerLog(@"Dequeueing first object"); + [self.queue removeObjectAtIndex:0]; +} + +// return first object in the queue without actually dequeuing it; returns nil if queue is empty +- (NSDictionary *)firstObjectInQueue +{ + return [self queueIsEmpty] ? nil : [self.queue objectAtIndex:0]; +} + +#pragma mark - +#pragma mark High-level methods + +- (void)doSubmission:(NSDictionary *)songInfo +{ + NSString *artist = [songInfo objectForKey:WO_ARTIST_KEY]; + NSString *track = [songInfo objectForKey:WO_TRACK_KEY]; + NSString *album = [songInfo objectForKey:WO_ALBUM_KEY]; + NSString *mbid = [songInfo objectForKey:WO_MBID_KEY]; + unsigned length = [[songInfo objectForKey:WO_LENGTH_KEY] unsignedIntValue]; + NSString *date = [songInfo objectForKey:WO_DATE_KEY]; + + // u=&s=&a[0]=&t[0]=&b[0]=&m[0]=&l[0]=&i[0]=