Removed unuse builder from project
[phpeclipse.git] / net.sourceforge.phpeclipse.webbrowser / src / net / sourceforge / phpeclipse / webbrowser / internal / WebBrowser.java
1 /**
2  * Copyright (c) 2003 IBM Corporation and others. All rights reserved. � This
3  * program and the accompanying materials are made available under the terms of
4  * the Common Public License v1.0 which accompanies this distribution, and is
5  * available at http://www.eclipse.org/legal/cpl-v10.html �* Contributors: IBM -
6  * Initial API and implementation
7  */
8
9 // TODO 1. Handle the sizing of a popup running in shelled out secondary window.
10 // TODO 2. Support printing: waiting on eclipse bug 47937/44823.
11 package net.sourceforge.phpeclipse.webbrowser.internal;
12
13 import java.util.Iterator;
14
15 import net.sourceforge.phpeclipse.webbrowser.IURLMap;
16
17 import org.eclipse.swt.SWT;
18 import org.eclipse.swt.browser.Browser;
19 import org.eclipse.swt.browser.CloseWindowListener;
20 import org.eclipse.swt.browser.LocationEvent;
21 import org.eclipse.swt.browser.LocationListener;
22 import org.eclipse.swt.browser.OpenWindowListener;
23 import org.eclipse.swt.browser.ProgressEvent;
24 import org.eclipse.swt.browser.ProgressListener;
25 import org.eclipse.swt.browser.StatusTextEvent;
26 import org.eclipse.swt.browser.StatusTextListener;
27 import org.eclipse.swt.browser.TitleEvent;
28 import org.eclipse.swt.browser.TitleListener;
29 import org.eclipse.swt.browser.WindowEvent;
30 import org.eclipse.swt.dnd.Clipboard;
31 import org.eclipse.swt.events.SelectionAdapter;
32 import org.eclipse.swt.events.SelectionEvent;
33 import org.eclipse.swt.graphics.Point;
34 import org.eclipse.swt.graphics.Rectangle;
35 import org.eclipse.swt.layout.FillLayout;
36 import org.eclipse.swt.layout.GridData;
37 import org.eclipse.swt.layout.GridLayout;
38 import org.eclipse.swt.widgets.Combo;
39 import org.eclipse.swt.widgets.Composite;
40 import org.eclipse.swt.widgets.Control;
41 import org.eclipse.swt.widgets.Event;
42 import org.eclipse.swt.widgets.Label;
43 import org.eclipse.swt.widgets.Listener;
44 import org.eclipse.swt.widgets.Menu;
45 import org.eclipse.swt.widgets.MenuItem;
46 import org.eclipse.swt.widgets.ProgressBar;
47 import org.eclipse.swt.widgets.Shell;
48 import org.eclipse.swt.widgets.ToolBar;
49 import org.eclipse.swt.widgets.ToolItem;
50 import org.eclipse.ui.PlatformUI;
51
52 public class WebBrowser extends Composite {
53         protected Composite toolbarComp;
54
55         protected Composite statusComp;
56
57         protected Combo combo;
58
59         protected Clipboard clipboard;
60
61         protected boolean showToolbar;
62
63         protected ToolItem back;
64
65         protected ToolItem forward;
66
67         protected ToolItem stop;
68
69         protected ToolItem favorites;
70
71         protected ToolItem refresh;
72
73         protected BusyIndicator busy;
74
75         protected boolean showStatusbar;
76
77         protected ProgressBar progress;
78
79         protected Label status;
80
81         private static int MAX_HISTORY = 50;
82
83         protected static java.util.List history;
84
85         protected Browser browser;
86
87         protected Shell shell;
88
89         protected WebBrowserEditor editor;
90
91         protected String title;
92
93         public WebBrowser(Composite parent, final boolean showToolbar,
94                         final boolean showStatusbar) {
95                 super(parent, SWT.NONE);
96
97                 this.showToolbar = showToolbar;
98                 this.showStatusbar = showStatusbar;
99
100                 GridLayout layout = new GridLayout();
101                 layout.marginHeight = 3;
102                 layout.marginWidth = 3;
103                 layout.horizontalSpacing = 3;
104                 layout.verticalSpacing = 3;
105                 layout.numColumns = 1;
106                 setLayout(layout);
107                 setLayoutData(new GridData(GridData.FILL_BOTH));
108                 clipboard = new Clipboard(parent.getDisplay());
109                 PlatformUI.getWorkbench().getHelpSystem().setHelp(this,
110                                 ContextIds.WEB_BROWSER);
111
112                 if (showToolbar) {
113                         toolbarComp = new Composite(this, SWT.NONE);
114                         GridLayout outerLayout = new GridLayout();
115                         outerLayout.numColumns = 2;
116                         outerLayout.marginWidth = 0;
117                         outerLayout.marginHeight = 0;
118                         toolbarComp.setLayout(outerLayout);
119                         toolbarComp.setLayoutData(new GridData(
120                                         GridData.VERTICAL_ALIGN_BEGINNING
121                                                         | GridData.FILL_HORIZONTAL));
122
123                         // create the top line, with a combo box for history and a "go"
124                         // button
125                         Composite top = new Composite(toolbarComp, SWT.NONE);
126                         GridLayout topLayout = new GridLayout();
127                         topLayout.numColumns = 2;
128                         topLayout.marginWidth = 0;
129                         topLayout.marginHeight = 0;
130                         top.setLayout(topLayout);
131                         top.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_CENTER
132                                         | GridData.FILL_HORIZONTAL));
133
134                         combo = new Combo(top, SWT.DROP_DOWN);
135
136                         updateHistory();
137
138                         combo.addSelectionListener(new SelectionAdapter() {
139                                 public void widgetSelected(SelectionEvent we) {
140                                         try {
141                                                 if (combo.getSelectionIndex() != -1)
142                                                         setURL(combo.getItem(combo.getSelectionIndex()));
143                                         } catch (Exception e) {
144                                         }
145                                 }
146                         });
147                         combo.addListener(SWT.DefaultSelection, new Listener() {
148                                 public void handleEvent(Event e) {
149                                         setURL(combo.getText());
150                                 }
151                         });
152                         combo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
153                         PlatformUI.getWorkbench().getHelpSystem().setHelp(combo,
154                                         ContextIds.WEB_BROWSER_URL);
155
156                         ToolBar toolbar = new ToolBar(top, SWT.FLAT);
157                         fillToolBar(toolbar);
158
159                         new ToolItem(toolbar, SWT.SEPARATOR);
160
161                         busy = new BusyIndicator(toolbarComp, SWT.NONE);
162                         busy.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_END));
163                 }
164
165                 // create a new SWT Web browser widget, checking once again to make sure
166                 // we can use it in this environment
167                 // if (WebBrowserUtil.canUseInternalWebBrowser() &
168                 // WebBrowserUtil.isInternalBrowserOperational())
169                 if (WebBrowserUtil.isInternalBrowserOperational())
170                         this.browser = new Browser(this, SWT.NONE);
171                 else {
172                         WebBrowserUtil.openError(WebBrowserUIPlugin
173                                         .getResource("%errorCouldNotLaunchInternalWebBrowser"));
174                         return;
175                 }
176
177                 if (showToolbar) {
178                         back.setEnabled(browser.isBackEnabled());
179                         forward.setEnabled(browser.isForwardEnabled());
180                 }
181
182                 PlatformUI.getWorkbench().getHelpSystem().setHelp(browser,
183                                 ContextIds.WEB_BROWSER_WEB);
184                 GridData data = new GridData();
185                 data.horizontalAlignment = GridData.FILL;
186                 data.verticalAlignment = GridData.FILL;
187                 data.horizontalSpan = 3;
188                 data.grabExcessHorizontalSpace = true;
189                 data.grabExcessVerticalSpace = true;
190                 browser.setLayoutData(data);
191
192                 if (showStatusbar)
193                         createStatusArea(this);
194
195                 addBrowserListeners();
196         }
197
198         /**
199          * 
200          */
201         protected void addBrowserListeners() {
202                 if (showStatusbar) {
203                         // respond to Browser StatusTextEvents events by updating the status
204                         // Text label
205                         browser.addStatusTextListener(new StatusTextListener() {
206                                 public void changed(StatusTextEvent event) {
207                                         status.setText(event.text);
208                                 }
209                         });
210                 }
211
212                 /**
213                  * Add listener for new window creation so that we can instead of
214                  * opening a separate new window in which the session is lost, we can
215                  * instead open a new window in a new shell within the browser area
216                  * thereby maintaining the session.
217                  */
218                 browser.addOpenWindowListener(new OpenWindowListener() {
219                         public void open(WindowEvent event) {
220                                 Shell shell2 = new Shell(getDisplay());
221                                 shell2.setLayout(new FillLayout());
222                                 shell2.setText(WebBrowserUIPlugin
223                                                 .getResource("%viewWebBrowserTitle"));
224                                 shell2.setImage(getShell().getImage());
225                                 WebBrowser browser2 = new WebBrowser(shell2, showToolbar,
226                                                 showStatusbar);
227                                 browser2.shell = shell2;
228                                 event.browser = browser2.browser;
229                                 shell2.open();
230                         }
231                 });
232
233                 browser.addCloseWindowListener(new CloseWindowListener() {
234                         public void close(WindowEvent event) {
235                                 // if shell is not null, it must be a secondary popup window,
236                                 // else its an editor window
237                                 if (shell != null)
238                                         shell.dispose();
239                                 else
240                                         editor.closeEditor();
241                         }
242                 });
243
244                 browser.addProgressListener(new ProgressListener() {
245                         public void changed(ProgressEvent event) {
246                                 if (event.total == 0)
247                                         return;
248
249                                 boolean done = (event.current == event.total);
250
251                                 int percentProgress = event.current * 100 / event.total;
252                                 if (showStatusbar) {
253                                         if (done)
254                                                 progress.setSelection(0);
255                                         else
256                                                 progress.setSelection(percentProgress);
257                                 }
258
259                                 if (showToolbar) {
260                                         if (!busy.isBusy()
261                                                         && (percentProgress > 0 && percentProgress < 100)) {
262                                                 busy.setBusy(true);
263                                         }
264                                         // Once the progress hits 100 percent, done, set busy to
265                                         // false
266                                         else if (busy.isBusy() && done) {
267                                                 busy.setBusy(false);
268                                         }
269                                 }
270                         }
271
272                         public void completed(ProgressEvent event) {
273                                 if (showStatusbar)
274                                         progress.setSelection(0);
275                                 if (showToolbar) {
276                                         busy.setBusy(false);
277                                         back.setEnabled(browser.isBackEnabled());
278                                         forward.setEnabled(browser.isForwardEnabled());
279                                 }
280                         }
281                 });
282
283                 if (showToolbar) {
284                         browser.addLocationListener(new LocationListener() {
285                                 public void changed(LocationEvent event) {
286                                         if (!event.top)
287                                                 return;
288                                         if (!isHome()) {
289                                                 combo.setText(event.location);
290                                                 addToHistory(event.location);
291                                                 updateHistory();
292                                         } else
293                                                 combo.setText("");
294                                 }
295
296                                 public void changing(LocationEvent event) {
297                                 }
298                         });
299                 }
300
301                 browser.addTitleListener(new TitleListener() {
302                         public void changed(TitleEvent event) {
303                                 title = event.title;
304                         }
305                 });
306         }
307
308         /**
309          * Return the underlying browser control.
310          * 
311          * @return org.eclipse.swt.browser.Browser
312          */
313         public Browser getBrowser() {
314                 return browser;
315         }
316
317         /**
318          * 
319          */
320         protected void forward() {
321                 browser.forward();
322         }
323
324         /**
325          * 
326          */
327         protected void back() {
328                 browser.back();
329         }
330
331         /**
332          * 
333          */
334         protected void stop() {
335                 browser.stop();
336         }
337
338         /**
339          * 
340          */
341         protected void navigate(String url) {
342                 Trace.trace(Trace.FINER, "Navigate: " + url);
343                 if (url != null && url.equals(getURL())) {
344                         refresh();
345                         return;
346                 }
347                 browser.setUrl(url);
348         }
349
350         /**
351          * Refresh the currently viewed page.
352          */
353         public void refresh() {
354                 browser.refresh();
355         }
356
357         protected void setURL(String url, boolean browse) {
358                 Trace.trace(Trace.FINEST, "setURL: " + url + " " + browse);
359                 if (url == null) {
360                         home();
361                         return;
362                 }
363
364                 if (url.endsWith(WebBrowserPreference.getHomePageURL().substring(9)))
365                         return;
366
367                 // check URL maps
368                 Iterator iterator = WebBrowserUtil.getURLMaps().iterator();
369                 String newURL = null;
370                 while (iterator.hasNext() && newURL == null) {
371                         try {
372                                 IURLMap map = (IURLMap) iterator.next();
373                                 newURL = map.getMappedURL(url);
374                         } catch (Exception e) {
375                         }
376                 }
377                 if (newURL != null)
378                         url = newURL;
379
380                 if (browse)
381                         navigate(url);
382
383                 addToHistory(url);
384                 updateHistory();
385         }
386
387         protected void addToHistory(String url) {
388                 if (history == null)
389                         history = WebBrowserPreference.getInternalWebBrowserHistory();
390                 int found = -1;
391                 int size = history.size();
392                 for (int i = 0; i < size; i++) {
393                         String s = (String) history.get(i);
394                         if (s.equals(url)) {
395                                 found = i;
396                                 break;
397                         }
398                 }
399
400                 if (found == -1) {
401                         if (size >= MAX_HISTORY)
402                                 history.remove(size - 1);
403                         history.add(0, url);
404                         WebBrowserPreference.setInternalWebBrowserHistory(history);
405                 } else if (found != 0) {
406                         history.remove(found);
407                         history.add(0, url);
408                         WebBrowserPreference.setInternalWebBrowserHistory(history);
409                 }
410         }
411
412         public void setURL(String url) {
413                 setURL(url, true);
414         }
415
416         /**
417          * Creates the Web browser status area.
418          */
419         private void createStatusArea(Composite parent) {
420                 Composite composite = new Composite(parent, SWT.NONE);
421                 GridLayout layout = new GridLayout();
422                 layout.numColumns = 2;
423                 layout.marginHeight = 0;
424                 layout.marginWidth = 0;
425                 layout.horizontalSpacing = 4;
426                 layout.verticalSpacing = 0;
427                 composite.setLayout(layout);
428                 composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
429
430                 // Add a label for displaying status messages as they are received from
431                 // the control
432                 status = new Label(composite, SWT.SINGLE | SWT.READ_ONLY);
433                 GridData gridData = new GridData(GridData.FILL_HORIZONTAL
434                                 | GridData.VERTICAL_ALIGN_FILL);
435                 gridData.horizontalIndent = 2;
436                 status.setLayoutData(gridData);
437
438                 // Add a progress bar to display downloading progress information
439                 progress = new ProgressBar(composite, SWT.BORDER);
440                 gridData = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING
441                                 | GridData.VERTICAL_ALIGN_FILL);
442                 gridData.widthHint = 100;
443                 gridData.heightHint = 10;
444                 progress.setLayoutData(gridData);
445         }
446
447         /**
448          * 
449          */
450         public void dispose() {
451                 super.dispose();
452
453                 showStatusbar = false;
454                 showToolbar = false;
455
456                 if (busy != null)
457                         busy.dispose();
458                 busy = null;
459
460                 browser = null;
461         }
462
463         /**
464          * Populate the toolbar.
465          * 
466          * @param toolbar
467          *            org.eclipse.swt.widgets.ToolBar
468          */
469         private void fillToolBar(final ToolBar toolbar) {
470                 ToolItem go = new ToolItem(toolbar, SWT.NONE);
471                 go.setImage(ImageResource.getImage(ImageResource.IMG_ELCL_NAV_GO));
472                 go.setHotImage(ImageResource.getImage(ImageResource.IMG_CLCL_NAV_GO));
473                 go.setDisabledImage(ImageResource
474                                 .getImage(ImageResource.IMG_DLCL_NAV_GO));
475                 go
476                                 .setToolTipText(WebBrowserUIPlugin
477                                                 .getResource("%actionWebBrowserGo"));
478                 go.addSelectionListener(new SelectionAdapter() {
479                         public void widgetSelected(SelectionEvent event) {
480                                 setURL(combo.getText());
481                         }
482                 });
483
484                 new ToolItem(toolbar, SWT.SEPARATOR);
485
486                 favorites = new ToolItem(toolbar, SWT.DROP_DOWN);
487                 favorites.setImage(ImageResource
488                                 .getImage(ImageResource.IMG_ELCL_NAV_FAVORITES));
489                 favorites.setHotImage(ImageResource
490                                 .getImage(ImageResource.IMG_CLCL_NAV_FAVORITES));
491                 favorites.setDisabledImage(ImageResource
492                                 .getImage(ImageResource.IMG_DLCL_NAV_FAVORITES));
493                 favorites.setToolTipText(WebBrowserUIPlugin
494                                 .getResource("%actionWebBrowserFavorites"));
495
496                 favorites.addSelectionListener(new SelectionAdapter() {
497                         public void widgetSelected(SelectionEvent event) {
498                                 if (event.detail == SWT.ARROW) {
499                                         Rectangle r = favorites.getBounds();
500                                         showFavorites(toolbar, toolbar.toDisplay(r.x, r.y
501                                                         + r.height));
502                                 } else
503                                         addFavorite();
504                         }
505                 });
506
507                 // create back and forward actions
508                 back = new ToolItem(toolbar, SWT.NONE);
509                 back.setImage(ImageResource
510                                 .getImage(ImageResource.IMG_ELCL_NAV_BACKWARD));
511                 back.setHotImage(ImageResource
512                                 .getImage(ImageResource.IMG_CLCL_NAV_BACKWARD));
513                 back.setDisabledImage(ImageResource
514                                 .getImage(ImageResource.IMG_DLCL_NAV_BACKWARD));
515                 back.setToolTipText(WebBrowserUIPlugin
516                                 .getResource("%actionWebBrowserBack"));
517                 back.addSelectionListener(new SelectionAdapter() {
518                         public void widgetSelected(SelectionEvent event) {
519                                 back();
520                         }
521                 });
522
523                 forward = new ToolItem(toolbar, SWT.NONE);
524                 forward.setImage(ImageResource
525                                 .getImage(ImageResource.IMG_ELCL_NAV_FORWARD));
526                 forward.setHotImage(ImageResource
527                                 .getImage(ImageResource.IMG_CLCL_NAV_FORWARD));
528                 forward.setDisabledImage(ImageResource
529                                 .getImage(ImageResource.IMG_DLCL_NAV_FORWARD));
530                 forward.setToolTipText(WebBrowserUIPlugin
531                                 .getResource("%actionWebBrowserForward"));
532                 forward.addSelectionListener(new SelectionAdapter() {
533                         public void widgetSelected(SelectionEvent event) {
534                                 forward();
535                         }
536                 });
537
538                 // create refresh, stop, and print actions
539                 stop = new ToolItem(toolbar, SWT.NONE);
540                 stop.setImage(ImageResource.getImage(ImageResource.IMG_ELCL_NAV_STOP));
541                 stop.setHotImage(ImageResource
542                                 .getImage(ImageResource.IMG_CLCL_NAV_STOP));
543                 stop.setDisabledImage(ImageResource
544                                 .getImage(ImageResource.IMG_DLCL_NAV_STOP));
545                 stop.setToolTipText(WebBrowserUIPlugin
546                                 .getResource("%actionWebBrowserStop"));
547                 stop.addSelectionListener(new SelectionAdapter() {
548                         public void widgetSelected(SelectionEvent event) {
549                                 stop();
550                         }
551                 });
552
553                 refresh = new ToolItem(toolbar, SWT.NONE);
554                 refresh.setImage(ImageResource
555                                 .getImage(ImageResource.IMG_ELCL_NAV_REFRESH));
556                 refresh.setHotImage(ImageResource
557                                 .getImage(ImageResource.IMG_CLCL_NAV_REFRESH));
558                 refresh.setDisabledImage(ImageResource
559                                 .getImage(ImageResource.IMG_DLCL_NAV_REFRESH));
560                 refresh.setToolTipText(WebBrowserUIPlugin
561                                 .getResource("%actionWebBrowserRefresh"));
562                 refresh.addSelectionListener(new SelectionAdapter() {
563                         public void widgetSelected(SelectionEvent event) {
564                                 refresh();
565                         }
566                 });
567         }
568
569         protected void addFavorite() {
570                 java.util.List list = WebBrowserPreference
571                                 .getInternalWebBrowserFavorites();
572                 Favorite f = new Favorite(title, browser.getUrl());
573                 if (!list.contains(f)) {
574                         list.add(f);
575                         WebBrowserPreference.setInternalWebBrowserFavorites(list);
576                 }
577         }
578
579         protected void showFavorites(Control parent, Point p) {
580                 Menu perspectiveBarMenu = null;
581                 if (perspectiveBarMenu == null) {
582                         Menu menu = new Menu(parent);
583
584                         // locked favorites
585                         Iterator iterator = WebBrowserUtil.getLockedFavorites().iterator();
586                         if (iterator.hasNext()) {
587                                 while (iterator.hasNext()) {
588                                         final Favorite f = (Favorite) iterator.next();
589                                         MenuItem item = new MenuItem(menu, SWT.NONE);
590                                         item.setText(f.getName());
591                                         item.setImage(ImageResource
592                                                         .getImage(ImageResource.IMG_FAVORITE));
593                                         item.addSelectionListener(new SelectionAdapter() {
594                                                 public void widgetSelected(SelectionEvent event) {
595                                                         setURL(f.getURL());
596                                                 }
597                                         });
598                                 }
599
600                                 new MenuItem(menu, SWT.SEPARATOR);
601                         }
602
603                         iterator = WebBrowserPreference.getInternalWebBrowserFavorites()
604                                         .iterator();
605                         if (!iterator.hasNext()) {
606                                 MenuItem item = new MenuItem(menu, SWT.NONE);
607                                 item.setText(WebBrowserUIPlugin
608                                                 .getResource("%actionWebBrowserNoFavorites"));
609                         }
610                         while (iterator.hasNext()) {
611                                 final Favorite f = (Favorite) iterator.next();
612                                 MenuItem item = new MenuItem(menu, SWT.NONE);
613                                 item.setText(f.getName());
614                                 item.setImage(ImageResource
615                                                 .getImage(ImageResource.IMG_FAVORITE));
616                                 item.addSelectionListener(new SelectionAdapter() {
617                                         public void widgetSelected(SelectionEvent event) {
618                                                 setURL(f.getURL());
619                                         }
620                                 });
621                         }
622
623                         new MenuItem(menu, SWT.SEPARATOR);
624
625                         MenuItem item = new MenuItem(menu, SWT.NONE);
626                         item.setText(WebBrowserUIPlugin
627                                         .getResource("%actionWebBrowserOrganizeFavorites"));
628                         item.addSelectionListener(new SelectionAdapter() {
629                                 public void widgetSelected(SelectionEvent event) {
630                                         OrganizeFavoritesDialog dialog = new OrganizeFavoritesDialog(
631                                                         shell);
632                                         dialog.open();
633                                 }
634                         });
635
636                         perspectiveBarMenu = menu;
637                 }
638
639                 if (perspectiveBarMenu != null) {
640                         perspectiveBarMenu.setLocation(p.x, p.y);
641                         perspectiveBarMenu.setVisible(true);
642                 }
643         }
644
645         public void home() {
646                 navigate(WebBrowserPreference.getHomePageURL());
647         }
648
649         /**
650          * Returns true if the homepage is currently being displayed.
651          * 
652          * @return boolean
653          */
654         protected boolean isHome() {
655                 return getURL() != null
656                                 && getURL().endsWith(
657                                                 WebBrowserPreference.getHomePageURL().substring(9));
658         }
659
660         protected String getURL() {
661                 return browser.getUrl();
662         }
663
664         /**
665          * Update the history list to the global copy.
666          */
667         protected void updateHistory() {
668                 if (combo == null)
669                         return;
670
671                 String temp = combo.getText();
672                 if (history == null)
673                         history = WebBrowserPreference.getInternalWebBrowserHistory();
674
675                 String[] historyList = new String[history.size()];
676                 history.toArray(historyList);
677                 combo.setItems(historyList);
678
679                 combo.setText(temp);
680         }
681
682         public void addProgressListener(ProgressListener listener) {
683                 browser.addProgressListener(listener);
684         }
685
686         public void addStatusTextListener(StatusTextListener listener) {
687                 browser.addStatusTextListener(listener);
688         }
689
690         public void addTitleListener(TitleListener listener) {
691                 browser.addTitleListener(listener);
692         }
693 }