2022-01-10 12:59:42 -03:00
/*
* Copyright ( c ) 2022 , Sam Atkins < atkinssj @ serenityos . org >
2022-02-10 16:28:48 -03:00
* Copyright ( c ) 2022 , the SerenityOS developers .
2022-01-10 12:59:42 -03:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include "CharacterMapWidget.h"
2022-01-12 13:12:59 -03:00
# include "CharacterSearchWidget.h"
2022-01-11 17:52:03 -03:00
# include <AK/StringUtils.h>
2022-01-10 12:59:42 -03:00
# include <Applications/CharacterMap/CharacterMapWindowGML.h>
# include <LibConfig/Client.h>
2022-01-12 14:14:46 -03:00
# include <LibDesktop/Launcher.h>
2022-01-10 12:59:42 -03:00
# include <LibGUI/Action.h>
# include <LibGUI/Application.h>
# include <LibGUI/Clipboard.h>
# include <LibGUI/FontPicker.h>
# include <LibGUI/Icon.h>
2022-01-11 17:52:03 -03:00
# include <LibGUI/InputBox.h>
2022-02-13 15:47:55 -03:00
# include <LibGUI/ItemListModel.h>
2022-01-10 12:59:42 -03:00
# include <LibGUI/Label.h>
2022-02-13 15:47:55 -03:00
# include <LibGUI/ListView.h>
2022-01-10 12:59:42 -03:00
# include <LibGUI/Menu.h>
2022-01-11 17:24:11 -03:00
# include <LibGUI/TextBox.h>
2022-01-10 12:59:42 -03:00
# include <LibGUI/Toolbar.h>
# include <LibUnicode/CharacterTypes.h>
CharacterMapWidget : : CharacterMapWidget ( )
{
load_from_gml ( character_map_window_gml ) ;
m_toolbar = find_descendant_of_type_named < GUI : : Toolbar > ( " toolbar " ) ;
m_font_name_label = find_descendant_of_type_named < GUI : : Label > ( " font_name " ) ;
m_glyph_map = find_descendant_of_type_named < GUI : : GlyphMapWidget > ( " glyph_map " ) ;
2022-01-11 17:24:11 -03:00
m_output_box = find_descendant_of_type_named < GUI : : TextBox > ( " output_box " ) ;
m_copy_output_button = find_descendant_of_type_named < GUI : : Button > ( " copy_output_button " ) ;
2022-01-10 12:59:42 -03:00
m_statusbar = find_descendant_of_type_named < GUI : : Statusbar > ( " statusbar " ) ;
2022-02-13 15:47:55 -03:00
m_unicode_block_listview = find_descendant_of_type_named < GUI : : ListView > ( " unicode_block_listview " ) ;
2022-01-10 12:59:42 -03:00
2022-07-11 14:32:29 -03:00
m_choose_font_action = GUI : : Action : : create ( " Choose Font... " , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/app-font-editor.png " sv ) . release_value_but_fixme_should_propagate_errors ( ) , [ & ] ( auto & ) {
2022-01-10 12:59:42 -03:00
auto font_picker = GUI : : FontPicker : : construct ( window ( ) , & font ( ) , false ) ;
2022-05-13 09:10:27 -03:00
if ( font_picker - > exec ( ) = = GUI : : Dialog : : ExecResult : : OK ) {
2022-01-10 12:59:42 -03:00
auto & font = * font_picker - > font ( ) ;
2022-07-11 14:32:29 -03:00
Config : : write_string ( " CharacterMap " sv , " History " sv , " Font " sv , font . qualified_name ( ) ) ;
2022-01-10 12:59:42 -03:00
set_font ( font ) ;
}
} ) ;
m_copy_selection_action = GUI : : CommonActions : : make_copy_action ( [ & ] ( GUI : : Action & ) {
auto selection = m_glyph_map - > selection ( ) ;
StringBuilder builder ;
for ( auto code_point = selection . start ( ) ; code_point < selection . start ( ) + selection . size ( ) ; + + code_point ) {
if ( ! m_glyph_map - > font ( ) . contains_glyph ( code_point ) )
continue ;
builder . append_code_point ( code_point ) ;
}
GUI : : Clipboard : : the ( ) . set_plain_text ( builder . to_string ( ) ) ;
} ) ;
2022-01-11 17:52:03 -03:00
m_copy_selection_action - > set_status_tip ( " Copy the highlighted characters to the clipboard " ) ;
2022-07-11 14:32:29 -03:00
m_previous_glyph_action = GUI : : Action : : create ( " Previous character " , { Mod_Alt , Key_Left } , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/go-back.png " sv ) . release_value_but_fixme_should_propagate_errors ( ) , [ & ] ( auto & ) {
2022-01-11 17:52:03 -03:00
m_glyph_map - > select_previous_existing_glyph ( ) ;
} ) ;
m_previous_glyph_action - > set_status_tip ( " Seek the previous visible glyph " ) ;
2022-07-11 14:32:29 -03:00
m_next_glyph_action = GUI : : Action : : create ( " &Next Glyph " , { Mod_Alt , Key_Right } , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/go-forward.png " sv ) . release_value_but_fixme_should_propagate_errors ( ) , [ & ] ( auto & ) {
2022-01-11 17:52:03 -03:00
m_glyph_map - > select_next_existing_glyph ( ) ;
} ) ;
m_next_glyph_action - > set_status_tip ( " Seek the next visible glyph " ) ;
2022-07-11 14:32:29 -03:00
m_go_to_glyph_action = GUI : : Action : : create ( " Go to glyph... " , { Mod_Ctrl , Key_G } , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/go-to.png " sv ) . release_value_but_fixme_should_propagate_errors ( ) , [ & ] ( auto & ) {
2022-01-11 17:52:03 -03:00
String input ;
2022-07-11 14:32:29 -03:00
if ( GUI : : InputBox : : show ( window ( ) , input , " Hexadecimal: " sv , " Go to glyph " sv ) = = GUI : : InputBox : : ExecResult : : OK & & ! input . is_empty ( ) ) {
2022-01-11 17:52:03 -03:00
auto maybe_code_point = AK : : StringUtils : : convert_to_uint_from_hex ( input ) ;
if ( ! maybe_code_point . has_value ( ) )
return ;
2022-02-13 15:47:55 -03:00
auto code_point = maybe_code_point . value ( ) ;
code_point = clamp ( code_point , m_range . first , m_range . last ) ;
2022-01-11 17:52:03 -03:00
m_glyph_map - > set_focus ( true ) ;
m_glyph_map - > set_active_glyph ( code_point ) ;
m_glyph_map - > scroll_to_glyph ( code_point ) ;
}
} ) ;
m_go_to_glyph_action - > set_status_tip ( " Go to the specified code point " ) ;
2022-01-10 12:59:42 -03:00
2022-07-11 14:32:29 -03:00
m_find_glyphs_action = GUI : : Action : : create ( " &Find glyphs... " , { Mod_Ctrl , Key_F } , Gfx : : Bitmap : : try_load_from_file ( " /res/icons/16x16/find.png " sv ) . release_value_but_fixme_should_propagate_errors ( ) , [ & ] ( auto & ) {
2022-01-12 13:12:59 -03:00
if ( m_find_window . is_null ( ) ) {
m_find_window = GUI : : Window : : construct ( window ( ) ) ;
auto & search_widget = m_find_window - > set_main_widget < CharacterSearchWidget > ( ) ;
search_widget . on_character_selected = [ & ] ( auto code_point ) {
m_glyph_map - > set_active_glyph ( code_point ) ;
m_glyph_map - > scroll_to_glyph ( code_point ) ;
} ;
2022-07-11 14:32:29 -03:00
m_find_window - > set_icon ( GUI : : Icon : : try_create_default_icon ( " find " sv ) . value ( ) . bitmap_for_size ( 16 ) ) ;
2022-01-12 13:12:59 -03:00
m_find_window - > set_title ( " Find a character " ) ;
m_find_window - > resize ( 300 , 400 ) ;
2022-08-22 16:30:52 -03:00
m_find_window - > set_window_mode ( GUI : : WindowMode : : Modeless ) ;
2022-01-12 13:12:59 -03:00
}
m_find_window - > show ( ) ;
m_find_window - > move_to_front ( ) ;
m_find_window - > find_descendant_of_type_named < GUI : : TextBox > ( " search_input " ) - > set_focus ( true ) ;
} ) ;
2022-01-10 12:59:42 -03:00
m_toolbar - > add_action ( * m_choose_font_action ) ;
m_toolbar - > add_separator ( ) ;
m_toolbar - > add_action ( * m_copy_selection_action ) ;
2022-01-11 17:52:03 -03:00
m_toolbar - > add_separator ( ) ;
m_toolbar - > add_action ( * m_previous_glyph_action ) ;
m_toolbar - > add_action ( * m_next_glyph_action ) ;
m_toolbar - > add_action ( * m_go_to_glyph_action ) ;
2022-01-12 13:12:59 -03:00
m_toolbar - > add_action ( * m_find_glyphs_action ) ;
2022-01-10 12:59:42 -03:00
m_glyph_map - > on_active_glyph_changed = [ & ] ( int ) {
update_statusbar ( ) ;
} ;
2022-01-11 17:24:11 -03:00
m_glyph_map - > on_glyph_double_clicked = [ & ] ( int code_point ) {
StringBuilder builder ;
builder . append ( m_output_box - > text ( ) ) ;
builder . append_code_point ( code_point ) ;
m_output_box - > set_text ( builder . string_view ( ) ) ;
} ;
m_copy_output_button - > on_click = [ & ] ( auto ) {
GUI : : Clipboard : : the ( ) . set_plain_text ( m_output_box - > text ( ) ) ;
} ;
2022-02-13 15:47:55 -03:00
auto unicode_blocks = Unicode : : block_display_names ( ) ;
2022-02-26 09:33:32 -03:00
m_unicode_block_listview - > on_selection_change = [ this , unicode_blocks ] {
auto index = m_unicode_block_listview - > selection ( ) . first ( ) ;
2022-02-13 15:47:55 -03:00
if ( index . row ( ) > 0 )
m_range = unicode_blocks [ index . row ( ) - 1 ] . code_point_range ;
else
m_range = { 0x0000 , 0x10FFFF } ;
m_glyph_map - > set_active_range ( m_range ) ;
} ;
m_unicode_block_list . append ( " Show All " ) ;
for ( auto & block : unicode_blocks )
m_unicode_block_list . append ( block . display_name ) ;
m_unicode_block_model = GUI : : ItemListModel < String > : : create ( m_unicode_block_list ) ;
m_unicode_block_listview - > set_model ( * m_unicode_block_model ) ;
m_unicode_block_listview - > set_activates_on_selection ( true ) ;
m_unicode_block_listview - > horizontal_scrollbar ( ) . set_visible ( false ) ;
m_unicode_block_listview - > set_cursor ( m_unicode_block_model - > index ( 0 , 0 ) , GUI : : AbstractView : : SelectionUpdate : : Set ) ;
2022-01-10 12:59:42 -03:00
did_change_font ( ) ;
update_statusbar ( ) ;
}
void CharacterMapWidget : : initialize_menubar ( GUI : : Window & window )
{
auto & file_menu = window . add_menu ( " &File " ) ;
file_menu . add_action ( GUI : : CommonActions : : make_quit_action ( [ ] ( GUI : : Action & ) {
GUI : : Application : : the ( ) - > quit ( ) ;
} ) ) ;
auto & help_menu = window . add_menu ( " &Help " ) ;
2022-01-12 14:14:46 -03:00
help_menu . add_action ( GUI : : CommonActions : : make_help_action ( [ & ] ( auto & ) {
Desktop : : Launcher : : open ( URL : : create_with_file_protocol ( " /usr/share/man/man1/CharacterMap.md " ) , " /bin/Help " ) ;
} ) ) ;
2022-07-11 14:32:29 -03:00
help_menu . add_action ( GUI : : CommonActions : : make_about_action ( " Character Map " , GUI : : Icon : : default_icon ( " app-character-map " sv ) , & window ) ) ;
2022-01-10 12:59:42 -03:00
}
void CharacterMapWidget : : did_change_font ( )
{
m_glyph_map - > set_font ( font ( ) ) ;
2022-01-31 22:54:27 -03:00
m_font_name_label - > set_text ( font ( ) . human_readable_name ( ) ) ;
2022-01-11 17:24:11 -03:00
m_output_box - > set_font ( font ( ) ) ;
2022-01-10 12:59:42 -03:00
}
void CharacterMapWidget : : update_statusbar ( )
{
auto code_point = m_glyph_map - > active_glyph ( ) ;
StringBuilder builder ;
builder . appendff ( " U+{:04X} " , code_point ) ;
if ( auto display_name = Unicode : : code_point_display_name ( code_point ) ; display_name . has_value ( ) )
builder . appendff ( " - {} " , display_name . value ( ) ) ;
m_statusbar - > set_text ( builder . to_string ( ) ) ;
}