// Windows.m Copyright (c) Kari Laitinen // http://www.naturalprogramming.com // 2013-07-16 Objective-C version of this program was created. // 2013-07-17 Last modification. // This program displays simple character-based 'windows' to // the Terminal screen. // The classes of this program provide an example of // a class hierarchy. // NOTE: As this is an educational program, the classes defined // here are not safe for all kinds of uses. If you try to define // very large or very small windows, or put long texts inside // the windows, the classes may not work properly. #include #import #include #define MAXIMUM_WINDOW_WIDTH 78 #define MAXIMUM_WINDOW_HEIGHT 22 /* Class Window is the topmost class in the class hierarchy. A Window could look like //////////// //////////// //////////// //////////// In this window the width is 12, the height is 4, and the background character is '/' */ @interface Window : NSObject { char window_contents [ MAXIMUM_WINDOW_WIDTH ] [ MAXIMUM_WINDOW_HEIGHT ] ; int window_width ; int window_height ; char background_character ; } - (id) init ; - (id) init_width: (int) desired_window_width and_height: (int) desired_window_height and_background: (char) given_background_character ; - (int) window_width ; - (int) window_height ; - (char) window_contents_at_x: (int) character_index_x at_y: (int) character_index_y ; - (void) fill_with_character: (char) filling_character ; - (void) print ; - (void) move_window: (Window*) another_window to_x: (int) destination_x_index to_y: (int) destination_y_index ; @end @implementation Window - (id) init_width: (int) desired_window_width and_height: (int) desired_window_height and_background: (char) given_background_character { self = [ super init ] ; if ( self ) { window_width = desired_window_width ; window_height = desired_window_height ; background_character = given_background_character ; [ self fill_with_character: background_character ] ; } return self ; } - (id) init { self = [ super init ] ; if ( self ) { [ self init_width: MAXIMUM_WINDOW_WIDTH and_height: MAXIMUM_WINDOW_HEIGHT and_background: ' ' ] ; } return self ; } - (int) window_width { return window_width ; } - (int) window_height { return window_height ; } - (char) window_contents_at_x: (int) character_index_x at_y: (int) character_index_y { return window_contents [ character_index_x ] [ character_index_y ] ; } - (void) fill_with_character: (char) filling_character { int row_index = 0 ; while ( row_index < window_height ) { int column_index = 0 ; while ( column_index < window_width ) { window_contents[ column_index ] [ row_index ] = filling_character ; column_index ++ ; } row_index ++ ; } } - (void) print { printf( "\n" ) ; int row_index = 0 ; while ( row_index < window_height ) { int column_index = 0 ; while ( column_index < window_width ) { printf( "%c", window_contents[ column_index ] [ row_index ] ) ; column_index ++ ; } row_index ++ ; printf( "\n" ) ; } } // The following method moves another window over 'this' window. - (void) move_window: (Window*) another_window to_x: (int) destination_x_index to_y: (int) destination_y_index { int source_y_index = 0 ; while ( source_y_index < [ another_window window_height ] ) { if ( destination_y_index >= 0 && destination_y_index < window_height ) { int source_x_index = 0 ; int saved_destination_x_index = destination_x_index ; while ( source_x_index < [ another_window window_width ] ) { if ( destination_x_index >= 0 && destination_x_index < window_width ) { window_contents [ destination_x_index ] [ destination_y_index ] = [ another_window window_contents_at_x: source_x_index at_y: source_y_index ] ; } source_x_index ++ ; destination_x_index ++ ; } destination_x_index = saved_destination_x_index ; } source_y_index ++ ; destination_y_index ++ ; } } @end /* Class FrameWindow represents a window that looks like |----------------------| | | | | | | | | | | |----------------------| The window has frames that are made by using characters '|' and '-'. The inner area of the window contains space characters. */ @interface FrameWindow : Window - (id) init_width: (int) desired_window_width and_height: (int) desired_window_height ; @end @implementation FrameWindow - (id) init_width: (int) desired_window_width and_height: (int) desired_window_height { [ super init_width: desired_window_width and_height: desired_window_height and_background: '|' ] ; Window* horizontal_frames = [ [ Window alloc ] init_width: desired_window_width - 2 and_height: desired_window_height and_background: '-' ] ; Window* spaces_inside_window = [ [ Window alloc ] init_width: desired_window_width - 2 and_height: desired_window_height - 2 and_background: ' ' ] ; [ self move_window: horizontal_frames to_x: 1 to_y: 0 ] ; [ self move_window: spaces_inside_window to_x: 1 to_y: 1 ] ; return self ; } @end /* A TextWindow can look like the following |----------------------------| | | | | | | | Hello, world. | | | | | |----------------------------| The text is stored as a C-style string. */ @interface TextWindow : FrameWindow { char text_inside_window[ MAXIMUM_WINDOW_WIDTH ] ; } - (id) init_width: (int) desired_window_width and_height: (int) desired_window_height and_text: (char*) given_line_of_text ; - (void) change_text: (char*) new_line_of_text ; @end @implementation TextWindow // embed_text_in_window is a private method that is not // mentioned in the interface of the class - (void) embed_text_in_window { int text_length = strlen( text_inside_window ) ; int text_row = window_height / 2 ; int text_start_column = (window_width - text_length) / 2 ; int character_index ; for ( character_index = 0 ; character_index < text_length ; character_index ++ ) { window_contents [ text_start_column + character_index ] [ text_row ] = text_inside_window[ character_index ] ; } } - (id) init_width: (int) desired_window_width and_height: (int) desired_window_height and_text: (char*) given_line_of_text { [ super init_width: desired_window_width and_height: desired_window_height ] ; // String function strcpy() copies the given string to // the data field (attribute) in this class. strcpy( text_inside_window, given_line_of_text ) ; [ self embed_text_in_window ] ; return self ; } - (void) change_text: (char*) new_line_of_text { strcpy( text_inside_window, new_line_of_text ) ; [ self embed_text_in_window ] ; } @end /* The lowest class in the hierarchy is DecoratedTextWindow. Such a window has a decoration frame that is made with characters '*': **************************** *|------------------------|* *| |* *| |* *| |* *| Smile! |* *| |* *| |* *| |* *|------------------------|* **************************** */ @interface DecoratedTextWindow : TextWindow - (id) init_width: (int) desired_window_width and_height: (int) desired_window_height and_text: (char*) given_line_of_text ; @end @implementation DecoratedTextWindow - (id) init_width: (int) desired_window_width and_height: (int) desired_window_height and_text: (char*) given_line_of_text { [ super init_width: desired_window_width and_height: desired_window_height and_text: given_line_of_text ] ; Window* decoration_frame = [ [ Window alloc ] init_width: desired_window_width and_height: desired_window_height and_background: '*' ] ; TextWindow* window_inside_window = [ [ TextWindow alloc ] init_width: desired_window_width - 2 and_height: desired_window_height - 2 and_text: given_line_of_text ] ; [ decoration_frame move_window: window_inside_window to_x: 1 to_y: 1 ] ; [ self move_window: decoration_frame to_x: 0 to_y: 0 ] ; return self ; } @end int main( void ) { NSAutoreleasePool* autorelease_pool = [ [ NSAutoreleasePool alloc ] init ] ; Window* background_window = [ [ Window alloc ] init_width: 76 and_height: 22 and_background: '/' ] ; FrameWindow* empty_window = [ [ FrameWindow alloc ] init_width: 24 and_height: 7 ] ; TextWindow* greeting_window = [ [ TextWindow alloc ] init_width: 30 and_height: 8 and_text: "Hello, world." ] ; DecoratedTextWindow* smiling_window = [ [ DecoratedTextWindow alloc ] init_width: 28 and_height: 11 and_text: "Smile!" ] ; [ background_window move_window: empty_window to_x: 6 to_y: 2 ] ; [ background_window move_window: greeting_window to_x: 4 to_y: 12 ] ; [ greeting_window change_text: "HELLO, UNIVERSE!" ] ; [ background_window move_window: greeting_window to_x: 43 to_y: 11 ] ; [ background_window move_window: smiling_window to_x: 40 to_y: 3 ] ; // When we print the background_window, it will contain all the // other windows that were moved onto it. [ background_window print ] ; [ autorelease_pool drain ] ; }