#include "infoview.h" #include "diskpane.h" #include "keyboard.h" #include "mouse.h" #include #include #include extern info_node empty_node; Regex info_hyperlink( "\\(\\(Next:\\|Prev:\\|Up:\\)[ \t]+[^,\n]+\\)\\|\ \\(\\(\\*[Nn]ote\\|\\(^\\*\\)\\)[ \t\n]+\\)\ \\(\\([^:]+:[^:][ \t\n]*\\((.*)\\)?[^\\.,]*\\(\\.\\|,\\)\\)\\|\ \\(\\((.*)\\)?[^:]*::\\)\\)", 1 ); Regex link_prefix( "\\(\\(Next:\\|Prev:\\|Up:\\)[ \t]+\\)\\|\ \\(\\(\\(\\(\\*[Nn]ote\\)\\|\\(^\\*\\)\\)\\)[ \t\n]+\ \\([^:]+:[^:][ \t\n]*\\)?\\)", 1 ); Regex link_start( "Next:\\|Prev:\\|Up:\\|\\*[Nn]ote\\|\\(^\\*\\)", 1 ); Regex link_char( "[^,\\.:]", 1 ); Regex menu_tag( "^\\* [Mm][Ee][Nn][Uu]:", 1 ); info_viewer:: info_viewer( const char * info_dir, const char * home_dir ) { _info_path = info_dir; _home_directory = home_dir; _current_file = "dir"; _current_index = 0; _history_index = 0; _history_height = 0; _history = 0; _current_hyperlink.start = not_found; _current_hyperlink.length = not_found; _current_hyperlink.end_pos = point_not_found; _current_hyperlink.start_pos = point_not_found; _hyperlink_colour = DEFAULT_HYPERLINK_COLOUR; resize_history( INITIAL_HISTORY_HEIGHT ); _time_travelling = false; designated_quit_key = q_key; viewing_window.set_colour( DEFAULT_INFO_TEXT_COLOUR ); set_shadow( false ); set_border( false ); resize( ScreenCols(), ScreenRows() ); adopt( viewing_window ); adopt( node_prompt_box ); node_prompt_box.set_title_colour( black_bg+yellow_fg ); node_prompt_box.set_border_colour( black_bg+yellow_fg ); node_prompt_box.set_fill_char( ' ', black_bg+white_fg ); node_prompt_box.set_title( "Enter node name:" ); node_prompt_box.set_border( true ); node_prompt_box.resize( 60, 1 ); node_prompt_box.move_to( 5, 3 ); node_prompt_box.hide(); if( _node_table.read_file( _home_directory+DEFAULT_INFO_INDEX_FILE ) ) { build_master_index(); fprintf( stderr, "\n\nInfo index size = %d bytes.\n", _node_table.size() ); fprintf( stderr, "Ok to save for future instant start? (y/n) " ); while( key.pure() != n_key ) { key.get(); if( key.pure() == y_key ) { _node_table.write_file( _home_directory+DEFAULT_INFO_INDEX_FILE ); fprintf( stderr, "\n\nIndex saved as \"%s\"", (const char *)(_home_directory+DEFAULT_INFO_INDEX_FILE) ); break; } } fprintf( stderr, "\n\nPress a key to start using minfo." ); key.get(); } } info_viewer:: ~info_viewer() { delete [] _history; } //------------------- building index --------------------- int info_viewer:: build_master_index() { String file_link; string_array dir_file_list; string_array dir_file; bool error = dir_file.read_file( _info_path+"dir" ); if( !error ) { fprintf( stderr, "\n\nBuilding info index, please wait.\n" ); fprintf( stderr, "The index will be saved for fast startup in future.\n" ); fprintf( stderr, "\n* If you add a file to the info directory or install " ); fprintf( stderr, "a djgpp package\nlike allegro, please delete " ); fprintf( stderr, "minfo.idx so the index can be rebuilt.\n\n" ); point file_name_pos = dir_file.index( '(' ); while( file_name_pos != point_not_found ) { file_link = dir_file[file_name_pos.y].after( '(' ); file_link = file_link.before( ')' ); if( is_a_file( _info_path + file_link ) ) dir_file_list += file_link; file_name_pos = dir_file.index( '(', file_name_pos.x + 1, file_name_pos.y ); } dir_file_list.remove_repeats(); } _extra_file_list = get_file_list( _info_path + "*.inf" ); point found; found = _extra_file_list.index( DEFAULT_INFO_HELP_FILE ); if( found != point_not_found ) _extra_file_list.delete_line_at( found.y ); int i = 0; while( i < dir_file_list.height() ) { found = _extra_file_list.index( dir_file_list[i] ); if( found != point_not_found ) _extra_file_list.delete_line_at( found.y ); i++; } _extra_file_list.alpha_sort(); _node_table.resize( 0 ); i = 0; while( i < dir_file_list.height() ) { fprintf( stderr, "adding %s...",(const char *)dir_file_list[i] ); _node_table += build_file_index( _info_path + dir_file_list[i] ); i++; } i = 0; while( i < _extra_file_list.height() ) { fprintf( stderr, "adding %s...",(const char *)_extra_file_list[i] ); _node_table += build_file_index( _info_path + _extra_file_list[i] ); i++; } fprintf( stderr, "preparing menu node..." ); prepare_top_node(); fprintf( stderr, "adding %s...", DEFAULT_INFO_HELP_FILE ); _node_table += build_file_index( _info_path + DEFAULT_INFO_HELP_FILE ); return node_count(); } //--------------------------- input --------------------------- int info_viewer:: take_control() { hyperlink_info hyperlink; point previous_cursor_pos; draw_all_windows(); if( viewing_window.page.height() == 0 ) display_node( "(dir)Top" ); int event = 0; while( event != designated_quit_key ) { if( key.pressed() ) { key.get(); event = process_key( key ); } else { mouse.poll(); event = process_mouse(); if( previous_cursor_pos != viewing_window.page.cursor() ) { colour_hyperlink( _current_hyperlink, viewing_window.colour() ); hyperlink = hyperlink_at( viewing_window.page.cursor().x, viewing_window.page.cursor().y ); if( node_number( hyperlink.address ) != not_found ) { colour_hyperlink( hyperlink ); _current_hyperlink = hyperlink; } draw_all_windows(); } previous_cursor_pos = viewing_window.page.cursor(); } } return event; } int info_viewer:: process_key( int keystroke ) { hyperlink_info hyperlink; switch( keystroke ) { case shift_q_key: return designated_quit_key; break; case tab_key: select_next_link( viewing_window.page.cursor().x, viewing_window.page.cursor().y ); break; case s_key: search_node(); break; case f3_key: viewing_window.next_match( false ); break; case shift_w_key: case w_key: save_node(); break; case shift_j_key: case j_key: prompt_for_node(); break; case shift_l_key: case l_key: jump_back(); break; case shift_f_key: case f_key: jump_forward(); break; case shift_n_key: case n_key: jump_to( "Next:" ); break; case shift_p_key: case p_key: jump_to( "Prev:" ); break; case shift_u_key: case u_key: jump_to( "Up:" ); break; case return_key: hyperlink = hyperlink_at( viewing_window.page.cursor().x, viewing_window.page.cursor().y ); if( hyperlink.start != not_found ) display_node( hyperlink.address ); break; case space_bar: viewing_window.process_key( page_down_key ); viewing_window.hide_cursor(); break; case left_arrow_key: case right_arrow_key: case up_arrow_key: case down_arrow_key: case page_up_key: case page_down_key: case home_key: case end_key: viewing_window.process_key( keystroke ); default: ; // do nowt } return keystroke; } int info_viewer:: process_mouse() { viewing_window.process_mouse(); bool jumped_forward; if( mouse.right_click() ) { jumped_forward = false; while( mouse.right_click() ) { mouse.poll(); if( mouse.left_click() ) { while( mouse.left_click() ) mouse.poll(); process_key( f_key ); jumped_forward = true; } } if( !jumped_forward ) process_key( l_key ); } else if( mouse.left_double_click() ) { if( mouse.x() == 0 && mouse.y() == 0 ) return designated_quit_key; process_key( return_key ); viewing_window.hide_cursor(); while( mouse.left_click() ) mouse.poll(); } else if( mouse.left_click() ) viewing_window.mouse_scroll_while( mouse_left_click ); return mouse.event(); } void info_viewer:: prompt_for_node() { node_prompt_box.take_control(); display_node( node_prompt_box.value() ); } //--------------------------- navigation ------------------------------ void info_viewer:: jump_back() { if( _history_index > 0 ) { _history_index--; _time_travelling = true; display_node( _history[_history_index] ); } } void info_viewer:: jump_to( const char * top_line_link_name ) { int link_start = viewing_window.page.text[0].index( top_line_link_name ); if( link_start > 0 ) { hyperlink_info hyperlink ; hyperlink = hyperlink_at( link_start, 0 ); if( hyperlink.start != not_found ) display_node( hyperlink.address ); } } void info_viewer:: jump_forward() { if( _history_index < _history_height && _history[_history_index+1] != not_found ) { _history_index++; _time_travelling = true; display_node( _history[_history_index] ); } } hyperlink_info info_viewer:: hyperlink_after( int x, int y ) { string_array & node_text = viewing_window.page.text; hyperlink_info failed = { not_found, not_found, { not_found, not_found } }; if( y < 0 || y >= node_text.height() || x < 0 ) return failed; point pos = node_text.index( link_start, x, y ); if( pos == point_not_found ) return failed; String big_string; int i = pos.y; int end = pos.y + 5; while( i < end && i < node_text.height() ) { big_string += node_text[i] + '\n'; i++; } return next_link( big_string, pos.x, pos.y ); } hyperlink_info info_viewer:: hyperlink_at( int x, int y ) { hyperlink_info failed = { not_found, not_found, { not_found, not_found } }; string_array & node_text = viewing_window.page.text; if( y < 0 || y >= node_text.height() || x < 0 || x >= (int)node_text[y].length() ) return failed; String big_string; int start_line = 0; if( y == 0 ) big_string = node_text[0]; else { if( y > 2 ) start_line = y - 2; else start_line = 1; int end = start_line + 5; if( end >= node_text.height() ) end = node_text.height() - 1; int i = start_line; while( i < end ) { big_string += node_text[i] + '\n'; i++; } } int target_index = big_string_index( big_string, x, y - start_line ); hyperlink_info hyperlink = next_link( big_string, 0, start_line ); while( hyperlink.start != not_found ) { if( hyperlink.start <= target_index && hyperlink.start + hyperlink.length > target_index ) break; hyperlink = next_link( big_string, hyperlink.start + hyperlink.length, start_line ); } return hyperlink; } hyperlink_info info_viewer:: next_link( String & big_string, int start_pos, int y_offset ) { hyperlink_info failed = { not_found, not_found, { not_found, not_found } }; hyperlink_info hyperlink = failed; if( big_string.length() == 0 || start_pos < 0 || start_pos >= (int)big_string.length() ) return failed; int start = big_string.index( info_hyperlink, start_pos ); if( start != not_found && big_string.index( menu_tag ) == start ) if( start < (int)big_string.length() - 1 ) start = big_string.index( info_hyperlink, start+1 ); if( start != not_found ) { hyperlink.start = start; hyperlink.address = big_string.from( start ); hyperlink.address = hyperlink.address.through( info_hyperlink ); hyperlink.length = hyperlink.address.length(); } hyperlink.address = hyperlink.address.after( link_prefix ); if( hyperlink.address.contains( "::" ) ) hyperlink.address = hyperlink.address.before( "::" ); else { int address_end = hyperlink.address.index( link_char, -1 ); if( address_end != not_found ) hyperlink.address = hyperlink.address.through( address_end ); } hyperlink.address.gsub( RXwhite, ' ' ); hyperlink.start_pos = big_string_pos( big_string, hyperlink.start ); hyperlink.end_pos = big_string_pos( big_string, hyperlink.start + hyperlink.length - 1 ); hyperlink.start_pos.y += y_offset; hyperlink.end_pos.y += y_offset; return hyperlink; } /* bool same_node_name( String one, String other ) { one.gsub( RXwhite, ' ' ); other.gsub( RXwhite, ' ' ); if( one.contains( other, 0 ) ) return true; else return false; } */ int info_viewer:: node_number( String node_name ) { if( node_name.length() == 0 ) return not_found; info_node index_entry; if( node_name.contains( '(' ) ) { index_entry.file_name = node_name.after( '(' ); index_entry.file_name = index_entry.file_name.before( ')' ); node_name = node_name.after( ')' ); } else index_entry.file_name = _current_file; index_entry.file_name.downcase(); if( !is_a_file( _info_path+index_entry.file_name ) ) return not_found; if( node_name.length() ) index_entry.node_name = node_name; else index_entry.node_name = "Top"; index_entry.node_name.gsub( RXwhite, ' ' ); String prefix; int best_match_index = not_found; int match, best_match = 0; int i = 0; while( i < _node_table.node_count() ) { if( _node_table[i].node_name == index_entry.node_name && _node_table[i].file_name == index_entry.file_name ) { best_match_index = i; break; } else if( _node_table[i].node_name.contains( index_entry.node_name, 0 ) ) { prefix = common_prefix( _node_table[i].file_name, index_entry.file_name ); match = prefix.length(); if( match > best_match ) { best_match = match; best_match_index = i; } } i++; } return best_match_index; } string_array info_viewer:: get_node_text( info_node desired_node ) { string_array section; if( desired_node == empty_node ) return section; bool error = section.read_file_from( _info_path + desired_node.file_name, desired_node.start, desired_node.height ); if( error ) { char buffer[32]; String message = "\nget_node_text() failed to read from file: \"" + desired_node.file_name + "\"\non attempting to read "; sprintf( buffer, "%d", desired_node.height ); message += buffer; message += " lines, starting at line "; sprintf( buffer, "%d", desired_node.start ); message += buffer; message += ".\n"; clear_screen(); perror( message ); key.get(); } section.set_tab_width( 8 ); section.detab(); return section; } bool info_viewer:: resize_history( int new_history_height ) { if( new_history_height < 0 ) return true; int * temp = new int[new_history_height]; if( !temp ) return true; memset( temp, not_found, new_history_height*sizeof(int) ); if( _history ) { int copy_height = smaller_of( _history_height, new_history_height ); memcpy( temp, _history, copy_height*sizeof(int) ); delete [] _history; } _history = temp; _history_height = new_history_height; return false; } void info_viewer:: set_history_start( const char * node_name ) { int position = node_number( node_name ); if( position != not_found ) { _history[0] = position; display_node( position ); _history_index = 0; } } bool info_viewer:: display_node( const char * literal_node_ref ) { return display_node( node_number( (String)literal_node_ref ) ); } bool info_viewer:: display_node( int node_reference ) { if( node_reference < 0 || node_reference >= _node_table.node_count() ) return true; _node_table[_current_index].viewing_position = viewing_window.page.top() - viewing_window.top(); string_array & node_text = viewing_window.page.text; node_text = get_node_text( _node_table[node_reference] ); if( node_text.height() == 0 ) return true; viewing_window.page.refresh_text(); if( !_time_travelling ) { _history_index++; _history[_history_index] = node_reference; if( _history_index == _history_height ) resize_history( _history_height * 2 ); } _time_travelling = false; _current_index = node_reference; _current_file = _node_table[node_reference].file_name; viewing_window.page.move_to( 0, _node_table[node_reference].viewing_position ); int row, col; ScreenGetCursor( &row, &col ); viewing_window.page.cursor_to( col - viewing_window.page.left(), row - viewing_window.page.top() ); draw_all_windows(); return false; } void info_viewer:: prepare_top_node() { String menu_string = "\n\ File: minfo.inf, Node: Info File List, Next: Top, Prev: Top, \ Up: Top\n\nThese are the Info files Minfo has found on your system.\n\n\ \n* The House of Bint: (minfo.inf)House of Bint.\n\tAdvice for djgpp newbies, \ plus a few djgpp freebies\n\n"; string_array top_node; bool error = top_node.read_file( _info_path + "dir" ); if( !error ) { String big_string = top_node.as_a_big_string(); int entry_start; int next_start = big_string.index( "* Menu" ); hyperlink_info next_hyperlink = next_link( big_string, next_start+1, 0 ); while( next_hyperlink.start != not_found ) { next_start = next_hyperlink.start + 1; if( node_number( next_hyperlink.address ) != not_found ) { entry_start = next_hyperlink.start; next_hyperlink = next_link( big_string, next_start, 0 ); if( next_hyperlink.start != not_found ) { menu_string += big_string.at( entry_start, next_hyperlink.start - entry_start ); } } next_hyperlink = next_link( big_string, next_start, 0 ); } } string_array file_list_node = string_to_array( menu_string ); file_list_node.set_tab_width( 8 ); file_list_node.detab(); file_list_node += ""; if( _extra_file_list.height() > 0 ) { file_list_node += "Also found:"; file_list_node += ""; String next_line; int i = 0; while( i < _extra_file_list.height() ) { next_line = "* (" + _extra_file_list[i] + ")::."; file_list_node += next_line; i++; } file_list_node += ""; file_list_node += ""; } file_list_node.write_file( _info_path + "minfo.i1" ); } bool info_viewer:: select_next_link( int x, int y ) { hyperlink_info hyperlink = hyperlink_after( x, y ); while( node_number( hyperlink.address ) == not_found ) { if( hyperlink.start == not_found ) { hyperlink = hyperlink_after( 0, 0 ); if( hyperlink.start == not_found ) return true; } else hyperlink = hyperlink_after( hyperlink.end_pos.x, hyperlink.end_pos.y ); } viewing_window.hide_cursor(); viewing_window.cursor_to( 0, hyperlink.start_pos.y ); viewing_window.scroll_to_cursor(); viewing_window.cursor_to( hyperlink.end_pos.x, hyperlink.end_pos.y ); viewing_window.scroll_to_cursor(); if( viewing_window.page.top() + hyperlink.end_pos.y == viewing_window.bottom() ) viewing_window.scroll_down( 3 ); colour_hyperlink( _current_hyperlink, viewing_window.colour() ); _current_hyperlink = hyperlink; return colour_hyperlink( hyperlink ); } bool info_viewer:: colour_hyperlink( hyperlink_info hyperlink, int colour ) { if( colour == not_found ) colour = _hyperlink_colour; if( hyperlink.start_pos != point_not_found && hyperlink.end_pos != point_not_found ) { viewing_window.page.colour_from_to( colour, hyperlink.start_pos, hyperlink.end_pos ); return false; } return true; } void info_viewer:: set_colour( int text_colour, int hyperlink_colour ) { viewing_window.set_colour( text_colour ); _hyperlink_colour = hyperlink_colour; } bool info_viewer:: save_node() { text_field save_as; save_as.hide(); save_as.set_title( "Save Node As..." ); save_as.resize( 128, 1 ); save_as.set_value( current_directory() + "node.txt" ); save_as.resize( save_as.value().length() + 8, 1 ); save_as.move_to( 2, 3 ); int event = save_as.take_control(); viewing_window.show_cursor(); if( event == return_key ) return viewing_window.page.text.write_file( save_as.value() ); else return true; } bool info_viewer:: search_node() { text_field search_node; search_node.hide(); search_node.set_title( "Search Node For..." ); search_node.resize( 32, 1 ); search_node.move_to( 2, 3 ); int event = search_node.take_control(); viewing_window.show_cursor(); if( event == return_key ) { point start = { 0, 0 }; return viewing_window.next_match( false, search_node.value(), start ); } else return true; } bool info_viewer:: set_viewing_pos( const char * node_name, int view_pos ) { int node_num = node_number( (String)node_name ); if( node_num != not_found ) { _node_table[node_num].viewing_position = view_pos; return false; } return true; }