1 /* 2 Open Tracker License 3 4 Terms and Conditions 5 6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy of 9 this software and associated documentation files (the "Software"), to deal in 10 the Software without restriction, including without limitation the rights to 11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 of the Software, and to permit persons to whom the Software is furnished to do 13 so, subject to the following conditions: 14 15 The above copyright notice and this permission notice applies to all licensees 16 and shall be included in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25 Except as contained in this notice, the name of Be Incorporated shall not be 26 used in advertising or otherwise to promote the sale, use or other dealings in 27 this Software without prior written authorization from Be Incorporated. 28 29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks 30 of Be Incorporated in the United States and other countries. Other brand product 31 names are registered trademarks or trademarks of their respective holders. 32 All rights reserved. 33 */ 34 35 #include <Debug.h> 36 #include <stdlib.h> 37 #include <string.h> 38 39 #include <AppFileInfo.h> 40 #include <Autolock.h> 41 #include <Bitmap.h> 42 #include <Directory.h> 43 #include <Dragger.h> 44 #include <File.h> 45 #include <FindDirectory.h> 46 #include <Mime.h> 47 #include <Path.h> 48 #include <Roster.h> 49 #include <RosterPrivate.h> 50 51 #include "icons.h" 52 #include "tracker_private.h" 53 #include "BarApp.h" 54 #include "BarView.h" 55 #include "BarWindow.h" 56 #include "DeskBarUtils.h" 57 #include "FSUtils.h" 58 #include "PublicCommands.h" 59 #include "ResourceSet.h" 60 #include "Switcher.h" 61 #include "TeamMenu.h" 62 #include "WindowMenuItem.h" 63 64 65 BLocker TBarApp::sSubscriberLock; 66 BList TBarApp::sBarTeamInfoList; 67 BList TBarApp::sSubscribers; 68 69 70 const uint32 kShowBeMenu = 'BeMn'; 71 const uint32 kShowTeamMenu = 'TmMn'; 72 73 const BRect kIconSize(0.0f, 0.0f, 15.0f, 15.0f); 74 75 static const color_space kIconFormat = B_RGBA32; 76 77 78 int 79 main() 80 { 81 TBarApp app; 82 app.Run(); 83 84 return B_OK; 85 } 86 87 88 TBarApp::TBarApp() 89 : BApplication(kDeskbarSignature), 90 fSettingsFile(NULL), 91 fPreferencesWindow(NULL) 92 { 93 InitSettings(); 94 InitIconPreloader(); 95 96 be_roster->StartWatching(this); 97 98 sBarTeamInfoList.MakeEmpty(); 99 100 BList teamList; 101 int32 numTeams; 102 be_roster->GetAppList(&teamList); 103 numTeams = teamList.CountItems(); 104 for (int32 i = 0; i < numTeams; i++) { 105 app_info appInfo; 106 team_id tID = (team_id)teamList.ItemAt(i); 107 if (be_roster->GetRunningAppInfo(tID, &appInfo) == B_OK) { 108 AddTeam(appInfo.team, appInfo.flags, appInfo.signature, 109 &appInfo.ref); 110 } 111 } 112 113 sSubscribers.MakeEmpty(); 114 115 fSwitcherMessenger = BMessenger(new TSwitchManager(fSettings.switcherLoc)); 116 117 fBarWindow = new TBarWindow(); 118 fBarWindow->Show(); 119 120 // this messenger now targets the barview instead of the 121 // statusview so that all additions to the tray 122 // follow the same path 123 fStatusViewMessenger = BMessenger(fBarWindow->FindView("BarView")); 124 } 125 126 127 TBarApp::~TBarApp() 128 { 129 be_roster->StopWatching(this); 130 131 int32 teamCount = sBarTeamInfoList.CountItems(); 132 for (int32 i = 0; i < teamCount; i++) { 133 BarTeamInfo* barInfo = (BarTeamInfo*)sBarTeamInfoList.ItemAt(i); 134 delete barInfo; 135 } 136 137 int32 subsCount = sSubscribers.CountItems(); 138 for (int32 i = 0; i < subsCount; i++) { 139 BMessenger* messenger 140 = static_cast<BMessenger*>(sSubscribers.ItemAt(i)); 141 delete messenger; 142 } 143 SaveSettings(); 144 delete fSettingsFile; 145 } 146 147 148 bool 149 TBarApp::QuitRequested() 150 { 151 // don't allow user quitting 152 if (CurrentMessage() && CurrentMessage()->FindBool("shortcut")) { 153 // but allow quitting to hide fPreferencesWindow 154 int32 index = 0; 155 BWindow* window = NULL; 156 while ((window = WindowAt(index++)) != NULL) { 157 if (window == fPreferencesWindow) { 158 if (fPreferencesWindow->Lock()) { 159 if (fPreferencesWindow->IsActive()) 160 fPreferencesWindow->PostMessage(B_QUIT_REQUESTED); 161 fPreferencesWindow->Unlock(); 162 } 163 break; 164 } 165 } 166 return false; 167 } 168 169 // system quitting, call inherited to notify all loopers 170 fBarWindow->SaveSettings(); 171 BApplication::QuitRequested(); 172 return true; 173 } 174 175 176 void 177 TBarApp::SaveSettings() 178 { 179 if (fSettingsFile->InitCheck() == B_OK) { 180 fSettingsFile->Seek(0, SEEK_SET); 181 fSettingsFile->Write(&fSettings.vertical, sizeof(bool)); 182 fSettingsFile->Write(&fSettings.left, sizeof(bool)); 183 fSettingsFile->Write(&fSettings.top, sizeof(bool)); 184 fSettingsFile->Write(&fSettings.ampmMode, sizeof(bool)); 185 fSettingsFile->Write(&fSettings.state, sizeof(uint32)); 186 fSettingsFile->Write(&fSettings.width, sizeof(float)); 187 fSettingsFile->Write(&fSettings.showTime, sizeof(bool)); 188 fSettingsFile->Write(&fSettings.switcherLoc, sizeof(BPoint)); 189 fSettingsFile->Write(&fSettings.recentAppsCount, sizeof(int32)); 190 fSettingsFile->Write(&fSettings.recentDocsCount, sizeof(int32)); 191 fSettingsFile->Write(&fSettings.timeShowSeconds, sizeof(bool)); 192 fSettingsFile->Write(&fSettings.timeShowMil, sizeof(bool)); 193 fSettingsFile->Write(&fSettings.recentFoldersCount, sizeof(int32)); 194 fSettingsFile->Write(&fSettings.timeShowEuro, sizeof(bool)); 195 fSettingsFile->Write(&fSettings.alwaysOnTop, sizeof(bool)); 196 fSettingsFile->Write(&fSettings.timeFullDate, sizeof(bool)); 197 fSettingsFile->Write(&fSettings.trackerAlwaysFirst, sizeof(bool)); 198 fSettingsFile->Write(&fSettings.sortRunningApps, sizeof(bool)); 199 fSettingsFile->Write(&fSettings.superExpando, sizeof(bool)); 200 fSettingsFile->Write(&fSettings.expandNewTeams, sizeof(bool)); 201 fSettingsFile->Write(&fSettings.autoRaise, sizeof(bool)); 202 } 203 } 204 205 206 void 207 TBarApp::InitSettings() 208 { 209 desk_settings settings; 210 settings.vertical = true; 211 settings.left = false; 212 settings.top = true; 213 settings.ampmMode = true; 214 settings.showTime = true; 215 settings.state = kExpandoState; 216 settings.width = 0; 217 settings.switcherLoc = BPoint(5000, 5000); 218 settings.recentAppsCount = 10; 219 settings.recentDocsCount = 10; 220 settings.timeShowSeconds = false; 221 settings.timeShowMil = false; 222 settings.recentFoldersCount = 10; 223 settings.timeShowEuro = false; 224 settings.alwaysOnTop = false; 225 settings.timeFullDate = false; 226 settings.trackerAlwaysFirst = false; 227 settings.sortRunningApps = false; 228 settings.superExpando = false; 229 settings.expandNewTeams = false; 230 settings.autoRaise = false; 231 232 BPath dirPath; 233 const char* settingsFileName = "Deskbar_settings"; 234 235 find_directory(B_USER_DESKBAR_DIRECTORY, &dirPath, true); 236 // just make it 237 238 if (find_directory (B_USER_SETTINGS_DIRECTORY, &dirPath, true) == B_OK) { 239 BPath filePath = dirPath; 240 filePath.Append(settingsFileName); 241 fSettingsFile = new BFile(filePath.Path(), O_RDWR); 242 if (fSettingsFile->InitCheck() != B_OK) { 243 BDirectory theDir(dirPath.Path()); 244 if (theDir.InitCheck() == B_OK) 245 theDir.CreateFile(settingsFileName, fSettingsFile); 246 } 247 248 if (fSettingsFile->InitCheck() == B_OK) { 249 off_t size = 0; 250 fSettingsFile->GetSize(&size); 251 252 if (size >= kValidSettingsSize1) { 253 fSettingsFile->Seek(0, SEEK_SET); 254 fSettingsFile->Read(&settings.vertical, sizeof(bool)); 255 fSettingsFile->Read(&settings.left, sizeof(bool)); 256 fSettingsFile->Read(&settings.top, sizeof(bool)); 257 fSettingsFile->Read(&settings.ampmMode, sizeof(bool)); 258 fSettingsFile->Read(&settings.state, sizeof(uint32)); 259 fSettingsFile->Read(&settings.width, sizeof(float)); 260 fSettingsFile->Read(&settings.showTime, sizeof(bool)); 261 } 262 if (size >= kValidSettingsSize2) 263 fSettingsFile->Read(&settings.switcherLoc, sizeof(BPoint)); 264 if (size >= kValidSettingsSize3) { 265 fSettingsFile->Read(&settings.recentAppsCount, sizeof(int32)); 266 fSettingsFile->Read(&settings.recentDocsCount, sizeof(int32)); 267 } 268 if (size >= kValidSettingsSize4) { 269 fSettingsFile->Read(&settings.timeShowSeconds, sizeof(bool)); 270 fSettingsFile->Read(&settings.timeShowMil, sizeof(bool)); 271 } 272 if (size >= kValidSettingsSize5) 273 fSettingsFile->Read(&settings.recentFoldersCount, 274 sizeof(int32)); 275 if (size >= kValidSettingsSize6) { 276 fSettingsFile->Read(&settings.timeShowEuro, sizeof(bool)); 277 fSettingsFile->Read(&settings.alwaysOnTop, sizeof(bool)); 278 } 279 if (size >= kValidSettingsSize7) 280 fSettingsFile->Read(&settings.timeFullDate, sizeof(bool)); 281 if (size >= kValidSettingsSize8) { 282 fSettingsFile->Read(&settings.trackerAlwaysFirst, sizeof(bool)); 283 fSettingsFile->Read(&settings.sortRunningApps, sizeof(bool)); 284 } 285 if (size >= kValidSettingsSize9) { 286 fSettingsFile->Read(&settings.superExpando, sizeof(bool)); 287 fSettingsFile->Read(&settings.expandNewTeams, sizeof(bool)); 288 } 289 if (size >= kValidSettingsSize10) 290 fSettingsFile->Read(&settings.autoRaise, sizeof(bool)); 291 } 292 } 293 294 fSettings = settings; 295 } 296 297 298 void 299 TBarApp::MessageReceived(BMessage* message) 300 { 301 int32 count; 302 switch (message->what) { 303 case 'gloc': 304 case 'sloc': 305 case 'gexp': 306 case 'sexp': 307 case 'info': 308 case 'exst': 309 case 'cwnt': 310 case 'icon': 311 case 'remv': 312 case 'adon': 313 // pass any BDeskbar originating messages on to the window 314 fBarWindow->PostMessage(message); 315 break; 316 317 case kConfigShow: 318 ShowPreferencesWindow(); 319 break; 320 321 case kShowBeMenu: 322 if (fBarWindow->Lock()) { 323 fBarWindow->ShowBeMenu(); 324 fBarWindow->Unlock(); 325 } 326 break; 327 328 case kShowTeamMenu: 329 if (fBarWindow->Lock()) { 330 fBarWindow->ShowTeamMenu(); 331 fBarWindow->Unlock(); 332 } 333 break; 334 335 case kUpdateRecentCounts: 336 if (message->FindInt32("applications", &count) == B_OK) 337 fSettings.recentAppsCount = count; 338 if (message->FindInt32("folders", &count) == B_OK) 339 fSettings.recentFoldersCount = count; 340 if (message->FindInt32("documents", &count) == B_OK) 341 fSettings.recentDocsCount = count; 342 break; 343 344 case kConfigClose: 345 fPreferencesWindow = NULL; 346 break; 347 348 case B_SOME_APP_LAUNCHED: 349 { 350 team_id team = -1; 351 message->FindInt32("be:team", &team); 352 353 uint32 flags = 0; 354 message->FindInt32("be:flags", (long*)&flags); 355 356 const char* sig = NULL; 357 message->FindString("be:signature", &sig); 358 359 entry_ref ref; 360 message->FindRef("be:ref", &ref); 361 362 AddTeam(team, flags, sig, &ref); 363 break; 364 } 365 366 case B_SOME_APP_QUIT: 367 { 368 team_id team = -1; 369 message->FindInt32("be:team", &team); 370 RemoveTeam(team); 371 break; 372 } 373 374 case B_ARCHIVED_OBJECT: 375 // TODO: what's this??? 376 message->AddString("special", "Alex Osadzinski"); 377 fStatusViewMessenger.SendMessage(message); 378 break; 379 380 case kToggleDraggers: 381 if (BDragger::AreDraggersDrawn()) 382 BDragger::HideAllDraggers(); 383 else 384 BDragger::ShowAllDraggers(); 385 break; 386 387 case kAlwaysTop: 388 fSettings.alwaysOnTop = !fSettings.alwaysOnTop; 389 390 fBarWindow->SetFeel(fSettings.alwaysOnTop ? 391 B_FLOATING_ALL_WINDOW_FEEL : B_NORMAL_WINDOW_FEEL); 392 break; 393 394 case kAutoRaise: 395 { 396 fSettings.autoRaise = !fSettings.autoRaise; 397 398 TBarView* barView = static_cast<TBarApp*>(be_app)->BarView(); 399 fBarWindow->Lock(); 400 barView->UpdateAutoRaise(); 401 fBarWindow->Unlock(); 402 break; 403 } 404 405 case kTrackerFirst: 406 { 407 fSettings.trackerAlwaysFirst = !fSettings.trackerAlwaysFirst; 408 409 TBarView* barView = static_cast<TBarApp*>(be_app)->BarView(); 410 fBarWindow->Lock(); 411 barView->UpdatePlacement(); 412 fBarWindow->Unlock(); 413 break; 414 } 415 416 case kSortRunningApps: 417 { 418 fSettings.sortRunningApps = !fSettings.sortRunningApps; 419 420 TBarView* barView = static_cast<TBarApp*>(be_app)->BarView(); 421 fBarWindow->Lock(); 422 barView->UpdatePlacement(); 423 fBarWindow->Unlock(); 424 break; 425 } 426 427 case kUnsubscribe: 428 { 429 BMessenger messenger; 430 if (message->FindMessenger("messenger", &messenger) == B_OK) 431 Unsubscribe(messenger); 432 break; 433 } 434 435 case kSuperExpando: 436 { 437 fSettings.superExpando = !fSettings.superExpando; 438 439 TBarView* barView = static_cast<TBarApp*>(be_app)->BarView(); 440 fBarWindow->Lock(); 441 barView->UpdatePlacement(); 442 fBarWindow->Unlock(); 443 break; 444 } 445 446 case kExpandNewTeams: 447 { 448 fSettings.expandNewTeams = !fSettings.expandNewTeams; 449 450 TBarView* barView = static_cast<TBarApp*>(be_app)->BarView(); 451 fBarWindow->Lock(); 452 barView->UpdatePlacement(); 453 fBarWindow->Unlock(); 454 break; 455 } 456 457 case 'TASK': 458 fSwitcherMessenger.SendMessage(message); 459 break; 460 461 case kSuspendSystem: 462 // TODO: Call BRoster? 463 break; 464 465 case kRebootSystem: 466 case kShutdownSystem: 467 { 468 bool reboot = (message->what == kRebootSystem); 469 bool confirm; 470 message->FindBool("confirm", &confirm); 471 472 BRoster roster; 473 BRoster::Private rosterPrivate(roster); 474 status_t error = rosterPrivate.ShutDown(reboot, confirm, false); 475 if (error != B_OK) 476 fprintf(stderr, "Shutdown failed: %s\n", strerror(error)); 477 478 break; 479 } 480 481 case kShowSplash: 482 run_be_about(); 483 break; 484 485 default: 486 BApplication::MessageReceived(message); 487 break; 488 } 489 } 490 491 492 void 493 TBarApp::RefsReceived(BMessage* refs) 494 { 495 entry_ref ref; 496 for (int32 i = 0; refs->FindRef("refs", i, &ref) == B_OK; i++) { 497 BMessage refsReceived(B_REFS_RECEIVED); 498 refsReceived.AddRef("refs", &ref); 499 500 BEntry entry(&ref); 501 if (!entry.IsDirectory()) 502 TrackerLaunch(&refsReceived, true); 503 } 504 } 505 506 507 void 508 TBarApp::Subscribe(const BMessenger &subscriber, BList* list) 509 { 510 // called when ExpandoMenuBar, TeamMenu or Switcher are built/rebuilt 511 list->MakeEmpty(); 512 513 BAutolock autolock(sSubscriberLock); 514 if (!autolock.IsLocked()) 515 return; 516 517 int32 numTeams = sBarTeamInfoList.CountItems(); 518 for (int32 i = 0; i < numTeams; i++) { 519 BarTeamInfo* barInfo = (BarTeamInfo*)sBarTeamInfoList.ItemAt(i); 520 BarTeamInfo* newBarInfo = new (std::nothrow) BarTeamInfo(*barInfo); 521 if (newBarInfo != NULL) 522 list->AddItem(newBarInfo); 523 } 524 525 int32 subsCount = sSubscribers.CountItems(); 526 for (int32 i = 0; i < subsCount; i++) { 527 BMessenger* messenger = (BMessenger*)sSubscribers.ItemAt(i); 528 if (*messenger == subscriber) 529 return; 530 } 531 532 sSubscribers.AddItem(new BMessenger(subscriber)); 533 } 534 535 536 void 537 TBarApp::Unsubscribe(const BMessenger &subscriber) 538 { 539 BAutolock autolock(sSubscriberLock); 540 if (!autolock.IsLocked()) 541 return; 542 543 int32 count = sSubscribers.CountItems(); 544 for (int32 i = 0; i < count; i++) { 545 BMessenger* messenger = (BMessenger*)sSubscribers.ItemAt(i); 546 if (*messenger == subscriber) { 547 sSubscribers.RemoveItem(i); 548 delete messenger; 549 break; 550 } 551 } 552 } 553 554 555 void 556 TBarApp::AddTeam(team_id team, uint32 flags, const char* sig, entry_ref* ref) 557 { 558 BAutolock autolock(sSubscriberLock); 559 if (!autolock.IsLocked()) 560 return; 561 562 // have we already seen this team, is this another instance of 563 // a known app? 564 BarTeamInfo* multiLaunchTeam = NULL; 565 int32 teamCount = sBarTeamInfoList.CountItems(); 566 for (int32 i = 0; i < teamCount; i++) { 567 BarTeamInfo* barInfo = (BarTeamInfo*)sBarTeamInfoList.ItemAt(i); 568 if (barInfo->teams->HasItem((void*)team)) 569 return; 570 if (strcasecmp(barInfo->sig, sig) == 0) 571 multiLaunchTeam = barInfo; 572 } 573 574 if (multiLaunchTeam != NULL) { 575 multiLaunchTeam->teams->AddItem((void*)team); 576 577 int32 subsCount = sSubscribers.CountItems(); 578 if (subsCount > 0) { 579 BMessage message(kAddTeam); 580 message.AddInt32("team", team); 581 message.AddString("sig", multiLaunchTeam->sig); 582 583 for (int32 i = 0; i < subsCount; i++) 584 ((BMessenger*)sSubscribers.ItemAt(i))->SendMessage(&message); 585 } 586 return; 587 } 588 589 BFile file(ref, B_READ_ONLY); 590 BAppFileInfo appMime(&file); 591 592 BarTeamInfo* barInfo = new BarTeamInfo(new BList(), flags, strdup(sig), 593 new BBitmap(kIconSize, kIconFormat), strdup(ref->name)); 594 595 barInfo->teams->AddItem((void*)team); 596 if (appMime.GetIcon(barInfo->icon, B_MINI_ICON) != B_OK) 597 appMime.GetTrackerIcon(barInfo->icon, B_MINI_ICON); 598 599 sBarTeamInfoList.AddItem(barInfo); 600 601 int32 subsCount = sSubscribers.CountItems(); 602 if (subsCount > 0) { 603 for (int32 i = 0; i < subsCount; i++) { 604 BMessenger* messenger = (BMessenger*)sSubscribers.ItemAt(i); 605 BMessage message(B_SOME_APP_LAUNCHED); 606 607 BList* tList = new BList(*(barInfo->teams)); 608 message.AddPointer("teams", tList); 609 610 BBitmap* icon = new BBitmap(barInfo->icon); 611 ASSERT(icon); 612 613 message.AddPointer("icon", icon); 614 615 message.AddInt32("flags", static_cast<int32>(barInfo->flags)); 616 message.AddString("name", barInfo->name); 617 message.AddString("sig", barInfo->sig); 618 619 messenger->SendMessage(&message); 620 } 621 } 622 } 623 624 625 void 626 TBarApp::RemoveTeam(team_id team) 627 { 628 BAutolock autolock(sSubscriberLock); 629 if (!autolock.IsLocked()) 630 return; 631 632 int32 teamCount = sBarTeamInfoList.CountItems(); 633 for (int32 i = 0; i < teamCount; i++) { 634 BarTeamInfo* barInfo = (BarTeamInfo*)sBarTeamInfoList.ItemAt(i); 635 if (barInfo->teams->HasItem((void*)team)) { 636 int32 subsCount = sSubscribers.CountItems(); 637 if (subsCount > 0) { 638 BMessage message((barInfo->teams->CountItems() == 1) ? 639 B_SOME_APP_QUIT : kRemoveTeam); 640 641 message.AddInt32("team", team); 642 for (int32 i = 0; i < subsCount; i++) { 643 BMessenger* messenger = (BMessenger*)sSubscribers.ItemAt(i); 644 messenger->SendMessage(&message); 645 } 646 } 647 648 barInfo->teams->RemoveItem((void*)team); 649 if (barInfo->teams->CountItems() < 1) { 650 delete (BarTeamInfo*)sBarTeamInfoList.RemoveItem(i); 651 return; 652 } 653 } 654 } 655 } 656 657 658 void 659 TBarApp::ShowPreferencesWindow() 660 { 661 if (fPreferencesWindow) 662 fPreferencesWindow->Activate(); 663 else { 664 fPreferencesWindow = new PreferencesWindow(BRect(0, 0, 320, 240)); 665 fPreferencesWindow->Show(); 666 } 667 } 668 669 670 // #pragma mark - 671 672 673 BarTeamInfo::BarTeamInfo(BList* teams, uint32 flags, char* sig, BBitmap* icon, 674 char* name) 675 : teams(teams), 676 flags(flags), 677 sig(sig), 678 icon(icon), 679 name(name) 680 { 681 } 682 683 684 BarTeamInfo::BarTeamInfo(const BarTeamInfo &info) 685 : teams(new BList(*info.teams)), 686 flags(info.flags), 687 sig(strdup(info.sig)), 688 icon(new BBitmap(*info.icon)), 689 name(strdup(info.name)) 690 { 691 } 692 693 694 BarTeamInfo::~BarTeamInfo() 695 { 696 delete teams; 697 free(sig); 698 delete icon; 699 free(name); 700 } 701 702