-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathlisting54.html
executable file
·1923 lines (1572 loc) · 70.5 KB
/
listing54.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
<html>
<head>
<!-- BEGIN META TAG INFO -->
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link rel="home" href="http://developer.apple.com/">
<link rel="find" href="http://developer.apple.com/search/">
<link rel="stylesheet" type="text/css" href="../../documentation/css/adcstyle.css" title="fonts">
<script language="JavaScript" src="../../documentation/js/adc.js" type="text/javascript"></script>
<!-- END META TAG INFO -->
<!-- BEGIN TITLE -->
<title>QISA - /QISAPanels.c</title>
<!-- END TITLE -->
<script language="JavaScript">
function JumpToNewPage() {
window.location=document.scpopupmenu.gotop.value;
return true;
}
</script>
</head>
<!-- BEGIN BODY OPEN -->
<body>
<!--END BODY OPEN -->
<!-- START CENTER OPEN -->
<center>
<!-- END CENTER OPEN -->
<!-- BEGIN LOGO AND SEARCH -->
<!--#include virtual="/includes/adcnavbar"-->
<!-- END LOGO AND SEARCH -->
<!-- START BREADCRUMB -->
<div id="breadcrumb">
<table width="680" border="0" cellpadding="0" cellspacing="0">
<tr>
<td scope="row"><img width="340" height="10" src="images/1dot.gif" alt=""></td>
<td><img width="340" height="10" src="images/1dot.gif" alt=""></td>
</tr>
<tr valign="middle">
<td align="left" colspan="2">
<a href="http://developer.apple.com/">ADC Home</a> > <a href="../../referencelibrary/index.html">Reference Library</a> > <a href="../../samplecode/index.html">Sample Code</a> > <a href="../../samplecode/Networking/index.html">Networking</a> > <a href="../../samplecode/Networking/idxDarwin-date.html">Darwin</a> > <A HREF="javascript:location.replace('index.html');">QISA</A> >
</td>
</tr>
<tr>
<td colspan="2" scope="row"><img width="680" height="35" src="images/1dot.gif" alt=""></td>
</tr>
</table>
</div>
<!-- END BREADCRUMB -->
<!-- START MAIN CONTENT -->
<!-- START TITLE GRAPHIC AND INTRO-->
<table width="680" border="0" cellpadding="0" cellspacing="0">
<tr align="left" valign="top">
<td><h1><div id="pagehead">QISA</div></h1></td>
</tr>
</table>
<!-- END TITLE GRAPHIC AND INTRO -->
<!-- START WIDE COLUMN -->
<table width="680" border="0" cellpadding="0" cellspacing="0">
<tr align="left" valign="top">
<td id="scdetails">
<h2>/QISAPanels.c</h2>
<form name="scpopupmenu" onSubmit="return false;" method=post>
<p><strong>View Source Code:</strong>
<select name="gotop" onChange="JumpToNewPage();" style="width:340px"><option selected value="ingnore">Select File</option>
<option value="listing1.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreAppearance/MoreAppearance.cp</option>
<option value="listing2.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreAppearance/MoreAppearance.h</option>
<option value="listing3.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreAppleEvents/MoreAEDataModel.c</option>
<option value="listing4.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreAppleEvents/MoreAEDataModel.h</option>
<option value="listing5.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreAppleEvents/MoreAEObjects.c</option>
<option value="listing6.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreAppleEvents/MoreAEObjects.h</option>
<option value="listing7.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreAppleEvents/MoreAppleEvents.cp</option>
<option value="listing8.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreAppleEvents/MoreAppleEvents.h</option>
<option value="listing9.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreCarbonEvents/MoreCarbonEvents.c</option>
<option value="listing10.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreCarbonEvents/MoreCarbonEvents.h</option>
<option value="listing11.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreCFQ/MoreCFQ.c</option>
<option value="listing12.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreCFQ/MoreCFQ.h</option>
<option value="listing13.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreControls/MoreControls.cp</option>
<option value="listing14.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreControls/MoreControls.h</option>
<option value="listing15.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreDialogs/MoreDialogs.cp</option>
<option value="listing16.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreDialogs/MoreDialogs.h</option>
<option value="listing17.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreInterfaceLib/MoreInterfaceLib.c</option>
<option value="listing18.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreInterfaceLib/MoreInterfaceLib.h</option>
<option value="listing19.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreInterfaceLib/RemoteAccessInterface.h</option>
<option value="listing20.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreMemory/MoreMemory.c</option>
<option value="listing21.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreMemory/MoreMemory.h</option>
<option value="listing22.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreOSUtils/MoreOSUtils.c</option>
<option value="listing23.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreOSUtils/MoreOSUtils.h</option>
<option value="listing24.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreProcesses/MoreProcesses.cp</option>
<option value="listing25.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreProcesses/MoreProcesses.h</option>
<option value="listing26.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreQuickDraw/MoreQuickDraw.cp</option>
<option value="listing27.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreQuickDraw/MoreQuickDraw.h</option>
<option value="listing28.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreSCF/MoreSCF.c</option>
<option value="listing29.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreSCF/MoreSCF.h</option>
<option value="listing30.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreSCF/MoreSCFCCLScanner.c</option>
<option value="listing31.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreSCF/MoreSCFCCLScanner.h</option>
<option value="listing32.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreSCF/MoreSCFDigest.c</option>
<option value="listing33.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreSCF/MoreSCFDigest.h</option>
<option value="listing34.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreSCF/MoreSCFHelpers.c</option>
<option value="listing35.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreSCF/MoreSCFHelpers.h</option>
<option value="listing36.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreSCF/MoreSCFPortScanner.c</option>
<option value="listing37.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreSCF/MoreSCFPortScanner.h</option>
<option value="listing38.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreSecurity/MoreSecurity.c</option>
<option value="listing39.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreSecurity/MoreSecurity.h</option>
<option value="listing40.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreSetup.h</option>
<option value="listing41.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreTextUtils/MoreTextUtils.cp</option>
<option value="listing42.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreTextUtils/MoreTextUtils.h</option>
<option value="listing43.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreUNIX/MoreUNIX.c</option>
<option value="listing44.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreUNIX/MoreUNIX.h</option>
<option value="listing45.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreWindows/MoreWindows.cp</option>
<option value="listing46.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/MoreWindows/MoreWindows.h</option>
<option value="listing47.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/NetworkSetup/MoreNetworkSetup.c</option>
<option value="listing48.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/NetworkSetup/MoreNetworkSetup.h</option>
<option value="listing49.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/NetworkSetup/NetworkSetupHelpers.c</option>
<option value="listing50.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/NetworkSetup/NetworkSetupHelpers.h</option>
<option value="listing51.html">/MoreIsBetter Bits/MoreIsBetter/MIB-Libraries/NetworkSetup/OldStyleAPI/OldOTConfigLib.h</option>
<option value="listing52.html">/QISA.c</option>
<option value="listing53.html">/QISA.h</option>
<option value="listing54.html">/QISAPanels.c</option>
<option value="listing55.html">/QISAPanels.h</option>
<option value="listing56.html">/QISAPlatform.c</option>
<option value="listing57.html">/QISAPlatform.h</option>
<option value="listing58.html">/QISAPlatformCFM/QISAPlatformCFM.c</option>
<option value="listing59.html">/QISAPlatformCFM/QISAPlatformCFMPrefix.h</option>
<option value="listing60.html">/QISAPlatformMach-O/QISAPlatformMach-O.c</option>
<option value="listing61.html">/QISAPlatformMach-O/QISASetupTool.c</option>
<option value="listing62.html">/ReadMeAboutQISA.html</option></select>
</p>
</form>
<p><strong><a href="QISA.zip">Download Sample</a></strong> (“QISA.zip”, 610.6K)<BR>
<strong><a href="QISA.dmg">Download Sample</a></strong> (“QISA.dmg”, 903.9K)</p>
<!--
<p><strong><a href="#">Download Sample</a></strong> (“filename.sit”, 500K)</p>
-->
</td>
</tr>
<tr>
<td scope="row"><img width="680" height="10" src="images/1dot.gif" alt=""><br>
<img height="1" width="680" src="images/1dot_919699.gif" alt=""><br>
<img width="680" height="20" src="images/1dot.gif" alt=""></td>
</tr>
<tr>
<td scope="row">
<!--googleon: index -->
<pre class="sourcecodebox">/* File: QISAPanels.c Contains: Implementation of the various panels. Written by: DTS Copyright: Copyright © 2002 by Apple Computer, Inc., All Rights Reserved. Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in consideration of your agreement to the following terms, and your use, installation, modification or redistribution of this Apple software constitutes acceptance of these terms. If you do not agree with these terms, please do not use, install, modify or redistribute this Apple software. In consideration of your agreement to abide by the following terms, and subject to these terms, Apple grants you a personal, non-exclusive license, under Apple's copyrights in this original Apple software (the "Apple Software"), to use, reproduce, modify and redistribute the Apple Software, with or without modifications, in source and/or binary forms; provided that if you redistribute the Apple Software in its entirety and without modifications, you must retain this notice and the following text and disclaimers in all such redistributions of the Apple Software. Neither the name, trademarks, service marks or logos of Apple Computer, Inc. may be used to endorse or promote products derived from the Apple Software without specific prior written permission from Apple. Except as expressly stated in this notice, no other rights or licenses, express or implied, are granted by Apple herein, including but not limited to any patent rights that may be infringed by your derivative works or by other works in which the Apple Software may be incorporated. The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Change History (most recent first):*//////////////////////////////////////////////////////////////////#include "QISAPanels.h"// System interfaces#if TARGET_RT_MAC_CFM #include <IBCarbonRuntime.h> #include <CFNumber.h>#endif#include <stdio.h>#include <stdlib.h>// MoreIsBetter interfaces#include "MoreControls.h"#include "MoreDialogs.h"#include "MoreCFQ.h"#include "MoreCarbonEvents.h"#include "MoreProcesses.h"#include "MoreAEDataModel.h"#include "MoreAppleEvents.h"// QISA interfaces#include "QISAPlatform.h"#include "QISA.h"/////////////////////////////////////////////////////////////////#pragma mark ***** Utilities#if 0static void *GetPopupMenuRefCon(ControlRef control) // Gets the menu refcon from a popup menu control.{ OSStatus err; void * result; MenuRef popupMenu; MenuItemIndex currentItem; assert(control != NULL); result = NULL; err = GetControlData(control, kControlEntireControl, kControlPopupButtonMenuRefTag, sizeof(popupMenu), &popupMenu, NULL); if (err == noErr) { currentItem = (MenuItemIndex) GetControlValue(control); err = GetMenuItemRefCon(popupMenu, (SInt16) currentItem, (UInt32 *) &result); } assert( (err == noErr) && (result != NULL) ); return result;}#endifstatic OSStatus SetTextControlToGlobalString(QISAPanel *panel, WindowRef window, OSType tag, SInt32 id, CFStringRef key) // Gets the global value (using QISAGetGlobalValue) for key // and stores the resulting string into the text contrel // described by window, tag, and id.{ OSStatus err; ControlRef control; CFPropertyListRef value; assert(QISAIsPanelValid(panel)); assert(window != NULL); assert(key != NULL); err = QISAGetGlobalValue(panel, key, &value); if (err == noErr) { err = GetControlByIDQ(window, tag, id, &control); } if (err == noErr) { assert( CFGetTypeID(value) == CFStringGetTypeID() ); err = SetTextControlTextCompat(control, false, (CFStringRef) value); } return err;}/////////////////////////////////////////////////////////////////#pragma mark ***** PortCCL Panel// The PortCCL panel allows the user to choose a modem port // and its CCL. It stores the results as the kQISAKeyChosenPort and // kQISAKeyChosenCCL global values.// PortCCLPanelData represents the panel's private data. // A reference to this structure is stored in the panel's // refCon.static const OSType kPortCCLPanelMagic = 'PCCL';struct PortCCLPanelData { OSType magic; // must be kPortCCLPanelMagic CFArrayRef portArray; // of CFDictionary, as returned by QISACreatePortArray ControlRef portPopup; // reference to the port popup menu control MenuRef portPopupMenu; // reference to the menu inside that control CFArrayRef cclArray; // of CFDictionary, as returned by QISACreateCCLArray ControlRef cclPopup; // reference to the CCL popup menu control MenuRef cclPopupMenu; // reference to the menu inside that control PanelSwitchToProc oldSwitchTo; // inherited SwitchTo proc PanelSwitchFromProc oldSwitchFrom; // inherited SwitchFrom proc};typedef struct PortCCLPanelData PortCCLPanelData;static PortCCLPanelData *GetPortCCLPanelData(QISAPanel *panel) // Returns the panel's private data.{ PortCCLPanelData * result; assert(QISAIsPanelValid(panel)); result = (PortCCLPanelData *) panel->refCon; assert( (result != NULL) && (result->magic == kPortCCLPanelMagic) ); return result;}#if 0static void ResetMenuWithCFRefCons(MenuRef menu, CFTypeID refConType){ OSStatus junk; UInt16 menuItemCount; SInt16 menuItemIndex; menuItemCount = CountMenuItems(menu); for (menuItemIndex = 1; menuItemIndex <= menuItemCount; menuItemIndex++) { CFDictionaryRef thisItemRefCon; thisItemRefCon = NULL; junk = GetMenuItemRefCon(menu, menuItemIndex, (UInt32 *) &thisItemRefCon); assert(junk == noErr); assert(thisItemRefCon != NULL); assert( CFGetTypeID(thisItemRefCon) == refConType ); CFQRelease(thisItemRefCon); } junk = DeleteMenuItems(menu, 1, menuItemCount); assert(junk == noErr); }#endifstatic void PortCCLPanelTerminate(QISAPanel *panel) // See PanelTerminateProc comments.{ PortCCLPanelData *panelData; // Can't call GetPortCCLPanelData because assertions may not hold // during termination if we weren't properly initialised. panelData = (PortCCLPanelData *) panel->refCon; if (panelData != NULL) { CFQRelease(panelData->portArray); CFQRelease(panelData->cclArray); panelData->magic = 'free'; free(panelData); }}#if MORE_DEBUG static void MungePortArray(QISAPanel *panel) // This routine adds a dummy modem port to the port array // if the port array is empty. I added it so that I could test // certain aspects of QISA on a victim machine that does not // have a modem port. { PortCCLPanelData * panelData; panelData = GetPortCCLPanelData(panel); assert(panelData->portArray != NULL); if ( CFArrayGetCount(panelData->portArray) == 0 ) { CFStringRef keys[2]; CFStringRef values[2]; CFDictionaryRef portDict; CFRelease(panelData->portArray); keys[0] = kSCPropUserDefinedName; values[0] = CFSTR("Hack Port"); keys[1] = kSCPropNetInterfaceHardware; values[1] = kSCEntNetModem; portDict = CFDictionaryCreate(NULL, (const void **) keys, (const void **) values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); assert(portDict != NULL); panelData->portArray = CFArrayCreate(NULL, (const void **) &portDict, 1, &kCFTypeArrayCallBacks); assert(panelData->portArray != NULL); } }#endifstatic OSStatus SetupPortPopup(QISAPanel *panel){ OSStatus err; OSStatus junk; PortCCLPanelData * panelData; CFDictionaryRef chosenPortDict; CFStringRef chosenPortUserDefinedName; MenuItemIndex chosenPortMenuItemIndex; int numSerialPorts; chosenPortMenuItemIndex = 0; numSerialPorts = 0; panelData = GetPortCCLPanelData(panel); // First get the chosen value, if any, from the global dictionary. junk = QISAGetGlobalValue(panel, kQISAKeyChosenPort, (CFPropertyListRef *) &chosenPortDict); assert( (junk == noErr) == (chosenPortDict != NULL) ); if ( chosenPortDict != NULL ) { assert( CFGetTypeID(chosenPortDict) == CFDictionaryGetTypeID() ); chosenPortUserDefinedName = CFDictionaryGetValue(chosenPortDict, kSCPropUserDefinedName); } else { chosenPortUserDefinedName = NULL; } assert( (chosenPortUserDefinedName == NULL) || (CFGetTypeID(chosenPortUserDefinedName) == CFStringGetTypeID()) ); // Delete the old menu contents and also get rid of the port array. junk = DeleteMenuItems(panelData->portPopupMenu, 1, CountMenuItems(panelData->portPopupMenu)); assert(junk == noErr); CFQRelease(panelData->portArray); panelData->portArray = NULL; // Now create the popup based on the current port scan. Note // that we don't release panelData->portArray if we get an error, // because we'll release the next time PortCCLPanelSwitchTo calls us, or // when PortCCLPanelTerminate is called. err = QISACreatePortArray(&panelData->portArray); if (err == noErr) { CFIndex portCount; CFIndex portIndex; #if 0 && MORE_DEBUG MungePortArray(panel); #endif portCount = CFArrayGetCount(panelData->portArray); for (portIndex = 0; portIndex < portCount; portIndex++) { CFDictionaryRef portDict; MenuItemIndex addedItemIndex; portDict = CFArrayGetValueAtIndex(panelData->portArray, portIndex); assert((portDict != NULL) && (CFGetTypeID(portDict) == CFDictionaryGetTypeID())); if ( CFEqual(CFDictionaryGetValue(portDict, kSCPropNetInterfaceHardware), kSCEntNetModem) ) { CFStringRef thisPortUserDefinedName; thisPortUserDefinedName = CFDictionaryGetValue(portDict, kSCPropUserDefinedName); assert(thisPortUserDefinedName != NULL); err = AppendMenuItemTextWithCFString(panelData->portPopupMenu, thisPortUserDefinedName, 0, 0, &addedItemIndex); if (err == noErr) { numSerialPorts += 1; if ( (chosenPortUserDefinedName != NULL) && CFEqual(chosenPortUserDefinedName, thisPortUserDefinedName) ) { chosenPortMenuItemIndex = addedItemIndex; } } if (err != noErr) { break; } } } } // Finally, set the popup value to the chosen port if it's present. if (err == noErr) { assert(numSerialPorts < 32768); SetControlMaximum(panelData->portPopup, (short) numSerialPorts); assert( (numSerialPorts == 0) || (GetControlValue(panelData->portPopup) == 1) ); if (numSerialPorts == 0) { chosenPortMenuItemIndex = 0; } else if (chosenPortMenuItemIndex == 0) { chosenPortMenuItemIndex = 1; } SetControlValue(panelData->portPopup, (short) chosenPortMenuItemIndex ); } return err;}static OSStatus SetupCCLPopup(QISAPanel *panel){ OSStatus err; OSStatus junk; PortCCLPanelData * panelData; CFIndex defaultCCLIndex; CFStringRef chosenCCLName; MenuItemIndex chosenCCLMenuItemIndex; int numCCLs; chosenCCLMenuItemIndex = 0; numCCLs = 0; panelData = GetPortCCLPanelData(panel); // Get the default CCL name, if any, from the global dictionary. junk = QISAGetGlobalValue(panel, kQISAKeyChosenCCL, (CFPropertyListRef *) &chosenCCLName); assert( (junk == noErr) == (chosenCCLName != NULL) ); assert( (chosenCCLName == NULL) || (CFGetTypeID(chosenCCLName) == CFStringGetTypeID()) ); // Delete the old menu contents and also get rid of the CCL array. junk = DeleteMenuItems(panelData->cclPopupMenu, 1, CountMenuItems(panelData->cclPopupMenu)); assert(junk == noErr); CFQRelease(panelData->cclArray); panelData->cclArray = NULL; // Now create the popup based on the current CCL scan. Note // that we don't release panelData->cclArray if we get an error, // because we'll release the next time PortCCLPanelSwitchTo calls us, or // when PortCCLPanelTerminate is called. err = QISACreateCCLArray(&panelData->cclArray, &defaultCCLIndex); if (err == noErr) { // The index returned by QISACreateCCLArray is an index of the // system default CCL in the array, which is 0 based. However, // chosenCCLMenuItemIndex is 1 based, so I have to add one to // defaultCCLIndex. // // In the case of serial ports, I think it's very unlikely that // you'll have more than 32767 serial ports, so I just assert // that limitation. However, for CCLs it's feasible that you // might have more than 32767, so I give a helpful error // message in that case. // // This check also guarantees that chosenCCLMenuItemIndex is // in the range [1..32767]. if (err == noErr) { if (CFArrayGetCount(panelData->cclArray) < 32768) { chosenCCLMenuItemIndex = (MenuItemIndex) (defaultCCLIndex + 1); } else { err = 5504; } } } if (err == noErr) { CFIndex cclCount; CFIndex cclIndex; cclCount = CFArrayGetCount(panelData->cclArray); for (cclIndex = 0; cclIndex < cclCount; cclIndex++) { MenuItemIndex addedItemIndex; CFStringRef cclStr; cclStr = CFArrayGetValueAtIndex(panelData->cclArray, cclIndex); assert(cclStr != NULL); err = AppendMenuItemTextWithCFString(panelData->cclPopupMenu, cclStr, 0, 0, &addedItemIndex); if (err == noErr) { numCCLs += 1; if ( (chosenCCLName != NULL) && CFEqual(chosenCCLName, cclStr) ) { chosenCCLMenuItemIndex = addedItemIndex; } } if (err != noErr) { break; } } } // Finally, set the popup value to the default if it's present. if (err == noErr) { assert( (numCCLs >= 0) && (numCCLs <= 32767) ); SetControlMaximum(panelData->cclPopup, (short) numCCLs); assert( chosenCCLMenuItemIndex <= 32767 ); if (numCCLs == 0) { chosenCCLMenuItemIndex = 0; } else if (chosenCCLMenuItemIndex == 0) { chosenCCLMenuItemIndex = 1; } SetControlValue(panelData->cclPopup, (short) chosenCCLMenuItemIndex); } return err;} static OSStatus PortCCLPanelSwitchTo(QISAPanel *panel) // See PanelSwitchToProc comments.{ OSStatus err; PortCCLPanelData * panelData; panelData = GetPortCCLPanelData(panel); // Set up port and CCL popup menus. err = SetupPortPopup(panel); if (err == noErr) { err = SetupCCLPopup(panel); } // Call through to inherited implementation and then override // the forward button enabled state. The forward button can // only be enabled if both popups have a selection. err = panelData->oldSwitchTo(panel); if (err == noErr) { if (QISAIsButtonEnabled(panel, kQISAPanelSwitchDirectionForward)) { QISASetButtonEnable(panel, kQISAPanelSwitchDirectionForward, (GetControlMaximum(panelData->cclPopup) > 0) && (GetControlMaximum(panelData->portPopup) > 0) ); } } return err;}static OSStatus PortCCLPanelSwitchFrom(QISAPanel *panel, QISAPanelSwitchDirection direction, QISAPanel **nextPanel) // See PanelSwitchFromProc comments.{ OSStatus err; PortCCLPanelData * panelData; CFIndex index; panelData = GetPortCCLPanelData(panel); // Store the state of the popups as global values. err = panelData->oldSwitchFrom(panel, direction, nextPanel); if (err == noErr && direction == kQISAPanelSwitchDirectionForward) { index = GetControlValue(panelData->cclPopup) - 1; assert( (index >= 0) && (index < CFArrayGetCount(panelData->cclArray)) ); err = QISASetGlobalValue(panel, kQISAKeyChosenCCL, CFArrayGetValueAtIndex(panelData->cclArray, index)); if (err == noErr) { index = GetControlValue(panelData->portPopup) - 1; assert( (index >= 0) && (index < CFArrayGetCount(panelData->portArray)) ); err = QISASetGlobalValue(panel, kQISAKeyChosenPort, CFArrayGetValueAtIndex(panelData->portArray, index)); } } return err;}extern OSStatus PortCCLPanelInitialise(QISAPanel *panel) // See PanelInitialiseProc comments.{ OSStatus err; PortCCLPanelData * panelData; assert(panel->refCon == NULL); // Create and initialise panel private data. err = noErr; panelData = (PortCCLPanelData *) calloc( 1, sizeof(*panelData) ); if (panelData == NULL) { err = memFullErr; } if (err == noErr) { err = GetControlByIDQ(panel->window, 'PPOP', 0, &panelData->portPopup); } if (err == noErr) { err = GetControlData(panelData->portPopup, kControlEntireControl, kControlPopupButtonMenuRefTag, sizeof(panelData->portPopupMenu), &panelData->portPopupMenu, NULL); } if (err == noErr) { err = GetControlByIDQ(panel->window, 'SPOP', 0, &panelData->cclPopup); } if (err == noErr) { err = GetControlData(panelData->cclPopup, kControlEntireControl, kControlPopupButtonMenuRefTag, sizeof(panelData->cclPopupMenu), &panelData->cclPopupMenu, NULL); } // Clean up. panel->refCon = panelData; if (err == noErr) { panelData->magic = kPortCCLPanelMagic; panel->Terminate = PortCCLPanelTerminate; panelData->oldSwitchTo = panel->SwitchTo; panel->SwitchTo = PortCCLPanelSwitchTo; panelData->oldSwitchFrom = panel->SwitchFrom; panel->SwitchFrom = PortCCLPanelSwitchFrom; } else { PortCCLPanelTerminate(panel); panel->refCon = NULL; } assert( (err == noErr) == (panel->refCon != NULL) ); return err;}/////////////////////////////////////////////////////////////////#pragma mark ***** User/Pass Panel// The User/Pass panel allows the user to enter a user name, // password, and phone number. It stores the results as // kQISAKeyUsername, kQISAKeyPassword, and kQISAKeyNumber // global values.// UserPassPanelData represents the panel's private data.// A reference to this structure is stored in the panel's // refCon.static const OSType kUserPassPanelMagic = 'USPW';struct UserPassPanelData { OSType magic; // must be kUserPassPanelMagic ControlRef userText; // reference to the user name edit text control ControlRef passText; // reference to the password edit text control ControlRef numberText; // reference to the phone number edit text control PanelSwitchToProc oldSwitchTo; // inherited SwitchTo proc PanelSwitchFromProc oldSwitchFrom; // inherited SwitchFrom proc Boolean forwardAllowed; // true if the inherited SwitchTo enabled the forward button};typedef struct UserPassPanelData UserPassPanelData;static UserPassPanelData *GetUserPassPanelData(QISAPanel *panel) // Returns the panel's private data.{ UserPassPanelData * result; assert(QISAIsPanelValid(panel)); result = (UserPassPanelData *) panel->refCon; assert( (result != NULL) && (result->magic == kUserPassPanelMagic) ); return result;}static void UserPassPanelTerminate(QISAPanel *panel) // See PanelTerminateProc comments.{ UserPassPanelData *panelData; // Can't call GetUserPassPanelData because assertions may not hold // during termination if we weren't properly initialised. panelData = (UserPassPanelData *) panel->refCon; if (panelData != NULL) { panelData->magic = 'free'; free(panelData); }}static OSStatus UserPassUpdateForwardButton(QISAPanel *panel) // Sets the enabled state of the forward button based on // the values in the text controls. Called from the SwitchTo // routine and whenever the text in one of the controls changes.{ OSStatus err; UserPassPanelData * panelData; panelData = GetUserPassPanelData(panel); err = noErr; if (panelData->forwardAllowed) { Boolean enabled; CFStringRef tmpText; // Assume the button can be enabled. enabled = true; // Get the text for each control, and validate it. Right now all // I do is check for empty text, but this could be smarter. tmpText = NULL; err = CopyTextControlTextCompat(panelData->userText, false, &tmpText); if (err == noErr) { if ( CFStringGetLength(tmpText) == 0 ) { enabled = false; } } CFQRelease(tmpText); tmpText = NULL; if (err == noErr) { err = CopyTextControlTextCompat(panelData->passText, true, &tmpText); } if (err == noErr) { if ( CFStringGetLength(tmpText) == 0 ) { enabled = false; } } CFQRelease(tmpText); tmpText = NULL; if (err == noErr) { err = CopyTextControlTextCompat(panelData->numberText, false, &tmpText); } if (err == noErr) { if ( CFStringGetLength(tmpText) == 0 ) { enabled = false; } } CFQRelease(tmpText); // Finally, set the enabled state of the button. QISASetButtonEnable(panel, kQISAPanelSwitchDirectionForward, enabled); } return err;}static OSStatus UserPassPanelSwitchTo(QISAPanel *panel) // See PanelSwitchToProc comments.{ OSStatus err; UserPassPanelData * panelData; panelData = GetUserPassPanelData(panel); // Get the inherited state of the forward button and then call // UserPassUpdateForwardButton to set the forward button based on // the content of the text fields. err = panelData->oldSwitchTo(panel); if (err == noErr) { panelData->forwardAllowed = QISAIsButtonEnabled(panel, kQISAPanelSwitchDirectionForward); err = UserPassUpdateForwardButton(panel); } return err;}static OSStatus UserPassPanelSwitchFrom(QISAPanel *panel, QISAPanelSwitchDirection direction, QISAPanel **nextPanel) // See PanelSwitchFromProc comments.{ OSStatus err; UserPassPanelData * panelData; panelData = GetUserPassPanelData(panel); // Store the text fields as global values. err = panelData->oldSwitchFrom(panel, direction, nextPanel); if (err == noErr && direction == kQISAPanelSwitchDirectionForward) { CFStringRef tmpText; tmpText = NULL; err = CopyTextControlTextCompat(panelData->userText, false, &tmpText); if (err == noErr) { err = QISASetGlobalValue(panel, kQISAKeyUsername, tmpText); } CFQRelease(tmpText); tmpText = NULL; if (err == noErr) { err = CopyTextControlTextCompat(panelData->passText, true, &tmpText); } if (err == noErr) { err = QISASetGlobalValue(panel, kQISAKeyPassword, tmpText); } CFQRelease(tmpText); tmpText = NULL; if (err == noErr) { err = CopyTextControlTextCompat(panelData->numberText, false, &tmpText); } if (err == noErr) { err = QISASetGlobalValue(panel, kQISAKeyNumber, tmpText); } CFQRelease(tmpText); } return err;}// Blah! In the Carbon events universe there's no easy way to be told when the // contents of a text field changes. I had to implement a bunch of ugly code // to make this work. Fortunately most of that code is hidden away in // MoreControls. All this module has to do is to declare a Carbon event // handler and install it appropriately.static const EventTypeSpec kUserPassRootEvents[1] = { {kMoreIsBetterEventClass, kMIBControlEditTextModifiedKind} };static EventHandlerUPP gUserPassRootEventHandlerUPP; // -> UserPassRootEventHandlerstatic pascal OSStatus UserPassRootEventHandler(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData) // This event handler is called whenever the text in one of our // edit text controls is modified. We respond to that by calling // UserPassUpdateForwardButton to update the enabled state of // the forward button.{ OSStatus junk; QISAPanel * panel; #pragma unused(inHandlerCallRef) panel = (QISAPanel *) inUserData; assert( GetEventClass(inEvent) == kMoreIsBetterEventClass ); assert( GetEventKind(inEvent) == kMIBControlEditTextModifiedKind); junk = UserPassUpdateForwardButton(panel); assert(junk == noErr); // Return eventNotHandledErr to allow other handlers to see this event. return eventNotHandledErr;}extern OSStatus UserPassPanelInitialise(QISAPanel *panel) // See PanelInitialiseProc comments.{ OSStatus err; UserPassPanelData * panelData; assert(panel->refCon == NULL); // Create and initialise panel private data. err = noErr; panelData = (UserPassPanelData *) calloc( 1, sizeof(*panelData) ); if (panelData == NULL) { err = memFullErr; } if (err == noErr) { err = GetControlByIDQ(panel->window, 'USER', 0, &panelData->userText); } if (err == noErr) { err = GetControlByIDQ(panel->window, 'PASS', 0, &panelData->passText); } if (err == noErr) { err = GetControlByIDQ(panel->window, 'NUMB', 0, &panelData->numberText); } // Set the value of each edit text control from global values. // After updating the text fields, we don't need to UserPassUpdateForwardButton // because QISA will call our SwitchTo routine before the user has a chance // to click on the button, and UserPassPanelSwitchTo routine will update the // state of the forward button. if (err == noErr) { CFStringRef value; if ( QISAGetGlobalValue(panel, kQISAKeyUsername, (CFPropertyListRef *) &value) == noErr) { assert( CFGetTypeID(value) == CFStringGetTypeID() ); err = SetTextControlTextCompat(panelData->userText, false, (CFStringRef) value); } } if (err == noErr) { CFStringRef value; if ( QISAGetGlobalValue(panel, kQISAKeyPassword, (CFPropertyListRef *) &value) == noErr) { assert( CFGetTypeID(value) == CFStringGetTypeID() ); err = SetTextControlTextCompat(panelData->passText, true, (CFStringRef) value); } } if (err == noErr) { CFStringRef value; if ( QISAGetGlobalValue(panel, kQISAKeyNumber, (CFPropertyListRef *) &value) == noErr) { assert( CFGetTypeID(value) == CFStringGetTypeID() ); err = SetTextControlTextCompat(panelData->numberText, false, (CFStringRef) value); } } // Install the callbacks required to get UserPassRootEventHandler called // whenever the content of our edit text controls change. if (err == noErr) { if (gUserPassRootEventHandlerUPP == NULL) { gUserPassRootEventHandlerUPP = NewEventHandlerUPP(UserPassRootEventHandler); assert(gUserPassRootEventHandlerUPP != NULL); } err = InstallControlEventHandler(panel->panelControl, gUserPassRootEventHandlerUPP, GetEventTypeCount(kUserPassRootEvents), kUserPassRootEvents, panel, NULL); } if (err == noErr) { err = InstallControlKeyFilter(panelData->userText, kMIBControlNoReturnsKeyFilterKeyFilterTag, GetControlNoReturnsKeyFilterUPP()); } if (err == noErr) { err = InstallControlKeyFilter(panelData->userText, kMIBControlEditTextModifiedKeyFilterTag, GetControlEditTextModifiedKeyFilterUPP()); } if (err == noErr) { err = InstallControlKeyFilter(panelData->passText, kMIBControlNoReturnsKeyFilterKeyFilterTag, GetControlNoReturnsKeyFilterUPP()); } if (err == noErr) { err = InstallControlKeyFilter(panelData->passText, kMIBControlEditTextModifiedKeyFilterTag, GetControlEditTextModifiedKeyFilterUPP()); } if (err == noErr) { err = InstallControlKeyFilter(panelData->numberText, kMIBControlNoReturnsKeyFilterKeyFilterTag, GetControlNoReturnsKeyFilterUPP()); } if (err == noErr) { err = InstallControlKeyFilter(panelData->numberText, kMIBControlEditTextModifiedKeyFilterTag, GetControlEditTextModifiedKeyFilterUPP()); } if (err == noErr) { ControlEditTextValidationUPP tmp; tmp = GetControlEditTextModifiedValidationProcUPP(); err = SetControlData(panelData->userText, kControlEntireControl, kControlEditTextValidationProcTag, sizeof(tmp), &tmp); if (err == noErr) { err = SetControlData(panelData->passText, kControlEntireControl, kControlEditTextValidationProcTag, sizeof(tmp), &tmp); } if (err == noErr) { err = SetControlData(panelData->numberText, kControlEntireControl, kControlEditTextValidationProcTag, sizeof(tmp), &tmp); } } // Clean up. panel->refCon = panelData; if (err == noErr) { panelData->magic = kUserPassPanelMagic; panel->Terminate = UserPassPanelTerminate; panelData->oldSwitchTo = panel->SwitchTo; panel->SwitchTo = UserPassPanelSwitchTo; panelData->oldSwitchFrom = panel->SwitchFrom; panel->SwitchFrom = UserPassPanelSwitchFrom; } else { UserPassPanelTerminate(panel); panel->refCon = NULL; } assert( (err == noErr) == (panel->refCon != NULL) ); return err;}/////////////////////////////////////////////////////////////////#pragma mark ***** Setup Panel// The Setup panel is where the rubber hits the road. It is the place // where the user actually commits to creating a new network configuration.// In a future world, the Setup panel will be more asynchronous, allowing all // sorts of advanced technology (such as dialling the modem using a temporary // connection). Right now the panel is only mildly asynchronous, while we // wait for network control panels to quit. Thus, I only use the states // marked with a "*".typedef enum { kSetupStateNotStarted, // * user can not yet clicked "Do It" button kSetupStateWaitingForQuit, // * waiting for control panels to quit kSetupStateCreatingTemporary, // creating a temporary configuration to allow the creation of the user account kSetupStateConnecting, // connecting with that temporary configuration kSetupStateConfiguring, // creating the user account on the server kSetupStateDisconnecting, // disconnecting the temporary connection kSetupStateCreatingPermanent, // * creating the user's permanent network settings kSetupStateDone // * all done} SetupState;/* State Transitions ----------------- kSetupStateNotStarted + "Do It" + SetupPanelAnyControlPanelsRunning -> kSetupStateWaitingForQuit + SetupPanelQuitAllControlPanels + start timer kSetupStateNotStarted + "Do It" + ! SetupPanelAnyControlPanelsRunning -> kSetupStateCreatingPermanent kSetupStateWaitingForQuit + timer expires + SetupPanelAnyControlPanelsRunning -> do nothing kSetupStateWaitingForQuit + timer expires + ! SetupPanelAnyControlPanelsRunning -> kSetupStateCreatingPermanent + SetupPanelStopTimer kSetupStateWaitingForQuit + "Stop" -> kSetupStateNotStarted + SetupPanelStopTimer kSetupStateCreatingPermanent -> kSetupStateDone*/// SetupPanelData represents the panel's private data.// A reference to this structure is stored in the panel's // refCon.static const OSType kSetupPanelMagic = 'StUp';struct SetupPanelData { OSType magic; // must be kSetupPanelMagic ControlRef statusText; // reference to the status static text control ControlRef arrow; // reference to the chasing arrows control ControlRef doItButton; // reference to the Do It button control ControlRef stopButton; // reference to the Cancel button control ControlRef infoButton; // reference to the Info button control PanelSwitchToProc oldSwitchTo; // inherited SwitchTo proc PanelSwitchFromProc oldSwitchFrom; // inherited SwitchFrom proc Boolean forwardAllowed; // true if the inherited SwitchTo enabled the forward button SetupState state; // current state of the panel EventLoopTimerRef waitQuitTimer; // -> gSetupPanelWaitQuitTimerUPP -> SetupPanelWaitQuitTimerProc CFArrayRef appsToGo; // platforms kQISAKeyControlPanels information CFStringRef blockingProcessName;// name of process that we're waiting for; only meaningful in state kSetupStateWaitingForQuit};typedef struct SetupPanelData SetupPanelData;static SetupPanelData *GetSetupPanelData(QISAPanel *panel) // Returns the panel's private data.{ SetupPanelData * result; assert(QISAIsPanelValid(panel)); result = (SetupPanelData *) panel->refCon; assert( (result != NULL) && (result->magic == kSetupPanelMagic) ); return result;}static void SetupPanelStopTimer(SetupPanelData *panelData) // Stop the waitQuitTimer.{ OSStatus junk; assert(panelData != NULL); assert(panelData->waitQuitTimer != NULL); junk = RemoveEventLoopTimer(panelData->waitQuitTimer); assert(junk == noErr); panelData->waitQuitTimer = NULL;}static void SetupPanelTerminate(QISAPanel *panel) // See PanelTerminateProc comments.{ SetupPanelData *panelData; // Can't call GetSetupPanelData because assertions may not hold // during termination if we weren't properly initialised. panelData = (SetupPanelData *) panel->refCon; if (panelData != NULL) { // The timer should not be running because our SetupPanelSwitchFrom must // return successfully before we're terminated, and SetupPanelSwitchFrom // only succeeds if it SetupPanelCommandStop succeeds, and SetupPanelCommandStop // calls SetupPanelStopTimer. However, in the principle of boots and suspenders, // we do handle this weird case in the non-debug build. We also have an // assert so that if it even happens in the debug build we hear about it. assert(panelData->waitQuitTimer == NULL); if (panelData->waitQuitTimer != NULL) { SetupPanelStopTimer(panelData); } CFQRelease(panelData->appsToGo); CFQRelease(panelData->blockingProcessName); panelData->magic = 'free'; free(panelData); }}static OSStatus SetupPanelSwitchTo(QISAPanel *panel) // See PanelSwitchToProc comments.{ OSStatus err; SetupPanelData * panelData; panelData = GetSetupPanelData(panel); // Call through to inherited implementation and then override // the forward button enabled state. The forward button can // only be enabled if the state is kSetupStateDone. assert( (panelData->state == kSetupStateNotStarted) || (panelData->state == kSetupStateDone) ); err = panelData->oldSwitchTo(panel); if (err == noErr) { panelData->forwardAllowed = QISAIsButtonEnabled(panel, kQISAPanelSwitchDirectionForward); QISASetButtonEnable(panel, kQISAPanelSwitchDirectionForward, panelData->forwardAllowed && (panelData->state == kSetupStateDone)); } return err;}static void SetupPanelCommandStop(QISAPanel *panel);static OSStatus SetupPanelSwitchFrom(QISAPanel *panel, QISAPanelSwitchDirection direction, QISAPanel **nextPanel) // See PanelSwitchFromProc comments.{ OSStatus err; SetupPanelData * panelData; panelData = GetSetupPanelData(panel); // If we're stepping backwards and the state is not quiescent, then // simulate a click on the Stop button. If that doesn't quiesce the // state (in a future build this could happen if SetupPanelCommandStop // displays a confirmation alert), then the user canceled and we // return an appropriate error. err = panelData->oldSwitchFrom(panel, direction, nextPanel); if ( (err == noErr) && (direction == kQISAPanelSwitchDirectionBackward) ) { if ( (panelData->state != kSetupStateNotStarted) && (panelData->state != kSetupStateDone) ) { SetupPanelCommandStop(panel); } if ( (panelData->state != kSetupStateNotStarted) && (panelData->state != kSetupStateDone) ) { err = userCanceledErr; } } return err;}static void SetupPanelCommandInfo(QISAPanel *panel) // Runs the dialog box that comes up when you click the Info button.{ OSStatus err; IBNibRef nibRef; WindowRef infoWindow; assert(QISAIsPanelValid(panel)); nibRef = NULL; infoWindow = NULL; // Create the dialog from our NIB. err = CreateNibReference(CFSTR("QISASetupPanel"), &nibRef); if (err == noErr) { err = CreateWindowFromNib(nibRef, CFSTR("InfoWindow"), &infoWindow); } if (err == noErr) { err = InstallOKCancelWindowEventHandler(infoWindow); } // Fill out the fields. // Modem Port if (err == noErr) { ControlRef control; CFPropertyListRef value; err = QISAGetGlobalValue(panel, kQISAKeyChosenPort, &value); if (err == noErr) { err = GetControlByIDQ(infoWindow, 'INFO', 0, &control); } if (err == noErr) { assert( CFGetTypeID(value) == CFDictionaryGetTypeID() ); value = CFDictionaryGetValue( (CFDictionaryRef) value, kSCPropUserDefinedName); assert( CFGetTypeID(value) == CFStringGetTypeID() ); err = SetTextControlTextCompat(control, false, (CFStringRef) value); } } // Modem Script if (err == noErr) { err = SetTextControlToGlobalString(panel, infoWindow, 'INFO', 1, kQISAKeyChosenCCL); } // Setup User if (err == noErr) { err = SetTextControlToGlobalString(panel, infoWindow, 'INFO', 2, kQISAKeyUsername); } // Setup Password if (err == noErr) { err = SetTextControlToGlobalString(panel, infoWindow, 'INFO', 3, kQISAKeyPassword); } // Setup Number if (err == noErr) { err = SetTextControlToGlobalString(panel, infoWindow, 'INFO', 4, kQISAKeyNumber); } // Setup Use Terminal if (err == noErr) { ControlRef control; CFPropertyListRef value; err = QISAGetGlobalValue(panel, kQISAKeyUseTerminal, &value); if (err == noErr) { err = GetControlByIDQ(infoWindow, 'INFO', 5, &control); } if (err == noErr) { assert( CFGetTypeID(value) == CFBooleanGetTypeID() ); err = SetTextControlTextCompat(control, false, CFBooleanGetValue( (CFBooleanRef) value) ? CFSTR("TRUE") : CFSTR("FALSE") ); } } // Run the dialog. if (err == noErr) { ShowWindow(infoWindow); err = RunAppModalLoopForWindow(infoWindow); } // Clean up. if (infoWindow != NULL) { DisposeWindow(infoWindow); } if (nibRef != NULL) { DisposeNibReference(nibRef); } QISADisplayError(NULL, err, CFSTR("DisplayingTheInformation"));}static void SetupPanelSyncUIToState(QISAPanel *panel) // Synchronised the control in the window to changes in the panel's state. // Called whenever the panel changes state (and by the Initialisation proc).{ OSStatus junk; SetupPanelData * panelData; SetupState state; Boolean started; Boolean active; CFStringRef key; CFStringRef stateFormat; CFStringRef stateStr; panelData = GetSetupPanelData(panel); state = panelData->state; started = (state != kSetupStateNotStarted); active = ((state != kSetupStateNotStarted) && (state != kSetupStateDone)); SetControlVisible(panelData->arrow, active ); SetControlActive( panelData->doItButton, ! started); SetControlActive( panelData->stopButton, active ); // Put a localised description of the state into the statusText. stateStr = NULL; key = CFStringCreateWithFormat(NULL, NULL, CFSTR("state%d"), (int) state); assert(key != NULL); stateFormat = CFBundleCopyLocalizedString(CFBundleGetMainBundle(), key, NULL, NULL); if (stateFormat != NULL) { stateStr = CFStringCreateWithFormat(NULL, NULL, stateFormat, panelData->blockingProcessName); if (stateStr != NULL) { junk = SetTextControlTextCompat(panelData->statusText, false, stateStr); assert(junk == noErr); Draw1Control(panelData->statusText); } } CFQRelease(stateFormat); CFQRelease(key); CFQRelease(stateStr); // Update the forward button. You can only go forward if the state is kSetupStateDone, // indicating that we've correctly set up the network. QISASetButtonEnable(panel, kQISAPanelSwitchDirectionForward, panelData->forwardAllowed && (panelData->state == kSetupStateDone));}static void SetupPanelCreateNetworkConfig(QISAPanel *panel, Boolean temporary) // A sub-routine used to create a network configuration by // calling the platform support routine, QISAMakeNetworkConfig. // The parameters that control the setup are stored in the // global values dictionary. Right now all clients pass false to // temporary, so we always set up a permanent configuration.{ OSStatus err; CFMutableDictionaryRef configDict; SetupPanelData * panelData; panelData = GetSetupPanelData(panel); configDict = NULL; // Tell the user what we're doing. if (temporary) { panelData->state = kSetupStateCreatingTemporary; } else { panelData->state = kSetupStateCreatingPermanent; } SetupPanelSyncUIToState(panel); // Do it. err = QISACopyGlobalsDict(panel, &configDict); if (err == noErr && temporary) { CFDictionaryAddValue(configDict, kQISAKeyTemporary, kCFBooleanTrue); } // err = -1; if (err == noErr) { err = QISAMakeNetworkConfig(configDict); } // Tell the user what happened. if (err == noErr) { if (temporary) { assert(false); panelData->state = kSetupStateNotStarted; // will eventually be kSetupStateConnecting, but that state not implemented yet } else { panelData->state = kSetupStateDone; } } else { panelData->state = kSetupStateNotStarted; } SetupPanelSyncUIToState(panel); QISADisplayError(NULL, err, CFSTR("CreatingTheNetworkSettings")); // Clean up. CFQRelease(configDict);}/* Control Panel Notes ------------------- Network setup can't take place while the network control panel is open. This is because the control panel is too lame to notice that we've changed the network state via the API, and continues to show obsolete (and confusing) information. So, we quit the panel before we start setting up the network preferences. This raises the question, which panel? Well, the list is different on traditional Mac OS vs Mac OS X. So, we get the list of panels to quit from the platform, via the global values dictionary. See kQISAKeyControlPanels for more information about how this is specified. We get that array and store it in panelData->appsToGo (which is an atrocious pun on a very old DTS sample code library).*/static OSStatus SetupPanelGetControlPanelPSN(QISAPanel *panel, CFIndex appIndex, ProcessSerialNumber *appPSN) // Get the process serial number of the appIndex'th network control panel. // The array of control panels, panelData->appsToGo, is provided to us // by the platform. See "Control Panel Notes" above. We index into // that array, extra the type and creator, and then search for a process // using that information. If we find it, we return true and place // its PSN into *appPSN.{ OSStatus err; SetupPanelData * panelData; CFDictionaryRef appDict; CFStringRef appCreatorStr; CFStringRef appTypeStr; OSType appCreator; OSType appType; CFIndex junkCount; CFIndex junkUsedCount; panelData = GetSetupPanelData(panel); assert( (appIndex >= 0) && (appIndex < CFArrayGetCount(panelData->appsToGo)) ); assert(appPSN != NULL); appDict = (CFDictionaryRef) CFArrayGetValueAtIndex(panelData->appsToGo, appIndex); assert( (appDict != NULL) && (CFGetTypeID(appDict) == CFDictionaryGetTypeID()) ); // CFShow(appDict); appCreatorStr = (CFStringRef) CFDictionaryGetValue(appDict, kQISAKeyControlPanelsCreator); assert( (appCreatorStr != NULL) && (CFGetTypeID(appCreatorStr) == CFStringGetTypeID()) ); // CFShow(appCreatorStr); appTypeStr = (CFStringRef) CFDictionaryGetValue(appDict, kQISAKeyControlPanelsType); assert( (appTypeStr != NULL) && (CFGetTypeID(appTypeStr) == CFStringGetTypeID()) ); // CFShow(appTypeStr); junkCount = CFStringGetBytes(appCreatorStr, CFRangeMake(0, CFStringGetLength(appCreatorStr)), kCFStringEncodingMacRoman, 0, false, (UInt8 *) &appCreator, sizeof(appCreator), &junkUsedCount); assert(junkCount == CFStringGetLength(appCreatorStr)); assert(junkUsedCount == sizeof(appCreator)); junkCount = CFStringGetBytes(appTypeStr, CFRangeMake(0, CFStringGetLength(appTypeStr)), kCFStringEncodingMacRoman, 0, false, (UInt8 *) &appType, sizeof(appType), &junkUsedCount); assert(junkCount == CFStringGetLength(appTypeStr)); assert(junkUsedCount == sizeof(appType)); if ( appType == '????' ) { err = MoreProcFindProcessByCreator(appCreator, appPSN); } else { err = MoreProcFindProcessBySignature(appCreator, appType, appPSN); } return err;}static Boolean SetupPanelAnyControlPanelsRunning(QISAPanel *panel) // Returns true if any of the control panels listed in panelData->appsToGo // is running. If it does return true, it also sets panelData->blockingProcessName // to the name of the first process it found, so SetupPanelSyncUIToState and // SetupPanelAskUserWhetherToQuit can display the name of the process we're // waiting on.{ Boolean result; SetupPanelData * panelData; CFIndex appCount; CFIndex appIndex; ProcessSerialNumber blockingPSN; panelData = GetSetupPanelData(panel); result = false; appCount = CFArrayGetCount(panelData->appsToGo); for (appIndex = 0; appIndex < appCount; appIndex++) { if (SetupPanelGetControlPanelPSN(panel, appIndex, &blockingPSN) == noErr) { result = true; break; } } // Put the name of the blocking process into // panelData->blockingProcessName, where it'll be picked up by // SetupPanelSyncUIToState and SetupPanelAskUserWhetherToQuit. if (result) { OSStatus err; CFStringRef currentBlockingProcess; currentBlockingProcess = NULL; err = CopyProcessName(&blockingPSN, &currentBlockingProcess); if ( (err == noErr) && ((panelData->blockingProcessName == NULL) || ! CFEqual(currentBlockingProcess, panelData->blockingProcessName)) ) { CFQRelease(panelData->blockingProcessName); panelData->blockingProcessName = NULL; CFRetain(currentBlockingProcess); panelData->blockingProcessName = currentBlockingProcess; SetupPanelSyncUIToState(panel); } CFQRelease(currentBlockingProcess); assert(err == noErr); } return result;}static void SetupPanelQuitAllControlPanels(QISAPanel *panel) // Bring the control panels to the front so that the user can see // any dialog that they show. We only do this once, here, instead // of in the timer, because the timer would do it once a second, // which makes for a very unusable machine. // // After bringing the control panels to the front, we send them // a quit Apple event. Again, we do this once when we start the // process, not in the timer (which I originally had). This avoids // the control panels beeping once per second, as they get a quit Apple // event that they can't handle while they have a modal dialog up.{ SetupPanelData * panelData; CFIndex appCount; CFIndex appIndex; ProcessSerialNumber appPSN; panelData = GetSetupPanelData(panel); // First go through the list of control panels and bring each to the front. // Bring them to front in the reverse order, which means that, when we send // them quit events, the first event goes to the frontmost panel. appCount = CFArrayGetCount(panelData->appsToGo); for (appIndex = 0; appIndex < appCount; appIndex++) { if ( SetupPanelGetControlPanelPSN(panel, (appCount - 1) - appIndex, &appPSN) == noErr) { (void) SetFrontProcess(&appPSN); } } // Now go through the list again and send them quit events. appCount = CFArrayGetCount(panelData->appsToGo); for (appIndex = 0; appIndex < appCount; appIndex++) { if ( SetupPanelGetControlPanelPSN(panel, appIndex, &appPSN) == noErr) { OSStatus err; AppleEvent quitEvent; AppleEvent junkReply; MoreAENullDesc(&quitEvent); err = MoreAECreateAppleEventProcessTarget(&appPSN, kCoreEventClass, kAEQuitApplication, &quitEvent); if (err == noErr) { err = AESend(&quitEvent, &junkReply, kAENoReply, kAENormalPriority, kAEDefaultTimeout, NULL, NULL); } MoreAEDisposeDesc(&quitEvent); assert(err == noErr); } }}static EventLoopTimerUPP gSetupPanelWaitQuitTimerUPP; // -> SetupPanelWaitQuitTimerProcstatic pascal void SetupPanelWaitQuitTimerProc(EventLoopTimerRef inTimer, void *inUserData) // This timer callback is called periodically while we wait for // network control panels to quit. If we find that the control // panels have quit, we call SetupPanelCreateNetworkConfig to // actually configure the network settings. // // This is a (very slow) form of polling. I could eliminate the // polling by watching application termination using the // kEventClassApplication/kEventAppTerminated Carbon event. // I decided not to do that because a) this polling is very slow, // and hence not particularly expensive, and b) by relying on // events, I expose myself to race conditions. Closing all // of the race conditions would complicate the code somewhat, // and that was more hassle than was justified given the limited // costs of this polling.{ OSStatus err; QISAPanel * panel; SetupPanelData * panelData; panel = (QISAPanel *) inUserData; assert(QISAIsPanelValid(panel)); panelData = GetSetupPanelData(panel); assert(inTimer == panelData->waitQuitTimer); if ( SetupPanelAnyControlPanelsRunning(panel) ) { // do nothing, waiting for the applications to quit err = noErr; } else { SetupPanelStopTimer(panelData); SetupPanelCreateNetworkConfig(panel, false); err = noErr; } assert(err == noErr);}static OSStatus SetupPanelAskUserWhetherToQuit(QISAPanel *panel) // Pose a dialog asking the user whether they want to quit network // preferences applications before continuing with the setup. // Returns userCanceledErr if the user doesn't agree. // // Note that this includes the name of one of the blocking // applications as an example. That name comes from // panelData->blockingProcessName, which is set up by // SetupPanelAnyControlPanelsRunning. This makes sense given that // SetupPanelAnyControlPanelsRunning is called to determine whether // we need to display this dialog.{ OSStatus err; SetupPanelData * panelData; CFStringRef errStr; CFStringRef expStrFormat; CFStringRef expStr; CFStringRef defaultStr; AlertStdCFStringAlertParamRec alertParams; SInt16 itemHit; panelData = GetSetupPanelData(panel); errStr = NULL; expStrFormat = NULL; expStr = NULL; defaultStr = NULL; errStr = CFCopyLocalizedString(CFSTR("QuitAppsErrStr"), CFSTR("QuitNetworkPreferencesApplications?")); err = CFQError(errStr); if (err == noErr) { expStrFormat = CFCopyLocalizedString(CFSTR("QuitAppsExpStrFormat"), CFSTR("QISACanNotModifyNetworkSettingsWhileCertainNetworkPreferencesApplications(SuchAs%@)AreRunning")); err = CFQError(expStrFormat); } if (err == noErr) { expStr = CFStringCreateWithFormat(NULL, NULL, expStrFormat, panelData->blockingProcessName); err = CFQError(expStr); } if (err == noErr) { defaultStr = CFCopyLocalizedString(CFSTR("QuitAppsDefault"), CFSTR("QuitApplications")); err = CFQError(defaultStr); } if (err == noErr) { alertParams.version = kStdCFStringAlertVersionOne; alertParams.movable = true; alertParams.helpButton = false; alertParams.defaultText = defaultStr; alertParams.cancelText = (CFStringRef) kAlertDefaultCancelText; alertParams.otherText = NULL; alertParams.defaultButton = kAlertStdAlertOKButton; alertParams.cancelButton = kAlertStdAlertCancelButton; alertParams.position = kWindowDefaultPosition; alertParams.flags = 0; err = StandardAlertCFStringCompat(kAlertCautionAlert, errStr, expStr, &alertParams, &itemHit); if ( (err == noErr) && (itemHit == kAlertStdAlertCancelButton) ) { err = userCanceledErr; } } CFQRelease(errStr); CFQRelease(expStrFormat); CFQRelease(expStr); CFQRelease(defaultStr); return err;}static void SetupPanelCommandDoIt(QISAPanel *panel) // Called when the Do It button is clicked. This routine // creates the network configuration for the user. There // are two execution paths here. If no network control // panel applications are running, we simply call // SetupPanelCreateNetworkConfig to do the work and we're // done. If network control panels are running, we // enter the kSetupStateWaitingForQuit, start a timer // to poll for the applications having been quit, and // send an Apple event to the control panels to tell // them to quit.{ OSStatus err; SetupPanelData * panelData; panelData = GetSetupPanelData(panel); err = noErr; if ( SetupPanelAnyControlPanelsRunning(panel) ) { assert(panelData->waitQuitTimer == NULL); err = SetupPanelAskUserWhetherToQuit(panel); if (err == noErr) { if (gSetupPanelWaitQuitTimerUPP == NULL) { gSetupPanelWaitQuitTimerUPP = NewEventLoopTimerUPP(SetupPanelWaitQuitTimerProc); } assert(gSetupPanelWaitQuitTimerUPP != NULL); err = InstallEventLoopTimer(GetCurrentEventLoop(), kEventDurationNoWait, kEventDurationSecond, gSetupPanelWaitQuitTimerUPP, panel, &panelData->waitQuitTimer); } if (err == noErr) { SetupPanelQuitAllControlPanels(panel); panelData->state = kSetupStateWaitingForQuit; SetupPanelSyncUIToState(panel); } } else { SetupPanelCreateNetworkConfig(panel, false); } QISADisplayError(panel, err, CFSTR("CreatingTheNetworkSettings"));}static void SetupPanelCommandStop(QISAPanel *panel) // Called when the Stop button is clicked. This never // happens in the current implementation because // SetupPanelCommandDoIt is entirely synchronous, but I've // left the code here just as an outline of how it will // eventually work.{ OSStatus err; SetupPanelData * panelData; panelData = GetSetupPanelData(panel); SetupPanelStopTimer(panelData); err = noErr; panelData->state = kSetupStateNotStarted; SetupPanelSyncUIToState(panel);}static const EventTypeSpec kSetupRootEvents[1] = { {kEventClassCommand, kEventCommandProcess} };static EventHandlerUPP gSetupRootEventHandlerUPP; // -> SetupRootEventHandlerenum { kHICommandInfo = 'INFO'};static pascal OSStatus SetupRootEventHandler(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData) // This routine is an HICommand handler that we install in our // panel's root control. It catches the Info, Do It and Stop // HICommands and dispatches them to their appropriate command // handlers.{ OSStatus err; HICommand command; QISAPanel * panel; #pragma unused(inHandlerCallRef) panel = (QISAPanel *) inUserData; assert( GetEventClass(inEvent) == kEventClassCommand ); assert( GetEventKind( inEvent) == kEventCommandProcess ); err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(command), NULL, &command); if (err == noErr) { switch (command.commandID) { case kHICommandInfo: SetupPanelCommandInfo(panel); break; case kHICommandOK: // start the connection process SetupPanelCommandDoIt(panel); break; case kHICommandCancel: SetupPanelCommandStop(panel); break; default: err = eventNotHandledErr; break; } } return err;}extern OSStatus SetupPanelInitialise(QISAPanel *panel) // See PanelInitialiseProc comments.{ OSStatus err; SetupPanelData * panelData; assert(panel->refCon == NULL); // Create and initialise panel private data. err = noErr; panelData = (SetupPanelData *) calloc( 1, sizeof(*panelData) ); if (panelData == NULL) { err = memFullErr; } if (err == noErr) { err = GetControlByIDQ(panel->window, 'STAT', 0, &panelData->statusText); } if (err == noErr) { err = GetControlByIDQ(panel->window, 'AROW', 0, &panelData->arrow); } if (err == noErr) { err = GetControlByIDQ(panel->window, 'DOIT', 0, &panelData->doItButton); } if (err == noErr) { err = GetControlByIDQ(panel->window, 'STOP', 0, &panelData->stopButton); } if (err == noErr) { err = GetControlByIDQ(panel->window, 'INFO', 0, &panelData->infoButton); } if (err == noErr) { err = QISAGetGlobalValue(panel, kQISAKeyControlPanels, (CFPropertyListRef *) &panelData->appsToGo); if (err == noErr) { CFRetain(panelData->appsToGo); if (CFGetTypeID(panelData->appsToGo) != CFArrayGetTypeID()) { err = kCFQDataErr; } } } // Install our HI command handler. if (err == noErr) { if (gSetupRootEventHandlerUPP == NULL) { gSetupRootEventHandlerUPP = NewEventHandlerUPP(SetupRootEventHandler); assert(gSetupRootEventHandlerUPP != NULL); } err = InstallControlEventHandler(panel->panelControl, gSetupRootEventHandlerUPP, GetEventTypeCount(kSetupRootEvents), kSetupRootEvents, panel, NULL); } // Clean up. panel->refCon = panelData; if (err == noErr) { panelData->magic = kSetupPanelMagic; panel->Terminate = SetupPanelTerminate; panelData->oldSwitchTo = panel->SwitchTo; panel->SwitchTo = SetupPanelSwitchTo; panelData->oldSwitchFrom = panel->SwitchFrom; panel->SwitchFrom = SetupPanelSwitchFrom; panelData->state = kSetupStateNotStarted; SetupPanelSyncUIToState(panel); } else { SetupPanelTerminate(panel); panel->refCon = NULL; } assert( (err == noErr) == (panel->refCon != NULL) ); return err;}</pre>
<!--googleoff: index -->
</td>
</tr>
</table>
<!-- END WIDE COLUMN -->
<!-- END MAIN CONTENT -->
<table width="680" border="0" cellpadding="0" cellspacing="0">
<tr>
<td><div style="width: 100%; height: 1px; background-color: #919699; margin-top: 5px; margin-bottom: 15px"></div></td>
</tr>
<tr>
<td align="center"><br/>
<table border="0" cellpadding="0" cellspacing="0" class="graybox">
<tr>
<th>Did this document help you?</th>
</tr>
<tr>
<td>
<div style="margin-bottom: 8px"><a href="http://developer.apple.com/feedback/?v=1&url=/samplecode/QISA/listing54.html%3Fid%3DDTS10000714-1.0&media=dvd" target=_new>Yes</a>: Tell us what works for you.</div>
<div style="margin-bottom: 8px"><a href="http://developer.apple.com/feedback/?v=2&url=/samplecode/QISA/listing54.html%3Fid%3DDTS10000714-1.0&media=dvd" target=_new>It’s good, but:</a> Report typos, inaccuracies, and so forth.</div>
<div><a href="http://developer.apple.com/feedback/?v=3&url=/samplecode/QISA/listing54.html%3Fid%3DDTS10000714-1.0&media=dvd" target=_new>It wasn’t helpful</a>: Tell us what would have helped.</div>
</td>
</tr>
</table>
</td>
</tr>
</table>
<!-- START BOTTOM APPLE NAVIGATION -->
<!--#include virtual="/includes/footer"-->
<!-- END BOTTOM APPLE NAVIGATION -->
<!-- START CENTER CLOSE -->
</center>
<!-- END CENTER CLOSE -->
</body>
</html>