# 3.3 - New Time Zone Dialog In this section, we will create a dialog and add it to our clock application. ![](GSPT_files/Panel-02.jpg) You will learn: ## Creating a Panel A Panel is a special kind of window. Read the Cocoa document for more details: [*Windows and Panels*](https://web.archive.org/web/20010606130438if_/https://developer.apple.com/techpubs/macosx/Cocoa/TasksAndConcepts/ProgrammingTopics/WinPanel/index.html). Since I have a clock already, I want to know the time in different time zones. My idea is that when I click the title of the `NSBox`, a panel will show up and ask the time zone. Once the time zone is inputted, it will display the time in that area. Since `NSPanel` is a subclass of `NSWindow`, the usage of `NSPanel` is similar to `NSWindow`. Again, I need a "controller" to control the "view", which is `NSPanel` in this case. The "view" is generated by Gorm, and I need to write the "controller" by myself. In this example, I'll show how to load the gorm file. There are many built-in panel in GNUstep. I also use one in this example. First, we need to build the interface for the panel. Open Gorm, and choose the menu item "Document → New Module → New Empty". Look at the palettes. There is one for "Panel". **Figure 4-22. Panel in Gorm** ![](GSPT_files/Panel-01.jpg) Drag the Panel out of Palettes. Build the interface as below. **Figure 4-23. Interface of time zone panel** ![](GSPT_files/Panel-02.jpg) You can change the size of panel in the inspector. Here are the attributes of the panel. **Figure 4-24. Panel attributes** ![](GSPT_files/Panel-03.jpg) ## Controlling the panel Now, I have the "view". Then where is the "controller"? Generally, you can write a new class as the controller of this view, but this is a small program, so you don't have to write a class just for the controller. So I decide to use the class `TimeView` as the controller for this panel. So `TimeView` acts as the custom view for that main window interface, and as the controller for the panel. Since `TimeView` is the controller of this panel, I need to connect the outlets and actions. Therefore, I need to create the class `TimeView` again in this gorm file, even though there is already one in TimeMachine.gorm file. ```{admonition} Editor's note This seems like a bad idea, but changing it would require a lot of refactoring of this tutorial. ``` You already know how to create the class `TimeView`. I add two outlets, `zonePanel` and `zoneField`, and two actions, `okAction:` and `cancelAction:`. **Figure 4-25. Outlets for time zone panel** ![](GSPT_files/Panel-05.jpg) **Figure 4-26. Actions for time zone panel** ![](GSPT_files/Panel-04.jpg) But rather than creating an instance to connect to the panel, let's set the owner of this panel to our `TimeView`. This reduces the amount of instances in our project. Select the NSOwner in Gorm main window, then select class `TimeView` in the Attributes tab of the inspector. **Figure 4-27. Set NSOwner to TimeView class** ![](GSPT_files/Panel-06.jpg) ![](GSPT_files/Panel-07.jpg) By this way, I can connect the panel to the `NSOwner`, which is an instance of class `TimeView`. Connect the two buttons to the actions in NSOwner, the outlet `zoneField` to the `NSTextField` in the panel, and the outlet `zonePanel` to the panel. Pay attention to how the `NSOwner` connects to the panel. **Figure 4-28. Connect outlet** ![](GSPT_files/Panel-08.jpg) ![](GSPT_files/Panel-09.jpg) Save this interface as `TimeZonePanel.gorm`, and quit Gorm. Don't generate the files for class `TimeView` because I already have the files. GNUstep can figure out where the classes are and where the outlets/actions are. ### The code Now, I need to add the new outlets and actions into the files of `TimeView`. Here is the header. `TimeView.h:` ```objc #import #import "ClockView.h" @interface TimeView : NSControl { id zonePanel; id zoneField; NSBox* box; NSTextField *labelDate, *labelTime; NSTextField *localDate, *localTime; NSCalendarDate* date; ClockView* clockView; } - (NSCalendarDate *) date; - (void) setDate: (NSCalendarDate *) date; - (void) okAction: (id) sender; - (void) cancelAction: (id) sender; @end ``` I add the outlets and actions by myself. `TimeView.m:` ```objc - (void) mouseDown: (NSEvent *) event { NSRect titleFrame = [box titleRect]; NSPoint windowLocation = [event locationInWindow]; NSPoint viewLocation = [self convertPoint: windowLocation fromView: [self superview]]; BOOL status = NSMouseInRect(viewLocation, titleFrame, NO); if (status == YES) { [NSBundle loadNibNamed: @"TimeZonePanel.gorm" owner: self]; [NSApp runModalForWindow: self->zonePanel]; } } ``` The method `-mouseDown:` is called when mouse is clicked within this view. Here, we calculate whether the mouse is clicked in the area of the title of the `NSBox`. If so, we use `[NSBundle loadNibNamed: owner:]` to load the window, and `[NSApp runModalForWindow]` to display it. Read Cocoa's document about "[*How Modal Windows Work*](http://developer.apple.com/techpubs/macosx/Cocoa/TasksAndConcepts/ProgrammingTopics/WinPanel/Concepts/UsingModalWindows.html#CJBEADBA)". Now, I just need to finish the actions part in `TimeView.m`. `TimeView.m:` ```objc - (void) cancelAction: (id) sender { [NSApp abortModal]; [self->zonePanel close]; } - (void) okAction: (id) sender { NSTimeZone* tempZone; tempZone = [NSTimeZone timeZoneWithName: [zoneField stringValue]]; [NSApp stopModal]; [zonePanel close]; if (tempZone == nil) { NSRunAlertPanel(@"Warning!", @"Wrong Time Zone !!", @"OK", nil, nil); } else { [date setTimeZone: tempZone]; [box setTitle: [tempZone description]]; [self setDate: date]; } } ``` In method `-okAction:`, I use a built-in panel, `NSRunAlertPanel`. There are several built-in panels in GNUstep ready to use. Now, you can display the current time in different time zone. ## Better keyboard access It is inconvenient to use this pop-up panel because you have to click the `NSTextField` before typing. Sometimes, it is more convenient to control the user interface via keyboard rather than mouse. When a window pop-up, it is the "First Responder" -- the first object to receive events and keypresses. But usually we want some other objects in this window to receive the keypresses. Therefore, we need to change the "first responder" of this window by using `[NSWindow makeFirstResponder:]`. When I want to use Tab key to switch between different views in the window, you need to assign the `nextKeyView` to the next view when the Tab key is pressed so that the Application Kit knows where the responder should be. Finally, when I finish typing in the `NSTextField`, I want to hit the Return key to click so that I don't need to move my hand out of the keyboard. In this case, since `NSTextField` is also a subclass of `NSControl`, I can set the target and action of `NSTextField` to be the same as the `NSButton` . Therefore, when I hit Return, it is equivalent to clicking on the button. These are small tune-ups for the application, but it makes it easier to use the application. First, let's set the "first responder" of the window to the NSTextField: `TimeView.m:` ```objc - (void) mouseDown: (NSEvent *) event { NSRect titleFrame = [self->box titleRect]; NSPoint windowLocation = [event locationInWindow]; NSPoint viewLocation = [self convertPoint: windowLocation fromView: [self superview]]; BOOL status = NSMouseInRect(viewLocation, titleFrame, NO); if (status == YES) { [NSBundle loadNibNamed: @"TimeZonePanel.gorm" owner: self]; [self->zonePanel makeFirstResponder: zoneField]; [NSApp runModalForWindow: self->zonePanel]; } } ``` Only one line is enough. Now, when this panel shows up, the cursor will automatically be in the `NSTextField`. Second, let's set the target and action of `NSTextField` to be the same as the `NSButton` . Open the `TimeZonePanel.gorm`, and connect the `NSTextField` to the method `-okAction:` of the `NSOwner`. That's it. Whenever you hit the Return key in the `NSTextField`, the method `-okAction:` is called. **Figure 4-29. Connection NSTextField action** ![](GSPT_files/Panel-10.jpg) ![](GSPT_files/Panel-11.jpg) ![](GSPT_files/Panel-12.jpg) Third, we need to connect the `nextKeyView` outlet between the views in the window. I'll connect the `nextKeyView` of `NSTextField` to the `NSButton` , the `nextKeyView` outlet of the `NSButton` to the `NSButton` , and the `nextKeyView` outlet of the `NSButton` to the `NSTextField`. By doing that, I can switch between these views using the Tab key. Here, I just show how the `nextKeyView` of `NSTextField` connects to the `NSButton` . **Figure 4-30. Connect nextKeyView** ![](GSPT_files/Panel-13.jpg) ![](GSPT_files/Panel-14.jpg) Source code: [Panel-src.tar.gz](http://gnustep.made-it.com/GSPT/Panel/Panel-src.tar.gz).