CHAPTER

36   Introducing JavaFX Menus

Menus are an important part of many GUIs because they give the user access to a program’s core functionally. Furthermore, the proper implementation of an application’s menus is a necessary part of creating a successful GUI. Because of the key role they play in many applications, JavaFX provides extensive support for menus. Fortunately, JavaFX’s approach to menus is both powerful and streamlined.

As you will see throughout the course of this chapter, JavaFX menus have several parallels with Swing menus, which were described in Chapter 33. As a result, if you already know how to create Swing menus, learning how to create menus in JavaFX is easy. That said, there are also several differences, so it is important not to jump to conclusions about the JavaFX menu system.

The JavaFX menu system supports several key elements, including

•   The menu bar, which is the main menu for an application.

•   The standard menu, which can contain either items to be selected or other menus (submenus).

•   The context menu, which is often activated by right-clicking the mouse. Context menus are also called popup menus.

JavaFX menus also support accelerator keys, which enable menu items to be selected without having to activate the menu, and mnemonics, which allow a menu item to be selected by the keyboard once the menu options are displayed. In addition to “normal” menus, JavaFX also supports the toolbar, which provides rapid access to program functionality, often paralleling menu items.

Menu Basics

The JavaFX menu system is supported by a group of related classes packaged in javafx.scene.control. The ones used in this chapter are shown in Table 36-1, and they represent the core of the menu system. Although JavaFX allows a high degree of customization if desired, normally you will simply use the menu classes as-is because their default look and feel is generally what you will want.

Images

Table 36-1   The Core JavaFX Menu Classes

Here is brief overview of how the classes fit together. To create a main menu for an application, you first need an instance of MenuBar. This class is, loosely speaking, a container for menus. To the MenuBar you add instances of Menu. Each Menu object defines a menu. That is, each Menu object contains one or more selectable items. The items displayed by a Menu are objects of type MenuItem. Thus, a MenuItem defines a selection that can be chosen by the user.

In addition to “standard” menu items, you can also include check and radio menu items in a menu. Their operation parallels check box and radio button controls. A check menu item is created by CheckMenuItem. A radio menu item is created by RadioMenuItem. Both of these classes extend MenuItem.

SeparatorMenuItem is a convenience class that creates a separator line in a menu. It inherits CustomMenuItem, which is a class that facilitates embedding other types of controls in a menu item. CustomMenuItem extends MenuItem.

One key point about JavaFX menus is that MenuItem does not inherit Node. Thus, instances of MenuItem can only be used in a menu. They cannot be otherwise incorporated into a scene graph. However, MenuBar does inherit Node, which does allow the menu bar to be added to the scene graph.

Another key point is that MenuItem is a superclass of Menu. This allows the creation of submenus, which are, essentially, menus within menus. To create a submenu, you first create and populate a Menu object with MenuItems and then add it to another Menu object. You will see this process in action in the examples that follow.

When a menu item is selected, an action event is generated. The text associated with the selection will be the name of the selection. Thus, when using one action event handler to process all menu selections, one way you can determine which item was selected is by examining the name. Of course, you can also use separate anonymous inner classes or lambda expressions to handle each menu item’s action events. In this case, the menu selection is already known and there is no need to examine the name to determine which item was selected.

As an alternative or adjunct to menus that descend from the menu bar, you can also create stand-alone, context menus, which pop up when activated. To create a context menu, first create an object of type ContextMenu. Then, add MenuItems to it. A context menu is often activated by clicking the right mouse button when the mouse is over a control for which a context menu has been defined. It is important to point out that ContextMenu is not derived from MenuItem. Rather, it inherits PopupControl.

A feature related to the menu is the toolbar. In JavaFX, toolbars are supported by the ToolBar class. It creates a stand-alone component that is often used to provide fast access to functionality contained within the menus of the application. For example, a toolbar might provide fast access to the formatting commands supported by a word processor.

An Overview of MenuBar, Menu, and MenuItem

Before you can create a menu, you need to know some specifics about MenuBar, Menu, and MenuItem. These form the minimum set of classes needed to construct a main menu for an application. MenuItems are also used by context (i.e., popup) menus. Thus, these classes form the foundation of the menu system.

MenuBar

MenuBar is essentially a container for menus. It is the control that supplies the main menu of an application. Like all JavaFX controls, it inherits Node. Thus, it can be added to a scene graph. MenuBar has two constructors. The first is the default constructor. With this constructor, initially, the menu bar will be empty, and you will need to populate it with menus prior to use. The second constructor lets you specify the initial list of menus. As a general rule, an application has one and only one menu bar.

MenuBar defines several methods, but often you will use only one: getMenus( ). It returns a list of the menus managed by the menu bar. It is to this list that you will add the menus that you create. The getMenus( ) method is shown here:

final ObservableList<Menu> getMenus( )

A Menu instance is added to this list of menus by calling add( ). You can also use addAll( ) to add two or more Menu instances in a single call. The added menus are positioned in the bar from left to right, in the order in which they are added. If you want to add a menu at a specific location, then use this version of add( ):

void add(int idx, E menu)

Here, menu is added at the index specified by idx. Indexing begins at 0, with 0 being the left-most menu.

In some cases, you might want to remove a menu that is no longer needed. You can do this by calling remove( ) on the ObservableList returned by getMenus( ). Here are two of its forms:

boolean remove(Object menu)

E remove(int idx)

Here, menu is a reference to the menu to remove, and idx is the index of the menu to remove. Indexing begins at zero. The first form returns true if the item was found and removed. The second form returns a reference to the removed element.

It is sometimes useful to obtain a count of the number of items in a menu bar. To do this, call size( ) on the list returned by getMenus( ).

NOTE Recall that ObservableList implements the List collections interface, which gives you access to all the methods defined by List.

Once a menu bar has been created and populated, it is added to the scene graph in the normal way.

Menu

Menu encapsulates a menu, which is populated with MenuItems. As mentioned, Menu is derived from MenuItem. This means that one Menu can be a selection in another Menu. This enables one menu to be submenu of another. Menu defines four constructors. Perhaps the most commonly used is shown here:

Menu(String name)

It creates a menu that has the name specified by name. You can specify an image along with text with this constructor:

Menu(String name, Node image)

Here, image specifies the image that is displayed. In both cases, the menu is empty until menu items are added to it. A third constructor lets you specify an initial set of menu items. It is shown here:

Menu(String name, Node image, MenuItem … menuItems)

Finally, you don’t have to give a menu a name when it is constructed. To create an unnamed menu, you can use the default constructor:

Menu( )

In this case, you can add a name and/or image after the fact by calling setText( ) or setGraphic( ).

Each menu maintains a list of menu items that it contains. To add an item to the menu, add items to this list. You can do this by either specifying them in the Menu constructor, or by adding them to the list later. To add items after a Menu has been constructed, first call getItems( ), shown here:

final ObservableList<MenuItem> getItems( )

It returns the list of items currently associated with the menu. To this list, add menu items by calling either add( ) or addAll( ). Among other actions, you can remove an item by calling remove( ) and obtain the size of the list by calling size( ).

One other point: You can add a menu separator to the list of menu items, which is an object of type SeparatorMenuItem. Separators help organize long menus by allowing you to group related items together. A separator can also help set off an important item, such as the Exit selection in a menu.

MenuItem

MenuItem encapsulates an element in a menu. This element can be either a selection linked to some program action, such as Save or Close, or it can cause a submenu to be displayed. MenuItem defines the following three constructors:

MenuItem( )

MenuItem(String name)

MenuItem(String name, Node image)

The first creates an empty menu item. The second lets you specify the name of the item, and the third enables you to include an image.

A MenuItem generates an action event when selected. You can register an action event handler for such an event by calling setOnAction( ), just as you did when handling button events. It is shown again for your convenience:

final void setOnAction(EventHandler<ActionEvent> handler)

Here, handler specifies the event handler. You can fire an action event on a menu item by calling fire( ).

MenuItem defines several methods. One that is often useful is setDisable( ), which you can use to enable or disable a menu item. It is shown here:

final void setDisable(boolean disable)

If disable is true, the menu item is disabled and cannot be selected. If disable is false, the item is enabled. Using setDisable( ), you can turn menu items on or off, depending on program conditions.

Create a Main Menu

As a general rule, the most commonly used menu is the main menu. This is the menu defined by the menu bar, and it is the menu that defines all (or nearly all) of the functionality of an application. As you will see, JavaFX streamlines the process of creating and managing the main menu. Here, you will see how to construct a simple main menu. Subsequent sections will show various options.

NOTE As a way of clearly illustrating the similarities and differences between the Swing and JavaFX menu systems, the examples in this chapter rework the menu examples from Chapter 33. If you already know Swing, you might find it helpful to compare the two different approaches.

Constructing the main menu requires several steps. Here is one approach. First, create the MenuBar instance that will hold the menus. Next, construct each menu that will be in the menu bar. In general, a menu is constructed by first creating a Menu object and then adding MenuItems to it. After the menus have been created, add them to the menu bar. Then, the menu bar, itself, must be added to the scene graph. Finally, for each menu item, you must add an action event handler that responds to the action event fired when a menu item is selected.

A good way to understand the process of creating and managing menus is to work through an example. Here is a program that creates a simple menu bar that contains three menus. The first is a standard File menu that contains Open, Close, Save, and Exit selections. The second menu is called Options, and it contains two submenus called Colors and Priority. The third menu is called Help, and it has one item: About. When a menu item is selected, the name of the selection is displayed in a label.

Images

Images

Images

Images

Images

Images

Sample output is shown here:

Images

Let’s examine, in detail, how the menus in this program are created. First, note that MenuDemo uses a BorderPane instance for the root node. BorderPane is similar to the AWT’s BorderLayout discussed in Chapter 26. It defines a window that has five areas: top, bottom, left, right, and center. The following methods set the node assigned to these areas:

final void setTop(Node node)

final void setBottom(Node node)

final void setLeft(Node node)

final void setRight(Node node)

final void setCenter(Node node)

Here, node specifies the element, such as a control, that will be shown in each location. Later in the program, the menu bar is positioned in the top location and a label that displays the menu selection is set to the center position. Setting the menu bar to the top position ensures that it will be shown at the top of the application and will automatically be resized to fit the horizontal width of the window. This is why BorderPane is used in the menu examples. Of course, other approaches, such as using a VBox, are also valid.

Much of the code in the program is used to construct the menu bar, its menus, and menu items, and this code warrants a close inspection. First, the menu bar is constructed and a reference to it is assigned to mb by this statement:

Images

At this point, the menu bar is empty. It will be populated by the menus that follow.

Next, the File menu and its menu entries are created by this sequence:

Images

The names Open, Close, Save, and Exit will be shown as selections in the menu. The menu entries are added to the File menu by this call to addAll( ) on the list of menu items returned by getItems( ):

Images

Recall that getItems( ) returns the menu items associated with a Menu instance. To add menu items to a menu, you will add them to this list. Notice that a separator is used to separate visually the Exit entry from the others.

Finally, the File menu is added to the menu bar by this line:

Images

Once the preceding code sequence completes, the menu bar will contain one entry: File. The File menu will contain four selections in this order: Open, Close, Save, and Exit.

The Options menu is constructed using the same basic process as the File menu. However, the Options menu consists of two submenus, Colors and Priority, and a Reset entry. The submenus are first constructed individually and then added to the Options menu. As explained, because Menu inherits MenuItem, a Menu can be added as an entry into another Menu. This is the way the submenus are created. The Reset item is added last. Then, the Options menu is added to the menu bar. The Help menu is constructed using the same process.

After all of the menus have been constructed, an ActionEvent handler called MEHandler is created that will process menu selections. For demonstration purposes, a single handler will process all selections, but in a real-world application, it is often easier to specify a separate handler for each individual selection by using anonymous inner classes or lambda expressions. The ActionEvent handler for the menu items is shown here:

Images

Inside handle( ), the target of the event is obtained by calling getTarget( ). The returned reference is cast to MenuItem, and its name is returned by calling getText( ). This string is then assigned to name. If name contains the string "Exit", the application is terminated by calling Platform.exit( ). Otherwise, the name is displayed in the response label.

Before continuing, it must be pointed out that a JavaFX application must call Platform.exit( ), not System.exit( ). The Platform class is defined by JavaFX and packaged in javafx.application. Its exit( ) method causes the stop( ) life-cycle method to be called. System.exit( ) does not.

Finally, MEHandler is registered as the action event handler for each menu item by the following statements:

Images

Notice that no listeners are added to the Colors or Priority items because they are not actually selections. They simply activate submenus.

Finally, the menu bar is added to the root node by the following line:

Images

This causes the menu bar to be placed at the top of the window.

At this point, you might want to experiment a bit with the MenuDemo program. Try adding another menu or adding additional items to an existing menu. It is important that you understand the basic menu concepts before moving on because this program will evolve throughout the remainder of this chapter.

Add Mnemonics and Accelerators to Menu Items

The menu created in the preceding example is functional, but it is possible to make it better. In real applications, a menu usually includes support for keyboard shortcuts. These come in two forms: accelerators and mnemonics. An accelerator is a key combination that lets you select a menu item without having to first activate the menu. As it applies to menus, a mnemonic defines a key that lets you select an item from an active menu by typing the key. Thus, a mnemonic allows you to use the keyboard to select an item from a menu that is already being displayed.

An accelerator can be associated with a Menu or MenuItem. It is specified by calling setAccelerator( ), shown next:

final void setAccelerator(KeyCombination keyComb)

Here, keyComb is the key combination that is pressed to select the menu item. KeyCombination is class that encapsulates a key combination, such as CTRL-S. It is packaged in javafx.scene.input.

KeyCombination defines two protected constructors, but often you will use the keyCombination( ) factory method, shown here:

static KeyCombination keyCombination(String keys)

In this case, keys is a string that specifies the key combination. It typically consists of a modifier, such as CTRL, ALT, SHIFT, or META, and a letter, such as S. There is a special value, called shortcut, which can be used to specify the CTRL key in a Windows system and the META key on a Mac. (It also maps to the typically used shortcut key on other types of systems.) Therefore, if you want to specify CTRL-S as the key combination for Save, then use the string "shortcut+S". This way, it will work for both Windows and Mac and elsewhere.

The following sequence adds accelerators to the File menu created by the MenuDemo program in the previous section. After making this change, you can directly select a File menu option by pressing the corresponding key.

Images

A mnemonic can be specified for both MenuItem and Menu objects, and it is very easy to do. Simply precede the letter in the name of the menu or menu item with an underscore. For example, in the preceding example, to add the mnemonic F to the File menu, declare fileMenu as shown here:

Images

After making this change, you can select the File menu by typing ALT then F. However, mnemonics are active only if mnemonic parsing is true (as it is by default). You can turn mnemonic parsing on or off by using setMnemonicParsing( ), shown here:

final void setMnemonicParsing(boolean enable)

In this case, if enable is true, then mnemonic parsing is turned on. Otherwise, it is turned off.

After making these changes, the File menu will now look like this:

Images

Add Images to Menu Items

You can add images to menu items or use images instead of text. The easiest way to add an image is to specify it when the menu item is being constructed using this constructor:

MenuItem(String name, Node image)

It creates a menu item with the name specified by name and the image specified by image. For example, here the About menu item is associated with an image when it is created:

Images

After this addition, the image specified by aboutIV will be displayed next to the text “About” when the Help menu is displayed, as shown here:

Images

One last point: You can also add an image to a menu item after the item has been created by calling setGraphic( ). This lets you change the image during program execution.

Use RadioMenuItem and CheckMenuItem

Although the type of menu items used by the preceding examples are, as a general rule, the most commonly used, JavaFX defines two others: check menu items and radio menu items. These elements can streamline a GUI by allowing a menu to provide functionality that would otherwise require additional, stand-alone components. Also, sometimes including check or radio menu items simply seems most natural for a specific set of features. Whatever your reason, it is easy to use check and/or radio menu items in menus, and both are examined here.

To add a check menu item to a menu, use CheckMenuItem. It defines three constructors, which parallel the ones defined by MenuItem. The one used in this chapter is shown here:

CheckMenuItem(String name)

Here, name specifies the name of the item. The initial state of the item is unchecked. If you want to check a check menu item under program control, call setSelected( ), shown here:

final void setSelected(boolean selected)

If selected is true, the menu item is checked. Otherwise, it is unchecked.

Like stand-alone check boxes, check menu items generate action events when their state is changed. Check menu items are especially appropriate in menus when you have options that can be selected and you want to display their selected/deselected status.

A radio menu item can be added to a menu by creating an object of type RadioMenuItem. RadioMenuItem defines a number of constructors. The one used in this chapter is shown here:

RadioMenuItem(String name)

It creates a radio menu item that has the name passed in name. The item is not selected. As with the case of check menu items, to select a radio menu item, call setSelected( ), passing true as an argument.

RadioMenuItem works like a stand-alone radio button, generating both change and action events. Like stand-alone radio buttons, menu radio items must be put into a toggle group in order for them to exhibit mutually exclusive selection behavior.

Because both CheckMenuItem and RadioMenuItem inherit MenuItem, each has all of the functionality provided by MenuItem. Aside from having the extra capabilities of check boxes and radio buttons, they act like and are used like other menu items.

To try check and radio menu items, first remove the code that creates the Options menu in the MenuDemo example program. Then substitute the following code sequence, which uses check menu items for the Colors submenu and radio menu items for the Priority submenu:

Images

Images

After making the substitution, the check menu items in the Colors submenu look like those shown here:

Images

Here is how the radio menu items in the Priority submenu now look:

Images

Create a Context Menu

A popular alternative or addition to the menu bar is the popup menu, which in JavaFX is referred to as a context menu. Typically, a context menu is activated by clicking the right mouse button when over a control. Popup menus are supported in JavaFX by the ContextMenu class. The direct superclass of ContextMenu is PopupControl. An indirect superclass of ContextMenu is javafx.stage.PopupWindow, which supplies much of its basic functionality.

ContextMenu has two constructors. The one used in this chapter is shown here:

ContextMenu(MenuItem … menuItems)

Here, menuItems specify the menu items that will constitute the context menu. The second ContextMenu constructor creates an empty menu to which items must be added.

In general, context menus are constructed like regular menus. Menu items are created and added to the menu. Menu item selections are also handled in the same way: by handling action events. The main difference between a context menu and a regular menu is the activation process.

To associate a context menu with a control is amazingly easy. Simply call setContextMenu( ) on the control, passing in a reference to the menu that you want to pop up. When you right-click on that control, the associated context menu will be shown. The setContextMenu( ) method is shown here:

final void setContextMenu(ContextMenu menu)

In this case, menu specifies the context menu associated with the invoking control.

To demonstrate a context menu, we will add one to the MenuDemo program. The context menu will present a standard “Edit” menu that includes the Cut, Copy, and Paste entries. It will be set on a text field control. When the mouse is right-clicked while in the text field, the context menu will pop up. To begin, create the context menu, as shown here:

Images

This sequence begins by constructing the MenuItems that will form the menu. It then creates an instance of ContextMenu called editMenu that contains the items.

Next, add the action event handler to these menu items, as shown here:

Images

This finishes the construction of the context menu, but the menu has not yet been associated with a control.

Now, add the following sequence that creates the text field:

Images

Next, set the context menu on the text field:

Images

Now, when the mouse is right-clicked over the text field, the context menu will pop up.

To add the text field to the program, you must create a flow pane that will hold both the text field and the response label. This pane will then be added to the center of the BorderPane. This step is necessary because only one node can be added to any single location within a BorderPane. First, remove this line of code:

Images

Replace it with the following code:

Images

Of course, the menu bar is still added to the top position of the border pane.

After making these changes, when you right-click over the text field, the context menu will pop up, as shown here:

Images

It is also possible to associate a context menu with a scene. One way to do this is by calling setOnContextMenuRequested( ) on the root node of the scene. This method is defined by Node and is shown here:

final void setOnContextMenuRequested(

EventHandler<? super ContextMenuEvent> eventHandler)

Here, eventHandler specifies the handler that will be called when a popup request has been received for the context menu. In this case, the handler must call the show( ) method defined by ContextMenu to cause the context menu to be displayed. This is the version we will use:

final void show(Node node, double upperX, double upperY)

Here, node is the element on which the context menu is linked. The values of upperX and upperY define the X,Y location of the upper-left corner of the menu, relative to the screen. Typically, you will pass the screen coordinates at which the right-click occurred. To do this, you will call the getScreenX( ) and getScreenY( ) methods defined by ContextMenuEvent. They are shown here:

final double getScreenX( )

final double getScreenY( )

Thus, you will typically pass the results of these methods to the show( ) method.

The preceding theory can be put into practice by adding the context menu to the root node of the scene graph. After doing so, right-clicking anywhere in the scene will cause the menu to pop up. To do this, add the following sequence to the MenuDemo program:

Images

After you have made this addition, the context menu can be activated by clicking the right mouse button anywhere inside the application scene. For example, here is the menu displayed after right-clicking in the upper-left portion of the window:

Images

Create a Toolbar

A toolbar is a component that can serve as both an alternative and as an adjunct to a menu. Typically, a toolbar contains a list of buttons that give the user immediate access to various program options. For example, a toolbar might contain buttons that select various font options, such as bold, italics, highlight, or underline. These options can be selected without the need to drop through a menu. As a general rule, toolbar buttons show images rather than text, although either or both are allowed. Furthermore, often tooltips are associated with image-based toolbar buttons.

In JavaFX, toolbars are instances of the ToolBar class. It defines the two constructors, shown here:

ToolBar( )

ToolBar(Node … nodes)

The first constructor creates an empty, horizontal toolbar. The second creates a horizontal toolbar that contains the specified nodes, which are usually some form of button. If you want to create a vertical toolbar, call setOrientation( ) on the toolbar. It is shown here:

final void setOrientation(Orientation how)

The value of how must be either Orientation.VERTICAL or Orientation.HORIZONTAL.

You add buttons (or other controls) to a toolbar in much the same way that you add them to a menu bar: call add( ) on the reference returned by the getItems( ) method. Often, however, it is easier to specify the items in the ToolBar constructor, and that is the approach used in this chapter. Once you have created a toolbar, add it to the scene graph. For example, when using a border layout, it could be added to the bottom location. Of course, other approaches are commonly used. For example, it could be added to a location directly under the menu bar or at the side of the window.

To illustrate a toolbar, we will add one to the MenuDemo program. The toolbar will present three debugging options: Set Breakpoint, Clear Breakpoint, and Resume Execution. We will also add tooltips to the menu items. Recall from the previous chapter, a tooltip is a small message that describes an item. It is automatically displayed if the mouse hovers over the item for moment. You can add a tooltip to the menu item in the same way as you add it to a control: by calling setTooltip( ). Tooltips are especially useful when applied to image-based toolbar controls because sometimes it’s hard to design images that are intuitive to all users.

First, add the following code, which creates the debugging toolbar:

Images

Let’s look at this code closely. First, three buttons are created that correspond to the debug actions. Notice that each has an image associated with it. Next, each button deactivates the text display by calling setContentDisplay( ). As a point of interest, it would have been possible to leave the text displayed, but the toolbar would have had a somewhat nonstandard look. (The text for each button is still needed, however, because it will be used by the action event handler for the buttons.) Tooltips are then set for each button. Finally, the toolbar is created, with the buttons specified as the contents.

Next, add the following sequence, which defines an action event handler for the toolbar buttons:

Images

Finally, add the toolbar to the bottom of the border layout by using this statement:

Images

After making these additions, each time the user presses a toolbar button, an action event is fired, and it is handled by displaying the button’s text in the response label. The following output shows the toolbar in action:

Images

Put the Entire MenuDemo Program Together

Throughout the course of this discussion, many changes and additions have been made to the MenuDemo program shown at the start of the chapter. Before concluding, it will be helpful to assemble all the pieces. Doing so not only eliminates any ambiguity about the way the pieces fit together, but it also gives you a complete menu demonstration program that you can experiment with.

The following version of MenuDemo includes all of the additions and enhancements described in this chapter. For clarity, the program has been reorganized, with separate methods being used to construct the various menus and toolbar. Notice that several of the menu-related variables, such as mb and tbDebug, have been made into instance variables so they can be directly accessed by any part of the class.

Images

Images

Images

Images

Images

Images

Images

Images

Images

Images

Images

Continuing Your Exploration of JavaFX

JavaFX represents a major advance in GUI frameworks for Java. It also redefines aspects of the Java platform. The preceding three chapters have introduced several of its core features, but there is much left to explore. For example, JavaFX supplies several more controls, such as sliders, stand-alone scroll bars, and tables. You will want to experiment with its layouts, such as VBox and HBox. You will also want to explore, in detail, the various effects in javafx.scene.effect and the various transforms in javafx.scene.transform. Another exciting class is WebView, which gives you an easy way to integrate web content into a scene graph. Frankly, all of JavaFX is worthy of serious study. In many ways, it is charting the future course of Java.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.118.229