The program of this section displays the leading portion of a file. It consists of a program source file. The program lists files of a given directory using a list box and uses a static display control to display the initial portion of the file. Perhaps one of the most interesting things this program does is subclass the list box. Subclassing a control window involves intercepting the messages for the window before the system provided window procedure receives them. For performance reasons, the list box of this example does not display files based upon the current section; rather, it relies upon the operator to double click the selection. The subclassing is necessary to provide an equivalent keyboard interface.
The client window data strcture has a number of variables declared within - as shown below.
struct window_data { procedure list_procedure_subclassed; bool valid_file; unsigned char read_buffer[maximum_read]; unsigned bytes_read; character buffer[maximum_read]; character filename[path_length::maximum_component]; handle listbox_handle, text_handle; irectangle bounds; };
These variables are described in the following table.
valid_file | A flag determining whether a file has been read. |
read_buffer | A buffer to contain the initial portion of the file. |
bytes_read | The number of bytes of the current file that were read. |
filename | The name of the file to be read. |
listbox_handle | The handle of the list box containing the list of files. |
text_handle | The handle of the static display window showing the file. |
bounds | The rectangle within which the text is drawn. |
The client window procedure creates the static display control and the listbox control during the processing of the message message::create - as shown below.
switch (identity) { case message::create: { window_data* data = new window_data(); set_window_pointer(window_handle, 0, (void*)data); handle device_context = get_device_context(window_handle); select_object(device_context, get_standard_object(standard_font::fixed_system)); text_metricstext_metrics_get; get_text_metrics(device_context, &text_metrics_get); release_device_context(window_handle, device_context); data->bounds[0](0) = 20 * text_metrics_get.average_character_width; data->bounds[0](1) = 3 * text_metrics_get.height; data->listbox_handle = create_window(L"listbox", (const character*)null, style::child | style::visible | listbox_style::_standard, text_metrics_get.average_character_width, text_metrics_get.height * 3, text_metrics_get.average_character_width * 13 + get_system_metrics(system_metric::vertical_scroll_width), text_metrics_get.height * 10, window_handle, (handle)1); data->list_procedure_subclassed = (procedure)set_window_pointer(data->listbox_handle, offset::window_procedure, (handle)list_procedure); send_message(data->listbox_handle, listbox_message::directory, (parameter)0x37, (parameter)L"*.*"); character buffer[path_length::maximum_component + 1]; get_current_directory(path_length::maximum_component, buffer); data->text_handle = create_window(L"static", buffer, style::child | style::visible | display_style::left, text_metrics_get.average_character_width, text_metrics_get.height, text_metrics_get.average_character_width * path_length::maximum_component, text_metrics_get.height, window_handle, (handle)2); } break;
A list box is first created then immediately subclassed via a call to set_window_pointer. A static display control is created and its initial text is set to contain the current directory. The subclassing window procedure is named list_procedure, and it is shown below.
result __stdcall list_procedure(handle window_handle, unsigned identity, parameter parameter1, parameter parameter2) { if (message == message::key_down && parameter1 == virtual_key::return) send_message(get_parent(window_handle), message::command, (parameter)make_integer((unsigned char)get_window_integer(window_handle,offset::identity), (unsigned char)listbox_notify::double_click), (parameter)window_handle); return call_window_procedure(list_procedure_subclassed,window_handle,identity,parameter1,parameter2); }
When the enter key is pressed, the code queries the parent window and packs a double click notification message to simulate the action of a double click on the selected item. all messages other than this are passed to the original list box procedure.
During the processing of message::create, the message listbox_message::directory is used to populate the list box with items from the selected directory. Selecting '..' moves up a directory.
When the notification message::command is received, the program either displays the selected file or changes directory depending upon whether a file or directory was selected. The code that does this is shown below.
case message::command: { window_data* data = (window_data*)get_window_pointer(window_handle, 0); if (low_part(parameter1) == 1 && high_part(parameter1) == listbox_notify::double_click) { int index = (int)send_message(data->listbox_handle, listbox_message::get_selection); if (index != listbox_return::error) { character buffer[path_length::maximum_component + 1]; send_message(data->listbox_handle, listbox_message::get_text, (parameter)index, (parameter)buffer); if (!(get_file_attributes(buffer) & file_attribute::directory)) { data->valid_file = true; copy_string(data->filename, buffer); get_current_directory(path_length::maximum_component, buffer); if (buffer[string_length(buffer) - 1] != '\\') concatenate_strings(buffer, L"\\"); set_window_text(data->text_handle, concatenate_strings(buffer, data->filename)); handle file = create_file(data->filename, access_type::generic_read, share_file::read); if (file) { read_file(file, data->read_buffer, maximum_read, &data->bytes_read); close_handle(file); multibyte_to_wide_character(0, 0, (const char*)data->read_buffer, data->bytes_read, data->buffer, maximum_read); } else data->valid_file = false; } else { data->valid_file = false; buffer[string_length(buffer) - 1] = '\0'; set_current_directory(buffer + 1); get_current_directory(path_length::maximum_component, buffer); set_window_text(data->text_handle, buffer); send_message(data->listbox_handle, listbox_message::reset_content); send_message(data->listbox_handle, listbox_message::directory, (parameter)0x37, (parameter)L"*.*"); } invalidate_rectangle(window_handle, (const irectangle*)null, true); } } } break;
The function get_file_attributes is called to determine if the selected item is a file or a directory. If a file is selected, the name of the file is concatenated to the name of the current directory and used to update the static display. The function create_file is then used to open the file for reading. The function read_file is used to read the initial portion of the file and the flag validFile is set to true.
When a directory is selected, that directory is made the current directory using the function set_current_directory. The full path name is then queried via a call to the function get_current_directory. The static display field is updated with the newly selected full path name. The message listbox_message::directory is used to repopulate the list box with the contents of the new directory. The window is then invalidated.
The text of the heading portion of the file is drawn when a paint message is received.
case message::paint: { window_data* data = (window_data*)get_window_pointer(window_handle, 0); paint paint_structure; handle device_context = begin_paint(window_handle, &paint_structure); select_object(device_context, get_standard_object(standard_font::fixed_system)); set_text_color(device_context, get_system_color(system_color::button_text)); set_background_color(device_context, get_system_color(system_color::button_face)); if (data->valid_file) draw_text(device_context, (const character*)data->buffer, data->bytes_read, &data->bounds, draw_text_format::word_break | draw_text_format::expand_tabs | draw_text_format::no_clip | draw_text_format::no_prefix); end_paint(window_handle, &paint_structure); } break;
The function draw_text is used to draw the text that was previously read from the file (that is, when the flag validFile is true).