From b2fdf78fd9ef46683775014716a2cbf98f11ad8c Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sun, 1 May 2022 00:59:55 +0000 Subject: [PATCH] Implement font selection dialog on Haiku * src/haiku_font_support.cc (font_style_to_flags): Handle style allocation failures. (be_font_style_to_flags): New function. * src/haiku_support.cc (struct font_selection_dialog_message): New struct. (class EmacsFontSelectionDialog): New class. (be_select_font): New function. * src/haiku_support.h: Update prototypes. * src/haikufont.c (Fx_select_font): New function. (syms_of_haikufont): Define new subr. --- src/haiku_font_support.cc | 11 ++ src/haiku_support.cc | 313 +++++++++++++++++++++++++++++++++++++- src/haiku_support.h | 4 + src/haikufont.c | 49 ++++++ 4 files changed, 369 insertions(+), 8 deletions(-) diff --git a/src/haiku_font_support.cc b/src/haiku_font_support.cc index 9acdd652e34..95a0db8ae68 100644 --- a/src/haiku_font_support.cc +++ b/src/haiku_font_support.cc @@ -294,6 +294,9 @@ font_style_to_flags (char *st, struct haiku_font_pattern *pattern) char *token; int tok = 0; + if (!style) + return; + pattern->weight = NO_WEIGHT; pattern->width = NO_WIDTH; pattern->slant = NO_SLANT; @@ -804,3 +807,11 @@ be_evict_font_cache (void) font_object_cache[i] = NULL; } } + +void +be_font_style_to_flags (char *style, struct haiku_font_pattern *pattern) +{ + pattern->specified = 0; + + font_style_to_flags (style, pattern); +} diff --git a/src/haiku_support.cc b/src/haiku_support.cc index 8ad3c58a17e..32b05a499df 100644 --- a/src/haiku_support.cc +++ b/src/haiku_support.cc @@ -38,6 +38,9 @@ along with GNU Emacs. If not, see . */ #include #include #include +#include +#include +#include #include @@ -54,6 +57,7 @@ along with GNU Emacs. If not, see . */ #include #include #include +#include #include #include @@ -84,14 +88,16 @@ along with GNU Emacs. If not, see . */ /* Some messages that Emacs sends to itself. */ enum { - SCROLL_BAR_UPDATE = 3000, - WAIT_FOR_RELEASE = 3001, - RELEASE_NOW = 3002, - CANCEL_DROP = 3003, - SHOW_MENU_BAR = 3004, - BE_MENU_BAR_OPEN = 3005, - QUIT_APPLICATION = 3006, - REPLAY_MENU_BAR = 3007, + SCROLL_BAR_UPDATE = 3000, + WAIT_FOR_RELEASE = 3001, + RELEASE_NOW = 3002, + CANCEL_DROP = 3003, + SHOW_MENU_BAR = 3004, + BE_MENU_BAR_OPEN = 3005, + QUIT_APPLICATION = 3006, + REPLAY_MENU_BAR = 3007, + FONT_FAMILY_SELECTED = 3008, + FONT_STYLE_SELECTED = 3009, }; /* X11 keysyms that we use. */ @@ -122,6 +128,18 @@ enum KEY_ZENKAKU_HANKAKU = 0xff2a, }; +struct font_selection_dialog_message +{ + /* Whether or not font selection was cancelled. */ + bool cancel; + + /* The index of the selected font family. */ + int family_idx; + + /* The index of the selected font style. */ + int style_idx; +}; + static color_space dpy_color_space = B_NO_COLOR_SPACE; static key_map *key_map = NULL; static char *key_chars = NULL; @@ -2408,6 +2426,243 @@ public: } }; +class EmacsFontSelectionDialog : public BWindow +{ + BSplitView split_view; + BListView font_family_pane; + BListView font_style_pane; + BObjectList all_families; + BObjectList all_styles; + BButton cancel_button, ok_button; + port_id comm_port; + + void + UpdateStylesForIndex (int idx) + { + int n, i; + uint32 flags; + font_family family; + font_style style; + BStringItem *item; + + n = all_styles.CountItems (); + + font_style_pane.MakeEmpty (); + all_styles.MakeEmpty (); + + if (get_font_family (idx, &family, &flags) == B_OK) + { + n = count_font_styles (family); + + for (i = 0; i < n; ++i) + { + if (get_font_style (family, i, &style, &flags) == B_OK) + item = new BStringItem (style); + else + item = new BStringItem (""); + + font_style_pane.AddItem (item); + all_styles.AddItem (item); + } + } + + UpdateForSelectedStyle (); + } + + bool + QuitRequested (void) + { + struct font_selection_dialog_message rq; + + rq.cancel = true; + write_port (comm_port, 0, &rq, sizeof rq); + + return false; + } + + void + UpdateForSelectedStyle (void) + { + if (font_style_pane.CurrentSelection () < 0) + ok_button.SetEnabled (false); + else + ok_button.SetEnabled (true); + } + + void + MessageReceived (BMessage *msg) + { + int idx; + struct font_selection_dialog_message rq; + + if (msg->what == FONT_FAMILY_SELECTED) + { + idx = font_family_pane.CurrentSelection (); + UpdateStylesForIndex (idx); + } + else if (msg->what == FONT_STYLE_SELECTED) + UpdateForSelectedStyle (); + else if (msg->what == B_OK) + { + rq.cancel = false; + rq.family_idx = font_family_pane.CurrentSelection (); + rq.style_idx = font_style_pane.CurrentSelection (); + + write_port (comm_port, 0, &rq, sizeof rq); + } + else if (msg->what == B_CANCEL) + { + rq.cancel = true; + + write_port (comm_port, 0, &rq, sizeof rq); + } + + BWindow::MessageReceived (msg); + } + +public: + + ~EmacsFontSelectionDialog (void) + { + font_family_pane.MakeEmpty (); + font_style_pane.MakeEmpty (); + + split_view.RemoveSelf (); + cancel_button.RemoveSelf (); + ok_button.RemoveSelf (); + + if (comm_port >= B_OK) + delete_port (comm_port); + } + + EmacsFontSelectionDialog (void) : BWindow (BRect (0, 0, 300, 300), "", + B_TITLED_WINDOW_LOOK, + B_NORMAL_WINDOW_FEEL, 0), + all_families (20, true), + all_styles (20, true), + cancel_button ("Cancel", "Cancel", + new BMessage (B_CANCEL)), + ok_button ("OK", "OK", new BMessage (B_OK)) + { + BStringItem *family_item; + int i, n_families; + font_family name; + uint32 flags; + BMessage *selection; + + AddChild (&split_view); + AddChild (&cancel_button); + AddChild (&ok_button); + split_view.AddChild (&font_family_pane); + split_view.AddChild (&font_style_pane); + + FrameResized (801, 801); + UpdateForSelectedStyle (); + + selection = new BMessage (FONT_FAMILY_SELECTED); + font_family_pane.SetSelectionMessage (selection); + selection = new BMessage (FONT_STYLE_SELECTED); + font_style_pane.SetSelectionMessage (selection); + + comm_port = create_port (1, "font dialog port"); + + n_families = count_font_families (); + + for (i = 0; i < n_families; ++i) + { + if (get_font_family (i, &name, &flags) == B_OK) + { + family_item = new BStringItem (name); + + all_families.AddItem (family_item); + font_family_pane.AddItem (family_item); + } + else + { + family_item = new BStringItem (""); + + all_families.AddItem (family_item); + font_family_pane.AddItem (family_item); + } + } + } + + void + FrameResized (float new_width, float new_height) + { + BRect frame = Frame (); + float ok_height, ok_width; + float cancel_height, cancel_width; + int max_height; + + ok_button.GetPreferredSize (&ok_width, &ok_height); + cancel_button.GetPreferredSize (&cancel_width, + &cancel_height); + + max_height = std::max (ok_height, cancel_height); + + split_view.ResizeTo (BE_RECT_WIDTH (frame), + BE_RECT_HEIGHT (frame) - 4 - max_height); + ok_button.MoveTo ((BE_RECT_WIDTH (frame) + - 4 - cancel_width - ok_width), + BE_RECT_HEIGHT (frame) - 2 - max_height); + cancel_button.MoveTo (BE_RECT_WIDTH (frame) - 2 - cancel_width, + BE_RECT_HEIGHT (frame) - 2 - max_height); + ok_button.ResizeTo (ok_width, ok_height); + cancel_button.ResizeTo (cancel_width, cancel_height); + } + + void + WaitForChoice (struct font_selection_dialog_message *msg, + void (*process_pending_signals_function) (void)) + { + int32 reply_type; + struct object_wait_info infos[2]; + ssize_t status; + + infos[0].object = port_application_to_emacs; + infos[0].type = B_OBJECT_TYPE_PORT; + infos[0].events = B_EVENT_READ; + + infos[1].object = comm_port; + infos[1].type = B_OBJECT_TYPE_PORT; + infos[1].events = B_EVENT_READ; + + while (true) + { + status = wait_for_objects (infos, 2); + + if (status < B_OK) + continue; + + if (infos[1].events & B_EVENT_READ) + { + if (read_port (comm_port, &reply_type, + msg, sizeof *msg) >= B_OK) + return; + + goto cancel; + } + + if (infos[0].events & B_EVENT_READ) + process_pending_signals_function (); + + infos[0].events = B_EVENT_READ; + infos[1].events = B_EVENT_READ; + } + + cancel: + msg->cancel = true; + return; + } + + status_t + InitCheck (void) + { + return comm_port >= B_OK ? B_OK : comm_port; + } +}; + static int32 start_running_application (void *data) { @@ -4370,3 +4625,45 @@ be_get_ui_color (const char *name, uint32_t *color) return 0; } + +bool +be_select_font (void (*process_pending_signals_function) (void), + haiku_font_family_or_style *family, + haiku_font_family_or_style *style) +{ + EmacsFontSelectionDialog *dialog; + struct font_selection_dialog_message msg; + uint32 flags; + font_family family_buffer; + font_style style_buffer; + + dialog = new EmacsFontSelectionDialog; + dialog->CenterOnScreen (); + + if (dialog->InitCheck () < B_OK) + { + dialog->Quit (); + return false; + } + + dialog->Show (); + dialog->WaitForChoice (&msg, process_pending_signals_function); + + if (!dialog->LockLooper ()) + gui_abort ("Failed to lock font selection dialog looper"); + dialog->Quit (); + + if (msg.cancel) + return false; + + if (get_font_family (msg.family_idx, + &family_buffer, &flags) != B_OK + || get_font_style (family_buffer, msg.style_idx, + &style_buffer, &flags) != B_OK) + return false; + + memcpy (family, family_buffer, sizeof *family); + memcpy (style, style_buffer, sizeof *style); + + return true; +} diff --git a/src/haiku_support.h b/src/haiku_support.h index 88edc1ae149..faaee2ed9de 100644 --- a/src/haiku_support.h +++ b/src/haiku_support.h @@ -648,6 +648,7 @@ extern int be_get_display_screens (void); extern bool be_use_subpixel_antialiasing (void); extern const char *be_find_setting (const char *); extern haiku_font_family_or_style *be_list_font_families (size_t *); +extern void be_font_style_to_flags (char *, struct haiku_font_pattern *); extern int be_get_ui_color (const char *, uint32_t *); extern void BMessage_delete (void *); @@ -658,6 +659,9 @@ extern bool be_drag_message (void *, void *, bool, void (*) (void), extern bool be_drag_and_drop_in_progress (void); extern bool be_replay_menu_bar_event (void *, struct haiku_menu_bar_click_event *); +extern bool be_select_font (void (*process_pending_signals_function) (void), + haiku_font_family_or_style *, + haiku_font_family_or_style *); #ifdef __cplusplus } diff --git a/src/haikufont.c b/src/haikufont.c index 7dd23fba7aa..bca29c4ed38 100644 --- a/src/haikufont.c +++ b/src/haikufont.c @@ -1082,6 +1082,53 @@ struct font_driver const haikufont_driver = .list_family = haikufont_list_family }; +DEFUN ("x-select-font", Fx_select_font, Sx_select_font, 0, 2, 0, + doc: /* Read a font using a native dialog. +Return a font spec describing the font chosen by the user. + +FRAME is the frame on which to pop up the font chooser. If omitted or +nil, it defaults to the selected frame. +If EXCLUDE-PROPORTIONAL is non-nil, exclude proportional fonts +in the font selection dialog. */) + (Lisp_Object frame, Lisp_Object exclude_proportional) +{ + haiku_font_family_or_style family, style; + bool rc; + struct haiku_font_pattern pattern; + Lisp_Object lfamily, lweight, lslant, lwidth, ladstyle; + + decode_window_system_frame (frame); + + if (popup_activated_p) + error ("Trying to use a menu from within a menu-entry"); + + popup_activated_p++; + rc = be_select_font (process_pending_signals, &family, &style); + popup_activated_p--; + + if (!rc) + quit (); + + be_font_style_to_flags (style, &pattern); + + lfamily = build_string_from_utf8 (family); + lweight = (pattern.specified & FSPEC_WEIGHT + ? haikufont_weight_to_lisp (pattern.weight) + : Qunspecified); + lslant = (pattern.specified & FSPEC_SLANT + ? haikufont_slant_to_lisp (pattern.slant) + : Qunspecified); + lwidth = (pattern.specified & FSPEC_WIDTH + ? haikufont_width_to_lisp (pattern.width) + : Qunspecified); + ladstyle = (pattern.specified & FSPEC_STYLE + ? intern (pattern.style) : Qunspecified); + + return CALLN (Ffont_spec, QCfamily, lfamily, + QCweight, lweight, QCslant, lslant, + QCwidth, lwidth, QCadstyle, ladstyle); +} + void syms_of_haikufont (void) { @@ -1112,5 +1159,7 @@ syms_of_haikufont (void) font_cache = list (Qnil); staticpro (&font_cache); + defsubr (&Sx_select_font); + be_init_font_data (); } -- 2.39.2