diff options
Diffstat (limited to 'src/gui_photon.c')
-rw-r--r-- | src/gui_photon.c | 3060 |
1 files changed, 3060 insertions, 0 deletions
diff --git a/src/gui_photon.c b/src/gui_photon.c new file mode 100644 index 000000000..f2bd5f09b --- /dev/null +++ b/src/gui_photon.c @@ -0,0 +1,3060 @@ +/* vi:set ts=8 sw=4 sts=4: + * + * VIM - Vi IMproved by Bram Moolenaar + * Photon GUI support by Julian Kinraid + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * + * + * Clipboard support is in os_qnx.c + * PhAttach() is called in os_qnx.c:qnx_init() + */ + +#include "vim.h" + +#ifdef FEAT_TOOLBAR +# include <photon/PxImage.h> +#endif + +#if !defined(__QNX__) +/* Used when generating prototypes. */ +# define PgColor_t int +# define PhEvent_t int +# define PhPoint_t int +# define PtWidget_t int +# define Pg_BLACK 0 +# define PtCallbackF_t int +# define PtCallbackInfo_t int +# define PhTile_t int +# define PtWidget_t int +# define PhImage_t int +#endif + +#define ARRAY_LENGTH(a) (sizeof(a) / sizeof(a[0])) +#define RGB(r,g,b) PgRGB(r,g,b) + +#define EVENT_BUFFER_SIZE sizeof( PhEvent_t ) + 1000 + +/* Some defines for gui_mch_mousehide() */ +#define MOUSE_HIDE TRUE +#define MOUSE_SHOW FALSE + +/* Optional support for using a PtPanelGroup widget, needs work */ +#undef USE_PANEL_GROUP + +#ifdef USE_PANEL_GROUP +static char *empty_title = " "; +static char **panel_titles = NULL; +static ushort_t num_panels = 0; +static short pg_margin_left, pg_margin_right, pg_margin_top, pg_margin_bottom; +#endif + +#define GUI_PH_MARGIN 4 /* Size of the bevel */ + +#define GUI_PH_MOUSE_TYPE Ph_CURSOR_INSERT +static PgColor_t gui_ph_mouse_color = Pg_BLACK; + +static PhPoint_t gui_ph_raw_offset; +static PtWidget_t *gui_ph_timer_cursor; /* handle cursor blinking */ +static PtWidget_t *gui_ph_timer_timeout; /* used in gui_mch_wait_for_chars */ +static short is_timeout; /* Has the timeout occured? */ + +/* + * This is set inside the mouse callback for a right mouse + * button click, and used for the popup menus + */ +static PhPoint_t abs_mouse; + +/* Try and avoid redraws while a resize is in progress */ +static int is_ignore_draw = FALSE; + +/* Used for converting to/from utf-8 and other charsets */ +static struct PxTransCtrl *charset_translate; + +/* + * Cursor blink functions. + * + * This is a simple state machine: + * BLINK_NONE not blinking at all + * BLINK_OFF blinking, cursor is not shown + * BLINK_ON blinking, cursor is shown + */ +static enum { + BLINK_NONE, + BLINK_OFF, + BLINK_ON +} blink_state = BLINK_NONE; + +static long_u blink_waittime = 700; +static long_u blink_ontime = 400; +static long_u blink_offtime = 250; + +static struct +{ + int key_sym; + char_u vim_code0; + char_u vim_code1; +} special_keys[] = +{ + {Pk_Up, 'k', 'u'}, + {Pk_Down, 'k', 'd'}, + {Pk_Left, 'k', 'l'}, + {Pk_Right, 'k', 'r'}, + + {Pk_F1, 'k', '1'}, + {Pk_F2, 'k', '2'}, + {Pk_F3, 'k', '3'}, + {Pk_F4, 'k', '4'}, + {Pk_F5, 'k', '5'}, + {Pk_F6, 'k', '6'}, + {Pk_F7, 'k', '7'}, + {Pk_F8, 'k', '8'}, + {Pk_F9, 'k', '9'}, + {Pk_F10, 'k', ';'}, + + {Pk_F11, 'F', '1'}, + {Pk_F12, 'F', '2'}, + {Pk_F13, 'F', '3'}, + {Pk_F14, 'F', '4'}, + {Pk_F15, 'F', '5'}, + {Pk_F16, 'F', '6'}, + {Pk_F17, 'F', '7'}, + {Pk_F18, 'F', '8'}, + {Pk_F19, 'F', '9'}, + {Pk_F20, 'F', 'A'}, + + {Pk_F21, 'F', 'B'}, + {Pk_F22, 'F', 'C'}, + {Pk_F23, 'F', 'D'}, + {Pk_F24, 'F', 'E'}, + {Pk_F25, 'F', 'F'}, + {Pk_F26, 'F', 'G'}, + {Pk_F27, 'F', 'H'}, + {Pk_F28, 'F', 'I'}, + {Pk_F29, 'F', 'J'}, + + {Pk_F30, 'F', 'K'}, + {Pk_F31, 'F', 'L'}, + {Pk_F32, 'F', 'M'}, + {Pk_F33, 'F', 'N'}, + {Pk_F34, 'F', 'O'}, + {Pk_F35, 'F', 'P'}, + + {Pk_Help, '%', '1'}, + {Pk_BackSpace, 'k', 'b'}, + {Pk_Insert, 'k', 'I'}, + {Pk_Delete, 'k', 'D'}, + {Pk_Home, 'k', 'h'}, + {Pk_End, '@', '7'}, + {Pk_Prior, 'k', 'P'}, + {Pk_Next, 'k', 'N'}, + {Pk_Print, '%', '9'}, + + {Pk_KP_Add, 'K', '6'}, + {Pk_KP_Subtract,'K', '7'}, + {Pk_KP_Divide, 'K', '8'}, + {Pk_KP_Multiply,'K', '9'}, + {Pk_KP_Enter, 'K', 'A'}, + + {Pk_KP_0, KS_EXTRA, KE_KINS}, /* Insert */ + {Pk_KP_Decimal, KS_EXTRA, KE_KDEL}, /* Delete */ + + {Pk_KP_4, 'k', 'l'}, /* Left */ + {Pk_KP_6, 'k', 'r'}, /* Right */ + {Pk_KP_8, 'k', 'u'}, /* Up */ + {Pk_KP_2, 'k', 'd'}, /* Down */ + + {Pk_KP_7, 'K', '1'}, /* Home */ + {Pk_KP_1, 'K', '4'}, /* End */ + + {Pk_KP_9, 'K', '3'}, /* Page Up */ + {Pk_KP_3, 'K', '5'}, /* Page Down */ + + {Pk_KP_5, '&', '8'}, /* Undo */ + + /* Keys that we want to be able to use any modifier with: */ + {Pk_Return, CAR, NUL}, + {Pk_space, ' ', NUL}, + {Pk_Tab, TAB, NUL}, + {Pk_Escape, ESC, NUL}, + {NL, NL, NUL}, + {CAR, CAR, NUL}, + + /* End of list marker: */ + {0, 0, 0} +}; + + +/****************************************************************************/ + +static PtCallbackF_t gui_ph_handle_timer_cursor; +static PtCallbackF_t gui_ph_handle_timer_timeout; + +static PtCallbackF_t gui_ph_handle_window_cb; + +static PtCallbackF_t gui_ph_handle_scrollbar; +static PtCallbackF_t gui_ph_handle_keyboard; +static PtCallbackF_t gui_ph_handle_mouse; +static PtCallbackF_t gui_ph_handle_pulldown_menu; +static PtCallbackF_t gui_ph_handle_menu; +static PtCallbackF_t gui_ph_handle_focus; /* focus change of text area */ + +static PtCallbackF_t gui_ph_handle_menu_resize; + +/* When a menu is unrealized, give focus back to vimTextArea */ +static PtCallbackF_t gui_ph_handle_menu_unrealized; + +#ifdef USE_PANEL_GROUP +static void gui_ph_get_panelgroup_margins( short*, short*, short*, short* ); +#endif + +#ifdef FEAT_TOOLBAR +static PhImage_t *gui_ph_toolbar_find_icon( vimmenu_T *menu ); +#endif + +static void gui_ph_draw_start( void ); +static void gui_ph_draw_end( void ); + +/* Set the text for the balloon */ +static PtWidget_t * gui_ph_show_tooltip( PtWidget_t *window, + PtWidget_t *widget, + int position, + char *text, + char *font, + PgColor_t fill_color, + PgColor_t text_color ); + +/****************************************************************************/ + +static PtWidget_t * gui_ph_show_tooltip( PtWidget_t *window, + PtWidget_t *widget, + int position, + char *text, + char *font, + PgColor_t fill_color, + PgColor_t text_color ) +{ + PtArg_t arg; + vimmenu_T *menu; + char_u *tooltip; + + PtSetArg( &arg, Pt_ARG_POINTER, &menu, 0 ); + PtGetResources( widget, 1, &arg ); + + /* Override the text and position */ + + tooltip = text; + if( menu != NULL ) + { + int index = MENU_INDEX_TIP; + if( menu->strings[ index ] != NULL ) + tooltip = menu->strings[ index ]; + } + + return( PtInflateBalloon( + window, + widget, + /* Don't put the balloon at the bottom, + * it gets drawn over by gfx done in the PtRaw */ + Pt_BALLOON_TOP, + tooltip, + font, + fill_color, + text_color ) ); +} + + static void +gui_ph_resize_container( void ) +{ + PhArea_t area; + + PtWidgetArea( gui.vimWindow, &area ); + PtWidgetPos ( gui.vimContainer, &area.pos ); + + PtSetResource( gui.vimContainer, Pt_ARG_AREA, &area, 0 ); +} + + static int +gui_ph_handle_menu_resize( + PtWidget_t *widget, + void *other, + PtCallbackInfo_t *info ) +{ + PtContainerCallback_t *sizes = info->cbdata; + PtWidget_t *container; + PhPoint_t below_menu; + int_u height; + + height = sizes->new_dim.h; + + /* Because vim treats the toolbar and menubar separatly, + * and here they're lumped together into a PtToolbarGroup, + * we only need either menu_height or toolbar_height set at once */ + if( gui.menu_is_active ) + { + gui.menu_height = height; + gui.toolbar_height = 0; + } +#ifdef FEAT_TOOLBAR + else + gui.toolbar_height = height; +#endif + + below_menu.x = 0; + below_menu.y = height; + +#ifdef USE_PANEL_GROUP + container = gui.vimPanelGroup; +#else + container = gui.vimContainer; +#endif + + PtSetResource( container, Pt_ARG_POS, &below_menu, 0 ); + + gui_ph_resize_container(); + +#ifdef USE_PANEL_GROUP + gui_ph_get_panelgroup_margins( + &pg_margin_top, &pg_margin_bottom, + &pg_margin_left, &pg_margin_right ); +#endif + return( Pt_CONTINUE ); +} + +/* + * Pt_ARG_TIMER_REPEAT isn't used because the on & off times + * are different + */ + static int +gui_ph_handle_timer_cursor( + PtWidget_t *widget, + void *data, + PtCallbackInfo_t *info ) +{ + if( blink_state == BLINK_ON ) + { + gui_undraw_cursor(); + blink_state = BLINK_OFF; + PtSetResource( gui_ph_timer_cursor, Pt_ARG_TIMER_INITIAL, + blink_offtime, 0 ); + } + else + { + gui_update_cursor(TRUE, FALSE); + blink_state = BLINK_ON; + PtSetResource( gui_ph_timer_cursor, Pt_ARG_TIMER_INITIAL, + blink_ontime, 0 ); + } + return( Pt_CONTINUE ); +} + + static int +gui_ph_handle_timer_timeout(PtWidget_t *widget, void *data, PtCallbackInfo_t *info) +{ + is_timeout = TRUE; + + return( Pt_CONTINUE ); +} + + static int +gui_ph_handle_window_cb( PtWidget_t *widget, void *data, PtCallbackInfo_t *info ) +{ + PhWindowEvent_t *we = info->cbdata; + ushort_t *width, *height; + + switch( we->event_f ) { + case Ph_WM_CLOSE: + gui_shell_closed(); + break; + + case Ph_WM_FOCUS: + /* Just in case it's hidden and needs to be shown */ + gui_mch_mousehide( MOUSE_SHOW ); + + if( we->event_state == Ph_WM_EVSTATE_FOCUS ) + { + gui_focus_change(TRUE); + gui_mch_start_blink(); + } + else + { + gui_focus_change(FALSE); + gui_mch_stop_blink(); + } + break; + + case Ph_WM_RESIZE: + PtGetResource( gui.vimWindow, Pt_ARG_WIDTH, &width, 0 ); + PtGetResource( gui.vimWindow, Pt_ARG_HEIGHT, &height, 0 ); +#ifdef USE_PANEL_GROUP + width -= (pg_margin_left + pg_margin_right); + height -= (pg_margin_top + pg_margin_bottom); +#endif + gui_resize_shell( *width, *height ); + gui_set_shellsize( FALSE, FALSE ); + is_ignore_draw = FALSE; + PtEndFlux( gui.vimContainer ); + PtContainerRelease( gui.vimContainer ); + break; + + default: + break; + } + + return( Pt_CONTINUE ); +} + + static int +gui_ph_handle_scrollbar( PtWidget_t *widget, void *data, PtCallbackInfo_t *info ) +{ + PtScrollbarCallback_t *scroll; + scrollbar_T *sb; + int value, dragging = FALSE; + + scroll = info->cbdata; + + sb = (scrollbar_T *) data; + if( sb != NULL ) + { + value = scroll->position; + switch( scroll->action ) + { + case Pt_SCROLL_DRAGGED: + dragging = TRUE; + break; + + case Pt_SCROLL_SET: + /* FIXME: return straight away here? */ + return( Pt_CONTINUE ); + break; + } + + gui_drag_scrollbar(sb, value, dragging); + } + return( Pt_CONTINUE ); +} + + static int +gui_ph_handle_keyboard( PtWidget_t *widget, void *data, PtCallbackInfo_t *info ) +{ + PhKeyEvent_t *key; + unsigned char string[6]; + int len, i; + int ch, modifiers; + + key = PhGetData( info->event ); + + ch = modifiers = len = 0; + + if( p_mh ) + gui_mch_mousehide( MOUSE_HIDE ); + + /* We're a good lil photon program, aren't we? yes we are, yeess wee arrr */ + if( key->key_flags & Pk_KF_Compose ) + { + return( Pt_CONTINUE ); + } + + if( (key->key_flags & Pk_KF_Cap_Valid) && + PkIsKeyDown( key->key_flags ) ) + { +#ifdef FEAT_MENU + /* + * Only show the menu if the Alt key is down, and the Shift & Ctrl + * keys aren't down, as well as the other conditions + */ + if( ( ( key->key_mods & Pk_KM_Alt ) && + !( key->key_mods & Pk_KM_Shift ) && + !( key->key_mods & Pk_KM_Ctrl ) ) && + gui.menu_is_active && + ( *p_wak == 'y' || + ( *p_wak == 'm' && + gui_is_menu_shortcut( key->key_cap ) ) ) ) + { + /* Fallthrough and let photon look for the hotkey */ + return( Pt_CONTINUE ); + } +#endif + + for( i = 0; special_keys[i].key_sym != 0; i++ ) + { + if( special_keys[i].key_sym == key->key_cap ) + { + len = 0; + if( special_keys[i].vim_code1 == NUL ) + ch = special_keys[i].vim_code0; + else + { + /* Detect if a keypad number key has been pressed + * and change the key if Num Lock is on */ + if( key->key_cap >= Pk_KP_Enter && key->key_cap <= Pk_KP_9 + && ( key->key_mods & Pk_KM_Num_Lock ) ) + { + /* FIXME: For now, just map the key to a ascii value + * (see <photon/PkKeyDef.h>) */ + ch = key->key_cap - 0xf080; + } + else + ch = TO_SPECIAL( special_keys[i].vim_code0, + special_keys[i].vim_code1 ); + } + break; + } + } + + if( key->key_mods & Pk_KM_Ctrl ) + modifiers |= MOD_MASK_CTRL; + if( key->key_mods & Pk_KM_Alt ) + modifiers |= MOD_MASK_ALT; + if( key->key_mods & Pk_KM_Shift ) + modifiers |= MOD_MASK_SHIFT; + + /* Is this not a special key? */ + if( special_keys[i].key_sym == 0 ) + { + ch = PhTo8859_1( key ); + if( ch == -1 +#ifdef FEAT_MBYTE + || ( enc_utf8 && ch > 127 ) +#endif + ) + { +#ifdef FEAT_MBYTE + len = PhKeyToMb( string, key ); + if( len > 0 ) + { + static char buf[6]; + int src_taken, dst_made; + if( enc_utf8 != TRUE ) + { + PxTranslateFromUTF( + charset_translate, + string, + len, + &src_taken, + buf, + 6, + &dst_made ); + + add_to_input_buf( buf, dst_made ); + } + else + { + add_to_input_buf( string, len ); + } + + return( Pt_CONSUME ); + } + len = 0; +#endif + ch = key->key_cap; + if( ch < 0xff ) + { + /* FIXME: is this the right thing to do? */ + if( modifiers & MOD_MASK_CTRL ) + { + modifiers &= ~MOD_MASK_CTRL; + + if( ( ch >= 'a' && ch <= 'z' ) || + ch == '[' || + ch == ']' || + ch == '\\' ) + ch = Ctrl_chr( ch ); + else if( ch == '2' ) + ch = NUL; + else if( ch == '6' ) + ch = 0x1e; + else if( ch == '-' ) + ch = 0x1f; + else + modifiers |= MOD_MASK_CTRL; + } + + if( modifiers & MOD_MASK_ALT ) + { + ch = Meta( ch ); + modifiers &= ~MOD_MASK_ALT; + } + } + else + { + return( Pt_CONTINUE ); + } + } + else + modifiers &= ~MOD_MASK_SHIFT; + } + + ch = simplify_key( ch, &modifiers ); + if( modifiers ) + { + string[ len++ ] = CSI; + string[ len++ ] = KS_MODIFIER; + string[ len++ ] = modifiers; + } + + if( IS_SPECIAL( ch ) ) + { + string[ len++ ] = CSI; + string[ len++ ] = K_SECOND( ch ); + string[ len++ ] = K_THIRD( ch ); + } + else + { + string[ len++ ] = ch; + } + + if (len == 1 && ((ch == Ctrl_C && ctrl_c_interrupts) + || ch == intr_char)) + { + trash_input_buf(); + got_int = TRUE; + } + + if (len == 1 && string[0] == CSI) + { + /* Turn CSI into K_CSI. */ + string[ len++ ] = KS_EXTRA; + string[ len++ ] = KE_CSI; + } + + if( len > 0 ) + { + add_to_input_buf( string, len ); + return( Pt_CONSUME ); + } + } + + return( Pt_CONTINUE ); +} + + static int +gui_ph_handle_mouse( PtWidget_t *widget, void *data, PtCallbackInfo_t *info ) +{ + PhPointerEvent_t *pointer; + PhRect_t *pos; + int button = 0, repeated_click, modifiers = 0x0; + short mouse_x, mouse_y; + + pointer = PhGetData( info->event ); + pos = PhGetRects( info->event ); + + gui_mch_mousehide( MOUSE_SHOW ); + + /* + * Coordinates need to be relative to the base window, + * not relative to the vimTextArea widget + */ + mouse_x = pos->ul.x + gui.border_width; + mouse_y = pos->ul.y + gui.border_width; + + if( info->event->type == Ph_EV_PTR_MOTION_NOBUTTON ) + { + gui_mouse_moved( mouse_x, mouse_y ); + return( Pt_CONTINUE ); + } + + if( pointer->key_mods & Pk_KM_Shift ) + modifiers |= MOUSE_SHIFT; + if( pointer->key_mods & Pk_KM_Ctrl ) + modifiers |= MOUSE_CTRL; + if( pointer->key_mods & Pk_KM_Alt ) + modifiers |= MOUSE_ALT; + + /* + * FIXME More than one button may be involved, but for + * now just deal with one + */ + if( pointer->buttons & Ph_BUTTON_SELECT ) + button = MOUSE_LEFT; + + if( pointer->buttons & Ph_BUTTON_MENU ) + { + button = MOUSE_RIGHT; + /* Need the absolute coordinates for the popup menu */ + abs_mouse.x = pointer->pos.x; + abs_mouse.y = pointer->pos.y; + } + + if( pointer->buttons & Ph_BUTTON_ADJUST ) + button = MOUSE_MIDDLE; + + /* Catch a real release (not phantom or other releases */ + if( info->event->type == Ph_EV_BUT_RELEASE ) + button = MOUSE_RELEASE; + + if( info->event->type & Ph_EV_PTR_MOTION_BUTTON ) + button = MOUSE_DRAG; + +#if 0 + /* Vim doesn't use button repeats */ + if( info->event->type & Ph_EV_BUT_REPEAT ) + button = MOUSE_DRAG; +#endif + + /* Don't do anything if it is one of the phantom mouse release events */ + if( ( button != MOUSE_RELEASE ) || + ( info->event->subtype == Ph_EV_RELEASE_REAL ) ) + { + repeated_click = (pointer->click_count >= 2) ? TRUE : FALSE; + + gui_send_mouse_event( button , mouse_x, mouse_y, repeated_click, modifiers ); + } + + return( Pt_CONTINUE ); +} + +/* Handle a focus change of the PtRaw widget */ + static int +gui_ph_handle_focus( PtWidget_t *widget, void *data, PtCallbackInfo_t *info ) +{ + if( info->reason == Pt_CB_LOST_FOCUS ) + { + PtRemoveEventHandler( gui.vimTextArea, Ph_EV_PTR_MOTION_NOBUTTON, + gui_ph_handle_mouse, NULL ); + + gui_mch_mousehide( MOUSE_SHOW ); + } + else + { + PtAddEventHandler( gui.vimTextArea, Ph_EV_PTR_MOTION_NOBUTTON, + gui_ph_handle_mouse, NULL ); + } + return( Pt_CONTINUE ); +} + + static void +gui_ph_handle_raw_draw( PtWidget_t *widget, PhTile_t *damage ) +{ + PhRect_t *r; + PhPoint_t offset; + PhPoint_t translation; + + if( is_ignore_draw == TRUE ) + return; + + PtSuperClassDraw( PtBasic, widget, damage ); + PgGetTranslation( &translation ); + PgClearTranslation(); + +#if 0 + /* + * This causes some wierd probems, with drawing being done from + * within this raw drawing function (rather than just simple clearing + * and text drawing done by gui_redraw) + * + * The main problem is when PhBlit is used, and the cursor appearing + * in places where it shouldn't + */ + out_flush(); +#endif + + PtWidgetOffset( widget, &offset ); + PhTranslatePoint( &offset, PtWidgetPos( gui.vimTextArea, NULL ) ); + +#if 1 + /* Redraw individual damage regions */ + if( damage->next != NULL ) + damage = damage->next; + + while( damage != NULL ) + { + r = &damage->rect; + gui_redraw( + r->ul.x - offset.x, r->ul.y - offset.y, + r->lr.x - r->ul.x + 1, + r->lr.y - r->ul.y + 1 ); + damage = damage->next; + } +#else + /* Redraw the rectangle that covers all the damaged regions */ + r = &damage->rect; + gui_redraw( + r->ul.x - offset.x, r->ul.y - offset.y, + r->lr.x - r->ul.x + 1, + r->lr.y - r->ul.y + 1 ); +#endif + + PgSetTranslation( &translation, 0 ); +} + + static int +gui_ph_handle_pulldown_menu( + PtWidget_t *widget, + void *data, + PtCallbackInfo_t *info ) +{ + if( data != NULL ) + { + vimmenu_T *menu = (vimmenu_T *) data; + + PtPositionMenu( menu->submenu_id, NULL ); + PtRealizeWidget( menu->submenu_id ); + } + + return( Pt_CONTINUE ); +} + +/* This is used for pulldown/popup menus and also toolbar buttons */ + static int +gui_ph_handle_menu( PtWidget_t *widget, void *data, PtCallbackInfo_t *info ) +{ + if( data != NULL ) + { + vimmenu_T *menu = (vimmenu_T *) data; + gui_menu_cb( menu ); + } + return( Pt_CONTINUE ); +} + +/* Stop focus from disappearing into the menubar... */ + static int +gui_ph_handle_menu_unrealized( + PtWidget_t *widget, + void *data, + PtCallbackInfo_t *info ) +{ + PtGiveFocus( gui.vimTextArea, NULL ); + return( Pt_CONTINUE ); +} + + static int +gui_ph_handle_window_open( + PtWidget_t *widget, + void *data, + PtCallbackInfo_t *info ) +{ + gui_set_shellsize( FALSE, TRUE ); + return( Pt_CONTINUE ); +} + +/****************************************************************************/ + +#define DRAW_START gui_ph_draw_start() +#define DRAW_END gui_ph_draw_end() + +/* TODO: Set a clipping rect? */ + static void +gui_ph_draw_start( void ) +{ + PgSetRegion( PtWidgetRid( PtFindDisjoint( gui.vimTextArea ) ) ); + + PtWidgetOffset( gui.vimTextArea, &gui_ph_raw_offset ); + PhTranslatePoint( &gui_ph_raw_offset, PtWidgetPos( gui.vimTextArea, NULL ) ); + + PgSetTranslation( &gui_ph_raw_offset, Pg_RELATIVE ); +} + + static void +gui_ph_draw_end( void ) +{ + gui_ph_raw_offset.x = -gui_ph_raw_offset.x; + gui_ph_raw_offset.y = -gui_ph_raw_offset.y; + PgSetTranslation( &gui_ph_raw_offset, Pg_RELATIVE ); +} + +#ifdef USE_PANEL_GROUP + static vimmenu_T * +gui_ph_find_buffer_item( char_u *name ) +{ + vimmenu_T *top_level = root_menu; + vimmenu_T *items = NULL; + + while( top_level != NULL && + ( STRCMP( top_level->dname, "Buffers" ) != 0 ) ) + top_level = top_level->next; + + if( top_level != NULL ) + { + items = top_level->children; + + while( items != NULL && + ( STRCMP( items->dname, name ) != 0 ) ) + items = items->next; + } + return( items ); +} + + static void +gui_ph_pg_set_buffer_num( int_u buf_num ) +{ + int i; + char search[16]; + char *mark; + + if( gui.vimTextArea == NULL || buf_num == 0 ) + return; + + search[0] = '('; + ultoa( buf_num, &search[1], 10 ); + STRCAT( search, ")" ); + + for( i = 0; i < num_panels; i++ ) + { + /* find the last "(" in the panel title and see if the buffer + * number in the title matches the one we're looking for */ + mark = STRRCHR( panel_titles[ i ], '(' ); + if( mark != NULL && STRCMP( mark, search ) == 0 ) + { + PtSetResource( gui.vimPanelGroup, Pt_ARG_PG_CURRENT_INDEX, + i, 0 ); + } + } +} + + static int +gui_ph_handle_pg_change( + PtWidget_t *widget, + void *data, + PtCallbackInfo_t *info ) +{ + vimmenu_T *menu; + PtPanelGroupCallback_t *panel; + + if( info->event != NULL ) + { + panel = info->cbdata; + if( panel->new_panel != NULL ) + { + menu = gui_ph_find_buffer_item( panel->new_panel ); + if( menu ) + gui_menu_cb( menu ); + } + } + return( Pt_CONTINUE ); +} + + static void +gui_ph_get_panelgroup_margins( + short *top, + short *bottom, + short *left, + short *right ) +{ + unsigned short abs_raw_x, abs_raw_y, abs_panel_x, abs_panel_y; + const unsigned short *margin_top, *margin_bottom; + const unsigned short *margin_left, *margin_right; + + PtGetAbsPosition( gui.vimTextArea, &abs_raw_x, &abs_raw_y ); + PtGetAbsPosition( gui.vimPanelGroup, &abs_panel_x, &abs_panel_y ); + + PtGetResource( gui.vimPanelGroup, Pt_ARG_MARGIN_RIGHT, &margin_right, 0 ); + PtGetResource( gui.vimPanelGroup, Pt_ARG_MARGIN_BOTTOM, &margin_bottom, 0 ); + + abs_raw_x -= abs_panel_x; + abs_raw_y -= abs_panel_y; + + *top = abs_raw_y; + *bottom = *margin_bottom; + + *left = abs_raw_x; + *right = *margin_right; +} + +/* Used for the tabs for PtPanelGroup */ + static int +gui_ph_is_buffer_item( vimmenu_T *menu, vimmenu_T *parent ) +{ + char *mark; + + if( STRCMP( parent->dname, "Buffers" ) == 0 ) + { + /* Look for '(' digits ')' */ + mark = vim_strchr( menu->dname, '(' ); + if( mark != NULL ) + { + mark++; + while( isdigit( *mark ) ) + mark++; + + if( *mark == ')' ) + return( TRUE); + } + } + return( FALSE ); +} + + static void +gui_ph_pg_add_buffer(char *name ) +{ + char **new_titles = NULL; + + new_titles = (char **) alloc( ( num_panels + 1 ) * sizeof( char ** ) ); + if( new_titles != NULL ) + { + if( num_panels > 0 ) + memcpy( new_titles, panel_titles, num_panels * sizeof( char ** ) ); + + new_titles[ num_panels++ ] = name; + + PtSetResource( gui.vimPanelGroup, Pt_ARG_PG_PANEL_TITLES, new_titles, + num_panels ); + + vim_free( panel_titles ); + panel_titles = new_titles; + } +} + + static void +gui_ph_pg_remove_buffer( char *name ) +{ + int i; + char **new_titles = NULL; + + /* If there is only 1 panel, we just use the temporary place holder */ + if( num_panels > 1 ) + { + new_titles = (char **) alloc( ( num_panels - 1 ) * sizeof( char ** ) ); + if( new_titles != NULL ) + { + char **s = new_titles; + /* Copy all the titles except the one we're removing */ + for( i = 0; i < num_panels; i++ ) + { + if( STRCMP( panel_titles[ i ], name ) != 0 ) + { + *s++ = panel_titles[ i ]; + } + } + num_panels--; + + PtSetResource( gui.vimPanelGroup, Pt_ARG_PG_PANEL_TITLES, new_titles, + num_panels ); + + vim_free( panel_titles ); + panel_titles = new_titles; + } + } + else + { + num_panels--; + PtSetResource( gui.vimPanelGroup, Pt_ARG_PG_PANEL_TITLES, &empty_title, + 1 ); + + vim_free( panel_titles ); + panel_titles = NULL; + } +} + +/* When a buffer item is deleted from the buffer menu */ + static int +gui_ph_handle_buffer_remove( + PtWidget_t *widget, + void *data, + PtCallbackInfo_t *info ) +{ + vimmenu_T *menu; + + if( data != NULL ) + { + menu = (vimmenu_T *) data; + gui_ph_pg_remove_buffer( menu->dname ); + } + + return( Pt_CONTINUE ); +} +#endif + + static int +gui_ph_pane_resize( PtWidget_t *widget, void *data, PtCallbackInfo_t *info ) +{ + if( PtWidgetIsRealized( widget ) ) + { + is_ignore_draw = TRUE; + PtStartFlux( gui.vimContainer ); + PtContainerHold( gui.vimContainer ); + } + + return( Pt_CONTINUE ); +} + +/****************************************************************************/ + +#ifdef FEAT_MBYTE + void +gui_ph_encoding_changed( int new_encoding ) +{ + /* Default encoding is latin1 */ + char *charset = "latin1"; + int i; + + struct { + int encoding; + char *name; + } charsets[] = { + { DBCS_JPN, "SHIFT_JIS" }, + { DBCS_KOR, "csEUCKR" }, + { DBCS_CHT, "big5" }, + { DBCS_CHS, "gb" } + }; + + for( i = 0; i < ARRAY_LENGTH( charsets ); i++ ) + { + if( new_encoding == charsets[ i ].encoding ) + charset = charsets[ i ].name; + } + + charset_translate = PxTranslateSet( charset_translate, charset ); +} +#endif + +/****************************************************************************/ +/****************************************************************************/ + + void +gui_mch_prepare(argc, argv) + int *argc; + char **argv; +{ + PtInit( NULL ); +} + + int +gui_mch_init(void) +{ + PtArg_t args[10]; + int flags = 0, n = 0; + + PhDim_t window_size = {100, 100}; /* Abitrary values */ + PhPoint_t pos = {0, 0}; + + gui.event_buffer = (PhEvent_t *) alloc( EVENT_BUFFER_SIZE ); + if( gui.event_buffer == NULL ) + return( FAIL ); + + /* Get a translation so we can convert from ISO Latin-1 to UTF */ + charset_translate = PxTranslateSet( NULL, "latin1" ); + + /* The +2 is for the 1 pixel dark line on each side */ + gui.border_offset = gui.border_width = GUI_PH_MARGIN + 2; + + /* Handle close events ourselves */ + PtSetArg( &args[ n++ ], Pt_ARG_WINDOW_MANAGED_FLAGS, Pt_FALSE, Ph_WM_CLOSE ); + PtSetArg( &args[ n++ ], Pt_ARG_WINDOW_NOTIFY_FLAGS, Pt_TRUE, + Ph_WM_CLOSE | Ph_WM_RESIZE | Ph_WM_FOCUS ); + PtSetArg( &args[ n++ ], Pt_ARG_DIM, &window_size, 0 ); + gui.vimWindow = PtCreateWidget( PtWindow, NULL, n, args ); + if( gui.vimWindow == NULL ) + return( FAIL ); + + PtAddCallback( gui.vimWindow, Pt_CB_WINDOW, gui_ph_handle_window_cb, NULL ); + PtAddCallback( gui.vimWindow, Pt_CB_WINDOW_OPENING, + gui_ph_handle_window_open, NULL ); + + n = 0; + PtSetArg( &args[ n++ ], Pt_ARG_ANCHOR_FLAGS, Pt_ANCHOR_ALL, Pt_IS_ANCHORED ); + PtSetArg( &args[ n++ ], Pt_ARG_DIM, &window_size, 0 ); + PtSetArg( &args[ n++ ], Pt_ARG_POS, &pos, 0 ); + +#ifdef USE_PANEL_GROUP + /* Put in a temprary place holder title */ + PtSetArg( &args[ n++ ], Pt_ARG_PG_PANEL_TITLES, &empty_title, 1 ); + + gui.vimPanelGroup = PtCreateWidget( PtPanelGroup, gui.vimWindow, n, args ); + if( gui.vimPanelGroup == NULL ) + return( FAIL ); + + PtAddCallback( gui.vimPanelGroup, Pt_CB_PG_PANEL_SWITCHING, + gui_ph_handle_pg_change, NULL ); +#else + /* Turn off all edge decorations */ + PtSetArg( &args[ n++ ], Pt_ARG_BASIC_FLAGS, Pt_FALSE, Pt_ALL ); + PtSetArg( &args[ n++ ], Pt_ARG_BEVEL_WIDTH, 0, 0 ); + PtSetArg( &args[ n++ ], Pt_ARG_MARGIN_WIDTH, 0, 0 ); + PtSetArg( &args[ n++ ], Pt_ARG_MARGIN_HEIGHT, 0, 0 ); + PtSetArg( &args[ n++ ], Pt_ARG_CONTAINER_FLAGS, Pt_TRUE, Pt_AUTO_EXTENT ); + + gui.vimContainer = PtCreateWidget( PtPane, gui.vimWindow, n, args ); + if( gui.vimContainer == NULL ) + return( FAIL ); + + PtAddCallback( gui.vimContainer, Pt_CB_RESIZE, gui_ph_pane_resize, NULL ); +#endif + + /* Size for the text area is set in gui_mch_set_text_area_pos */ + n = 0; + + PtSetArg( &args[ n++ ], Pt_ARG_RAW_DRAW_F, gui_ph_handle_raw_draw, 1 ); + PtSetArg( &args[ n++ ], Pt_ARG_BEVEL_WIDTH, GUI_PH_MARGIN, 0 ); + /* + * Using focus render also causes the whole widget to be redrawn + * whenever it changes focus, which is very annoying :p + */ + PtSetArg( &args[ n++ ], Pt_ARG_FLAGS, Pt_TRUE, + Pt_GETS_FOCUS | Pt_HIGHLIGHTED ); +#ifndef FEAT_MOUSESHAPE + PtSetArg( &args[ n++ ], Pt_ARG_CURSOR_TYPE, GUI_PH_MOUSE_TYPE, 0 ); + PtSetArg( &args[ n++ ], Pt_ARG_CURSOR_COLOR, gui_ph_mouse_color, 0 ); +#endif + + gui.vimTextArea = PtCreateWidget( PtRaw, Pt_DFLT_PARENT, n, args ); + if( gui.vimTextArea == NULL) + return( FAIL ); + + /* TODO: use PtAddEventHandlers instead? */ + /* Not using Ph_EV_BUT_REPEAT because vim wouldn't use it anyway */ + PtAddEventHandler( gui.vimTextArea, + Ph_EV_BUT_PRESS | Ph_EV_BUT_RELEASE | Ph_EV_PTR_MOTION_BUTTON, + gui_ph_handle_mouse, NULL ); + PtAddEventHandler( gui.vimTextArea, Ph_EV_KEY, + gui_ph_handle_keyboard, NULL ); + PtAddCallback( gui.vimTextArea, Pt_CB_GOT_FOCUS, + gui_ph_handle_focus, NULL ); + PtAddCallback( gui.vimTextArea, Pt_CB_LOST_FOCUS, + gui_ph_handle_focus, NULL ); + + /* + * Now that the text area widget has been created, set up the colours, + * which wil call PtSetResource from gui_mch_new_colors + */ + + /* + * Create the two timers, not as accurate as using the kernel timer + * functions, but good enough + */ + gui_ph_timer_cursor = PtCreateWidget( PtTimer, gui.vimWindow, 0, NULL ); + if( gui_ph_timer_cursor == NULL ) + return( FAIL ); + + gui_ph_timer_timeout = PtCreateWidget( PtTimer, gui.vimWindow, 0, NULL ); + if( gui_ph_timer_timeout == NULL ) + return( FAIL ); + + PtAddCallback( gui_ph_timer_cursor, Pt_CB_TIMER_ACTIVATE, + gui_ph_handle_timer_cursor, NULL); + PtAddCallback( gui_ph_timer_timeout, Pt_CB_TIMER_ACTIVATE, + gui_ph_handle_timer_timeout, NULL); + +#ifdef FEAT_MENU + n = 0; + PtSetArg( &args[ n++ ], Pt_ARG_WIDTH, window_size.w, 0 ); + PtSetArg( &args[ n++ ], Pt_ARG_ANCHOR_FLAGS, Pt_ANCHOR_LEFT_RIGHT, + Pt_IS_ANCHORED ); + gui.vimToolBarGroup = PtCreateWidget( PtToolbarGroup, gui.vimWindow, + n, args ); + if( gui.vimToolBarGroup == NULL ) + return( FAIL ); + + PtAddCallback( gui.vimToolBarGroup, Pt_CB_RESIZE, + gui_ph_handle_menu_resize, NULL ); + + n = 0; + flags = 0; + PtSetArg( &args[ n++ ], Pt_ARG_WIDTH, window_size.w, 0 ); + if( ! vim_strchr( p_go, GO_MENUS ) ) + { + flags |= Pt_DELAY_REALIZE; + PtSetArg( &args[ n++ ], Pt_ARG_FLAGS, Pt_TRUE, flags ); + } + gui.vimMenuBar = PtCreateWidget( PtMenuBar, gui.vimToolBarGroup, n, args ); + if( gui.vimMenuBar == NULL ) + return( FAIL ); + +# ifdef FEAT_TOOLBAR + n = 0; + + PtSetArg( &args[ n++ ], Pt_ARG_ANCHOR_FLAGS, + Pt_ANCHOR_LEFT_RIGHT |Pt_TOP_ANCHORED_TOP, Pt_IS_ANCHORED ); + PtSetArg( &args[ n++ ], Pt_ARG_RESIZE_FLAGS, Pt_TRUE, + Pt_RESIZE_Y_AS_REQUIRED ); + PtSetArg( &args[ n++ ], Pt_ARG_WIDTH, window_size.w, 0 ); + + flags = Pt_GETS_FOCUS; + if( ! vim_strchr( p_go, GO_TOOLBAR ) ) + flags |= Pt_DELAY_REALIZE; + + PtSetArg( &args[ n++ ], Pt_ARG_FLAGS, Pt_DELAY_REALIZE, flags ); + + gui.vimToolBar = PtCreateWidget( PtToolbar, gui.vimToolBarGroup, n, args ); + if( gui.vimToolBar == NULL ) + return( FAIL ); + + /* + * Size for the toolbar is fetched in gui_mch_show_toolbar, after + * the buttons have been added and the toolbar has resized it's height + * for the buttons to fit + */ +# endif + +#endif + + return( OK ); +} + + int +gui_mch_init_check(void) +{ + return( (is_photon_available == TRUE) ? OK : FAIL ); +} + + int +gui_mch_open(void) +{ + gui.norm_pixel = Pg_BLACK; + gui.back_pixel = Pg_WHITE; + + set_normal_colors(); + + gui_check_colors(); + gui.def_norm_pixel = gui.norm_pixel; + gui.def_back_pixel = gui.back_pixel; + + highlight_gui_started(); + + if (gui_win_x != -1 && gui_win_y != -1) + gui_mch_set_winpos(gui_win_x, gui_win_y); + + return( (PtRealizeWidget( gui.vimWindow ) == 0) ? OK : FAIL ); +} + + void +gui_mch_exit(int rc) +{ + PtDestroyWidget( gui.vimWindow ); + + PxTranslateSet( charset_translate, NULL ); + + vim_free( gui.event_buffer ); + +#ifdef USE_PANEL_GROUPS + vim_free( panel_titles ); +#endif +} + +/****************************************************************************/ +/* events */ + +/* When no events are available, photon will call this function, working is + * set to FALSE, and the gui_mch_update loop will exit. */ + static int +exit_gui_mch_update( void *data ) +{ + *(int *)data = FALSE; + return( Pt_END ); +} + + void +gui_mch_update(void) +{ + int working = TRUE; + + PtAppAddWorkProc( NULL, exit_gui_mch_update, &working ); + while( ( working == TRUE ) && !vim_is_input_buf_full()) + { + PtProcessEvent(); + } +} + + int +gui_mch_wait_for_chars(int wtime) +{ + is_timeout = FALSE; + + if( wtime > 0 ) + PtSetResource( gui_ph_timer_timeout, Pt_ARG_TIMER_INITIAL, wtime, 0 ); + + while( 1 ) + { + PtProcessEvent(); + if( input_available() ) + { + PtSetResource( gui_ph_timer_timeout, Pt_ARG_TIMER_INITIAL, 0, 0 ); + return( OK ); + } + else if( is_timeout == TRUE ) + return( FAIL ); + } +} + +#if defined( FEAT_BROWSE ) || defined( PROTO ) +/* + * Put up a file requester. + * Returns the selected name in allocated memory, or NULL for Cancel. + * saving, select file to write + * title title for the window + * default_name default name (well duh!) + * ext not used (extension added) + * initdir initial directory, NULL for current dir + * filter not used (file name filter) + */ + char_u * +gui_mch_browse( + int saving, + char_u *title, + char_u *default_name, + char_u *ext, + char_u *initdir, + char_u *filter) +{ + PtFileSelectionInfo_t file; + int flags; + char_u *default_path; + char_u *open_text = NULL; + + flags = 0; + memset( &file, 0, sizeof( file ) ); + + default_path = alloc( MAXPATHL + 1 + NAME_MAX + 1 ); + if( default_path != NULL ) + { + if( saving == TRUE ) + { + /* Don't need Pt_FSR_CONFIRM_EXISTING, vim will ask anyway */ + flags |= Pt_FSR_NO_FCHECK; + open_text = "&Save"; + } + + /* combine the directory and filename into a single path */ + if( initdir == NULL || *initdir == NUL ) + { + mch_dirname( default_path, MAXPATHL ); + initdir = default_path; + } + else + { + STRCPY( default_path, initdir ); + initdir = default_path; + } + + if( default_name != NULL ) + { + if( default_path[ STRLEN( default_path ) - 1 ] != '/' ) + STRCAT( default_path, "/" ); + + STRCAT( default_path, default_name ); + } + + /* TODO: add a filter? */ + PtFileSelection( + gui.vimWindow, + NULL, + title, + default_path, + NULL, + open_text, + NULL, + NULL, + &file, + flags ); + + vim_free( default_path ); + + if( file.ret == Pt_FSDIALOG_BTN1 ) + return( vim_strsave( file.path ) ); + } + return( NULL ); +} +#endif + +#if defined( FEAT_GUI_DIALOG ) || defined( PROTO ) +static PtWidget_t *gui_ph_dialog_text = NULL; + + static int +gui_ph_dialog_close( int button, void *data ) +{ + PtModalCtrl_t *modal_ctrl = data; + char_u *dialog_text, *vim_text; + + if( gui_ph_dialog_text != NULL ) + { + PtGetResource( gui_ph_dialog_text, Pt_ARG_TEXT_STRING, &dialog_text, 0 ); + PtGetResource( gui_ph_dialog_text, Pt_ARG_POINTER, &vim_text, 0 ); + STRNCPY( vim_text, dialog_text, IOSIZE - 1 ); + } + + PtModalUnblock( modal_ctrl, (void *) button ); + + return( Pt_TRUE ); +} + + static int +gui_ph_dialog_text_enter( PtWidget_t *widget, void *data, PtCallbackInfo_t *info ) +{ + if( info->reason_subtype == Pt_EDIT_ACTIVATE ) + gui_ph_dialog_close( 1, data ); + return( Pt_CONTINUE ); +} + + static int +gui_ph_dialog_esc( PtWidget_t *widget, void *data, PtCallbackInfo_t *info ) +{ + PhKeyEvent_t *key; + + key = PhGetData( info->event ); + if( ( key->key_flags & Pk_KF_Cap_Valid ) && ( key->key_cap == Pk_Escape ) ) + { + gui_ph_dialog_close( 0, data ); + return( Pt_CONSUME ); + } + return( Pt_PROCESS ); +} + + int +gui_mch_dialog( + int type, + char_u *title, + char_u *message, + char_u *buttons, + int default_button, + char_u *textfield) +{ + char_u *str; + char_u **button_array; + char_u *buttons_copy; + + int button_count; + int i, len; + int dialog_result = -1; + + /* FIXME: the vertical option in guioptions is blatantly ignored */ + /* FIXME: so is the type */ + + button_count = len = i = 0; + + if( buttons == NULL || *buttons == NUL ) + return( -1 ); + + /* There is one less separator than buttons, so bump up the button count */ + button_count = 1; + + /* Count string length and number of seperators */ + for( str = buttons; *str; str++ ) + { + len++; + if( *str == DLG_BUTTON_SEP ) + button_count++; + } + + if ( title == NULL ) + title = "Vim"; + + buttons_copy = alloc( len + 1 ); + button_array = (char_u **) alloc( button_count * sizeof( char_u * ) ); + if( buttons_copy != NULL && button_array != NULL ) + { + STRCPY( buttons_copy, buttons ); + + /* + * Convert DLG_BUTTON_SEP into NUL's and fill in + * button_array with the pointer to each NUL terminated string + */ + str = buttons_copy; + for( i = 0; i < button_count; i++ ) + { + button_array[ i ] = str; + for( ; *str; str++ ) + { + if( *str == DLG_BUTTON_SEP ) + { + *str++ = NUL; + break; + } + } + } +#ifndef FEAT_GUI_TEXTDIALOG + dialog_result = PtAlert( + gui.vimWindow, NULL, + title, + NULL, + message, NULL, + button_count, (const char **) button_array, NULL, + default_button, 0, Pt_MODAL ); +#else + /* Writing the dialog ourselves lets us add extra features, like + * trapping the escape key and returning 0 to vim */ + { + int n; + PtArg_t args[5]; + PtWidget_t *dialog, *pane; + PtModalCtrl_t modal_ctrl; + PtDialogInfo_t di; + + memset( &di, 0, sizeof( di ) ); + memset( &modal_ctrl, 0, sizeof( modal_ctrl ) ); + + n = 0; + PtSetArg( &args[n++], Pt_ARG_GROUP_ROWS_COLS, 0, 0 ); + PtSetArg( &args[n++], Pt_ARG_WIDTH, 350, 0 ); + PtSetArg( &args[n++], Pt_ARG_GROUP_ORIENTATION, + Pt_GROUP_VERTICAL, 0 ); + PtSetArg( &args[n++], Pt_ARG_GROUP_FLAGS, + Pt_TRUE, Pt_GROUP_NO_KEYS | Pt_GROUP_STRETCH_HORIZONTAL ); + PtSetArg( &args[n++], Pt_ARG_CONTAINER_FLAGS, Pt_FALSE, Pt_TRUE ); + pane = PtCreateWidget( PtGroup, NULL, n, args ); + + n = 0; + PtSetArg( &args[n++], Pt_ARG_TEXT_STRING, message, 0 ); + PtCreateWidget( PtLabel, pane, n, args ); + + if( textfield != NULL ) + { + n = 0; + PtSetArg( &args[n++], Pt_ARG_MAX_LENGTH, IOSIZE - 1, 0 ); + PtSetArg( &args[n++], Pt_ARG_TEXT_STRING, textfield, 0 ); + PtSetArg( &args[n++], Pt_ARG_POINTER, textfield, 0 ); + gui_ph_dialog_text = PtCreateWidget( PtText, pane, n, args ); + PtAddCallback( gui_ph_dialog_text, Pt_CB_ACTIVATE, + gui_ph_dialog_text_enter, &modal_ctrl ); + } + + di.parent = gui.vimWindow; + di.pane = pane; + di.title = title; + di.buttons = (const char **) button_array; + di.nbtns = button_count; + di.def_btn = default_button; + /* This is just to give the dialog the close button. + * We check for the Escape key ourselves and return 0 */ + di.esc_btn = button_count; + di.callback = gui_ph_dialog_close; + di.data = &modal_ctrl; + + dialog = PtCreateDialog( &di ); + PtAddFilterCallback( dialog, Ph_EV_KEY, + gui_ph_dialog_esc, &modal_ctrl ); + + if( gui_ph_dialog_text != NULL ) + PtGiveFocus( gui_ph_dialog_text, NULL ); + + /* Open dialog, block the vim window and wait for the dialog to close */ + PtRealizeWidget( dialog ); + PtMakeModal( dialog, Ph_CURSOR_NOINPUT, Ph_CURSOR_DEFAULT_COLOR ); + dialog_result = (int) PtModalBlock( &modal_ctrl, 0 ); + + PtDestroyWidget( dialog ); + gui_ph_dialog_text = NULL; + } +#endif + } + + vim_free( button_array ); + vim_free( buttons_copy ); + + return( dialog_result ); +} +#endif +/****************************************************************************/ +/* window size/position/state */ + + int +gui_mch_get_winpos(int *x, int *y) +{ + PhPoint_t *pos; + + pos = PtWidgetPos( gui.vimWindow, NULL ); + + *x = pos->x; + *y = pos->y; + + return( OK ); +} + + void +gui_mch_set_winpos(int x, int y) +{ + PhPoint_t pos = { x, y }; + + PtSetResource( gui.vimWindow, Pt_ARG_POS, &pos, 0 ); +} + + void +gui_mch_set_shellsize(int width, int height, + int min_width, int min_height, int base_width, int base_height) +{ + PhDim_t window_size = { width, height }; + PhDim_t min_size = { min_width, min_height }; + +#ifdef USE_PANEL_GROUP + window_size.w += pg_margin_left + pg_margin_right; + window_size.h += pg_margin_top + pg_margin_bottom; +#endif + + PtSetResource( gui.vimWindow, Pt_ARG_MINIMUM_DIM, &min_size, 0 ); + PtSetResource( gui.vimWindow, Pt_ARG_DIM, &window_size, 0 ); + + if( ! PtWidgetIsRealized( gui.vimWindow ) ) + gui_ph_resize_container(); +} + +/* + * Return the amount of screen space that hasn't been allocated (such as + * by the shelf). + */ + void +gui_mch_get_screen_dimensions(int *screen_w, int *screen_h) +{ + PhRect_t console; + + PhWindowQueryVisible( Ph_QUERY_WORKSPACE, 0, + PhInputGroup( NULL ), &console ); + + *screen_w = console.lr.x - console.ul.x + 1; + *screen_h = console.lr.y - console.ul.y + 1; +} + + void +gui_mch_iconify(void) +{ + PhWindowEvent_t event; + + memset( &event, 0, sizeof (event) ); + event.event_f = Ph_WM_HIDE; + event.event_state = Ph_WM_EVSTATE_HIDE; + event.rid = PtWidgetRid( gui.vimWindow ); + PtForwardWindowEvent( &event ); +} + +#if defined(FEAT_EVAL) || defined(PROTO) +/* + * Bring the Vim window to the foreground. + */ + void +gui_mch_set_foreground() +{ + PhWindowEvent_t event; + + memset( &event, 0, sizeof (event) ); + event.event_f = Ph_WM_TOFRONT; + event.event_state = Ph_WM_EVSTATE_FFRONT; + event.rid = PtWidgetRid( gui.vimWindow ); + PtForwardWindowEvent( &event ); +} +#endif + + void +gui_mch_settitle(char_u *title, char_u *icon) +{ +#ifdef USE_PANEL_GROUP + gui_ph_pg_set_buffer_num( curwin->w_buffer->b_fnum ); +#endif + PtSetResource( gui.vimWindow, Pt_ARG_WINDOW_TITLE, title, 0 ); + /* Not sure what to do with the icon text, set balloon text somehow? */ +} + +/****************************************************************************/ +/* Scrollbar */ + + void +gui_mch_set_scrollbar_thumb(scrollbar_T *sb, int val, int size, int max) +{ + int n = 0; + PtArg_t args[3]; + + PtSetArg( &args[ n++ ], Pt_ARG_MAXIMUM, max, 0 ); + PtSetArg( &args[ n++ ], Pt_ARG_SLIDER_SIZE, size, 0 ); + PtSetArg( &args[ n++ ], Pt_ARG_GAUGE_VALUE, val, 0 ); + PtSetResources( sb->id, n, args ); +} + + void +gui_mch_set_scrollbar_pos(scrollbar_T *sb, int x, int y, int w, int h) +{ + PhArea_t area = {{ x, y }, { w, h }}; + + PtSetResource( sb->id, Pt_ARG_AREA, &area, 0 ); +} + + void +gui_mch_create_scrollbar(scrollbar_T *sb, int orient) +{ + int n = 0; +/* int anchor_flags = 0;*/ + PtArg_t args[4]; + + /* + * Stop the scrollbar from being realized when the parent + * is realized, so it can be explicitly realized by vim. + * + * Also, don't let the scrollbar get focus + */ + PtSetArg( &args[ n++ ], Pt_ARG_FLAGS, Pt_DELAY_REALIZE, + Pt_DELAY_REALIZE | Pt_GETS_FOCUS ); + PtSetArg( &args[ n++ ], Pt_ARG_SCROLLBAR_FLAGS, Pt_SCROLLBAR_SHOW_ARROWS, 0); +#if 0 + /* Don't need this anchoring for the scrollbars */ + if( orient == SBAR_HORIZ ) + { + anchor_flags = Pt_BOTTOM_ANCHORED_BOTTOM | + Pt_LEFT_ANCHORED_LEFT | Pt_RIGHT_ANCHORED_RIGHT; + } + else + { + anchor_flags = Pt_BOTTOM_ANCHORED_BOTTOM | Pt_TOP_ANCHORED_TOP; + if( sb->wp != NULL ) + { + if( sb == &sb->wp->w_scrollbars[ SBAR_LEFT ] ) + anchor_flags |= Pt_LEFT_ANCHORED_LEFT; + else + anchor_flags |= Pt_RIGHT_ANCHORED_RIGHT; + } + } + PtSetArg( &args[ n++ ], Pt_ARG_ANCHOR_FLAGS, anchor_flags, Pt_IS_ANCHORED ); +#endif + PtSetArg( &args[ n++ ], Pt_ARG_ORIENTATION, + (orient == SBAR_HORIZ) ? Pt_HORIZONTAL : Pt_VERTICAL, 0 ); +#ifdef USE_PANEL_GROUP + sb->id = PtCreateWidget( PtScrollbar, gui.vimPanelGroup, n, args ); +#else + sb->id = PtCreateWidget( PtScrollbar, gui.vimContainer, n, args ); +#endif + + PtAddCallback( sb->id, Pt_CB_SCROLLBAR_MOVE, gui_ph_handle_scrollbar, sb ); +} + + void +gui_mch_enable_scrollbar(scrollbar_T *sb, int flag) +{ + if( flag != 0 ) + PtRealizeWidget( sb->id ); + else + PtUnrealizeWidget( sb->id ); +} + + void +gui_mch_destroy_scrollbar(scrollbar_T *sb) +{ + PtDestroyWidget( sb->id ); + sb->id = NULL; +} + +/****************************************************************************/ +/* Mouse functions */ + +#if defined(FEAT_MOUSESHAPE) || defined(PROTO) +/* The last set mouse pointer shape is remembered, to be used when it goes + * from hidden to not hidden. */ +static int last_shape = 0; + +/* Table for shape IDs. Keep in sync with the mshape_names[] table in + * misc2.c! */ +static int mshape_ids[] = +{ + Ph_CURSOR_POINTER, /* arrow */ + Ph_CURSOR_NONE, /* blank */ + Ph_CURSOR_INSERT, /* beam */ + Ph_CURSOR_DRAG_VERTICAL, /* updown */ + Ph_CURSOR_DRAG_VERTICAL, /* udsizing */ + Ph_CURSOR_DRAG_HORIZONTAL, /* leftright */ + Ph_CURSOR_DRAG_HORIZONTAL, /* lrsizing */ + Ph_CURSOR_WAIT, /* busy */ + Ph_CURSOR_DONT, /* no */ + Ph_CURSOR_CROSSHAIR, /* crosshair */ + Ph_CURSOR_FINGER, /* hand1 */ + Ph_CURSOR_FINGER, /* hand2 */ + Ph_CURSOR_FINGER, /* pencil */ + Ph_CURSOR_QUESTION_POINT, /* question */ + Ph_CURSOR_POINTER, /* right-arrow */ + Ph_CURSOR_POINTER, /* up-arrow */ + Ph_CURSOR_POINTER /* last one */ +}; + + void +mch_set_mouse_shape(shape) + int shape; +{ + int id; + + if (!gui.in_use) + return; + + if (shape == MSHAPE_HIDE || gui.pointer_hidden) + PtSetResource( gui.vimTextArea, Pt_ARG_CURSOR_TYPE, Ph_CURSOR_NONE, + 0 ); + else + { + if (shape >= MSHAPE_NUMBERED) + id = Ph_CURSOR_POINTER; + else + id = mshape_ids[shape]; + + PtSetResource( gui.vimTextArea, Pt_ARG_CURSOR_TYPE, id, 0 ); + } + if (shape != MSHAPE_HIDE) + last_shape = shape; +} +#endif + + void +gui_mch_mousehide(int hide) +{ + if( gui.pointer_hidden != hide ) + { + gui.pointer_hidden = hide; +#ifdef FEAT_MOUSESHAPE + if( hide ) + PtSetResource( gui.vimTextArea, Pt_ARG_CURSOR_TYPE, + Ph_CURSOR_NONE, 0 ); + else + mch_set_mouse_shape( last_shape ); +#else + PtSetResource( gui.vimTextArea, Pt_ARG_CURSOR_TYPE, + ( hide == MOUSE_SHOW ) ? GUI_PH_MOUSE_TYPE : Ph_CURSOR_NONE, + 0 ); +#endif + } +} + + int +gui_mch_get_mouse_x(void) +{ + PhCursorInfo_t info; + short x, y; + + /* FIXME: does this return the correct position, + * with respect to the border? */ + PhQueryCursor( PhInputGroup( NULL ), &info ); + PtGetAbsPosition( gui.vimTextArea , &x, &y ); + + return( info.pos.x - x ); +} + + int +gui_mch_get_mouse_y(void) +{ + PhCursorInfo_t info; + short x, y; + + PhQueryCursor( PhInputGroup( NULL ), &info ); + PtGetAbsPosition( gui.vimTextArea , &x, &y ); + /* TODO: Add border offset? */ + return( info.pos.y - y ); +} + + void +gui_mch_setmouse(int x, int y) +{ + short abs_x, abs_y; + + PtGetAbsPosition( gui.vimTextArea, &abs_x, &abs_y ); + /* Add the border offset? */ + PhMoveCursorAbs( PhInputGroup( NULL ), abs_x + x, abs_y + y ); +} + +/****************************************************************************/ +/* Colours */ + +/* + * Return the RGB value of a pixel as a long. + */ + long_u +gui_mch_get_rgb(guicolor_T pixel) +{ + return PgRGB(PgRedValue(pixel), PgGreenValue(pixel), PgBlueValue(pixel)); +} + + void +gui_mch_new_colors(void) +{ +#if 0 /* Don't bother changing the cursor colour */ + short color_diff; + + /* + * If there isn't enough difference between the background colour and + * the mouse pointer colour then change the mouse pointer colour + */ + color_diff = gui_get_lightness(gui_ph_mouse_color) + - gui_get_lightness(gui.back_pixel); + + if( abs( color_diff ) < 64 ) + { + short r, g, b; + /* not a great algorithm... */ + r = PgRedValue( gui_ph_mouse_color ) ^ 255; + g = PgGreenValue( gui_ph_mouse_color ) ^ 255; + b = PgBlueValue( gui_ph_mouse_color ) ^ 255; + +#ifndef FEAT_MOUSESHAPE + gui_ph_mouse_color = PgRGB( r, g, b ); + PtSetResource( gui.vimTextArea, Pt_ARG_CURSOR_COLOR, + gui_ph_mouse_color, 0 ); +#endif + } +#endif + + PtSetResource( gui.vimTextArea, Pt_ARG_FILL_COLOR, gui.back_pixel, 0 ); +} + + static int +hex_digit(int c) +{ + if (VIM_ISDIGIT(c)) + return( c - '0' ); + c = TOLOWER_ASC(c); + if (c >= 'a' && c <= 'f') + return( c - 'a' + 10 ); + return( -1000 ); +} + + +/* + * This should be split out into a seperate file, + * every port does basically the same thing. + * + * This is the gui_w32.c version (i think..) + * Return INVALCOLOR when failed. + */ + + guicolor_T +gui_mch_get_color(char_u *name) +{ + int i; + int r, g, b; + + + typedef struct GuiColourTable + { + char *name; + guicolor_T colour; + } GuiColourTable; + + static GuiColourTable table[] = + { + {"Black", RGB(0x00, 0x00, 0x00)}, + {"DarkGray", RGB(0x80, 0x80, 0x80)}, + {"DarkGrey", RGB(0x80, 0x80, 0x80)}, + {"Gray", RGB(0xC0, 0xC0, 0xC0)}, + {"Grey", RGB(0xC0, 0xC0, 0xC0)}, + {"LightGray", RGB(0xD3, 0xD3, 0xD3)}, + {"LightGrey", RGB(0xD3, 0xD3, 0xD3)}, + {"White", RGB(0xFF, 0xFF, 0xFF)}, + {"DarkRed", RGB(0x80, 0x00, 0x00)}, + {"Red", RGB(0xFF, 0x00, 0x00)}, + {"LightRed", RGB(0xFF, 0xA0, 0xA0)}, + {"DarkBlue", RGB(0x00, 0x00, 0x80)}, + {"Blue", RGB(0x00, 0x00, 0xFF)}, + {"LightBlue", RGB(0xA0, 0xA0, 0xFF)}, + {"DarkGreen", RGB(0x00, 0x80, 0x00)}, + {"Green", RGB(0x00, 0xFF, 0x00)}, + {"LightGreen", RGB(0xA0, 0xFF, 0xA0)}, + {"DarkCyan", RGB(0x00, 0x80, 0x80)}, + {"Cyan", RGB(0x00, 0xFF, 0xFF)}, + {"LightCyan", RGB(0xA0, 0xFF, 0xFF)}, + {"DarkMagenta", RGB(0x80, 0x00, 0x80)}, + {"Magenta", RGB(0xFF, 0x00, 0xFF)}, + {"LightMagenta", RGB(0xFF, 0xA0, 0xFF)}, + {"Brown", RGB(0x80, 0x40, 0x40)}, + {"Yellow", RGB(0xFF, 0xFF, 0x00)}, + {"LightYellow", RGB(0xFF, 0xFF, 0xA0)}, + {"SeaGreen", RGB(0x2E, 0x8B, 0x57)}, + {"Orange", RGB(0xFF, 0xA5, 0x00)}, + {"Purple", RGB(0xA0, 0x20, 0xF0)}, + {"SlateBlue", RGB(0x6A, 0x5A, 0xCD)}, + {"Violet", RGB(0xEE, 0x82, 0xEE)}, + }; + + /* is name #rrggbb format? */ + if( name[0] == '#' && STRLEN( name ) == 7 ) + { + r = hex_digit( name[1] ) * 16 + hex_digit( name[2] ); + g = hex_digit( name[3] ) * 16 + hex_digit( name[4] ); + b = hex_digit( name[5] ) * 16 + hex_digit( name[6] ); + if( r < 0 || g < 0 || b < 0 ) + return INVALCOLOR; + return( RGB( r, g, b ) ); + } + + for( i = 0; i < ARRAY_LENGTH( table ); i++ ) + { + if( STRICMP( name, table[i].name ) == 0 ) + return( table[i].colour ); + } + + /* + * Last attempt. Look in the file "$VIMRUNTIME/rgb.txt". + */ + { +#define LINE_LEN 100 + FILE *fd; + char line[LINE_LEN]; + char_u *fname; + + fname = expand_env_save((char_u *)"$VIMRUNTIME/rgb.txt"); + if (fname == NULL) + return INVALCOLOR; + + fd = fopen((char *)fname, "rt"); + vim_free(fname); + if (fd == NULL) + return INVALCOLOR; + + while (!feof(fd)) + { + int len; + int pos; + char *color; + + fgets(line, LINE_LEN, fd); + len = STRLEN(line); + + if (len <= 1 || line[len-1] != '\n') + continue; + + line[len-1] = '\0'; + + i = sscanf(line, "%d %d %d %n", &r, &g, &b, &pos); + if (i != 3) + continue; + + color = line + pos; + + if (STRICMP(color, name) == 0) + { + fclose(fd); + return( (guicolor_T) RGB(r,g,b) ); + } + } + + fclose(fd); + } + + + return INVALCOLOR; +} + + void +gui_mch_set_fg_color(guicolor_T color) +{ + PgSetTextColor( color ); +} + + void +gui_mch_set_bg_color(guicolor_T color) +{ + PgSetFillColor( color ); +} + + void +gui_mch_invert_rectangle(int row, int col, int nr, int nc) +{ + PhRect_t rect; + + rect.ul.x = FILL_X( col ); + rect.ul.y = FILL_Y( row ); + + /* FIXME: This has an off by one pixel problem */ + rect.lr.x = rect.ul.x + nc * gui.char_width; + rect.lr.y = rect.ul.y + nr * gui.char_height; + if( nc > 0 ) + rect.lr.x -= 1; + if( nr > 0 ) + rect.lr.y -= 1; + + DRAW_START; + PgSetDrawMode( Pg_DrawModeDSTINVERT ); + PgDrawRect( &rect, Pg_DRAW_FILL ); + PgSetDrawMode( Pg_DrawModeSRCCOPY ); + DRAW_END; +} + + void +gui_mch_clear_block(int row1, int col1, int row2, int col2) +{ + PhRect_t block = { + { FILL_X( col1 ), FILL_Y( row1 ) }, + { FILL_X( col2 + 1 ) - 1, FILL_Y( row2 + 1 ) - 1} + }; + + DRAW_START; + gui_mch_set_bg_color( gui.back_pixel ); + PgDrawRect( &block, Pg_DRAW_FILL ); + DRAW_END; +} + + void +gui_mch_clear_all() +{ + PhRect_t text_rect = { + { gui.border_width, gui.border_width }, + { Columns * gui.char_width + gui.border_width - 1 , + Rows * gui.char_height + gui.border_width - 1 } + }; + + if( is_ignore_draw == TRUE ) + return; + + DRAW_START; + gui_mch_set_bg_color( gui.back_pixel ); + PgDrawRect( &text_rect, Pg_DRAW_FILL ); + DRAW_END; +} + + void +gui_mch_delete_lines(int row, int num_lines) +{ + PhRect_t rect; + PhPoint_t delta; + + rect.ul.x = FILL_X( gui.scroll_region_left ); + rect.ul.y = FILL_Y( row + num_lines ); + + rect.lr.x = FILL_X( gui.scroll_region_right + 1 ) - 1; + rect.lr.y = FILL_Y( gui.scroll_region_bot + 1) - 1; + + PtWidgetOffset( gui.vimTextArea, &gui_ph_raw_offset ); + PhTranslatePoint( &gui_ph_raw_offset, PtWidgetPos(gui.vimTextArea, NULL)); + PhTranslateRect( &rect, &gui_ph_raw_offset ); + + delta.x = 0; + delta.y = -num_lines * gui.char_height; + + PgFlush(); + + PhBlit( PtWidgetRid( PtFindDisjoint( gui.vimTextArea ) ), &rect, &delta ); + + gui_clear_block( + gui.scroll_region_bot - num_lines + 1, + gui.scroll_region_left, + gui.scroll_region_bot, + gui.scroll_region_right ); +} + + void +gui_mch_insert_lines(int row, int num_lines) +{ + PhRect_t rect; + PhPoint_t delta; + + rect.ul.x = FILL_X( gui.scroll_region_left ); + rect.ul.y = FILL_Y( row ); + + rect.lr.x = FILL_X( gui.scroll_region_right + 1 ) - 1; + rect.lr.y = FILL_Y( gui.scroll_region_bot - num_lines + 1 ) - 1; + + PtWidgetOffset( gui.vimTextArea, &gui_ph_raw_offset ); + PhTranslatePoint( &gui_ph_raw_offset, PtWidgetPos( gui.vimTextArea, NULL ) ); + PhTranslateRect( &rect, &gui_ph_raw_offset ); + + delta.x = 0; + delta.y = num_lines * gui.char_height; + + PgFlush(); + + PhBlit( PtWidgetRid( PtFindDisjoint( gui.vimTextArea ) ) , &rect, &delta ); + + gui_clear_block( row, gui.scroll_region_left, + row + num_lines - 1, gui.scroll_region_right ); +} + + void +gui_mch_draw_string(int row, int col, char_u *s, int len, int flags) +{ + static char *utf8_buffer = NULL; + static int utf8_len = 0; + + PhPoint_t pos = { TEXT_X( col ), TEXT_Y( row ) }; + PhRect_t rect; + + if( is_ignore_draw == TRUE ) + return; + + DRAW_START; + + if( !( flags & DRAW_TRANSP ) ) + { + PgDrawIRect( + FILL_X( col ), FILL_Y( row ), + FILL_X( col + len ) - 1, FILL_Y( row + 1 ) - 1, + Pg_DRAW_FILL ); + } + + if( flags & DRAW_UNDERL ) + PgSetUnderline( gui.norm_pixel, Pg_TRANSPARENT, 0 ); + + if( charset_translate != NULL +#ifdef FEAT_MBYTE + && enc_utf8 == 0 +#endif + ) + { + int src_taken, dst_made; + + /* Use a static buffer to avoid large amounts of de/allocations */ + if( utf8_len < len ) + { + utf8_buffer = realloc( utf8_buffer, len * MB_LEN_MAX ); + utf8_len = len; + } + + PxTranslateToUTF( + charset_translate, + s, + len, + &src_taken, + utf8_buffer, + utf8_len, + &dst_made ); + s = utf8_buffer; + len = dst_made; + } + + PgDrawText( s, len, &pos, 0 ); + + if( flags & DRAW_BOLD ) + { + /* FIXME: try and only calculate these values once... */ + rect.ul.x = FILL_X( col ) + 1; + rect.ul.y = FILL_Y( row ); + rect.lr.x = FILL_X( col + len ) - 1; + rect.lr.y = FILL_Y( row + 1) - 1; + /* PgSetUserClip( NULL ) causes the scrollbar to not redraw... */ +#if 0 + pos.x++; + + PgSetUserClip( &rect ); + PgDrawText( s, len, &pos, 0 ); + PgSetUserClip( NULL ); +#else + rect.lr.y -= ( p_linespace + 1 ) / 2; + /* XXX: DrawTextArea doesn't work with phditto */ + PgDrawTextArea( s, len, &rect, Pg_TEXT_BOTTOM ); +#endif + } + + if( flags & DRAW_UNDERL ) + PgSetUnderline( Pg_TRANSPARENT, Pg_TRANSPARENT, 0 ); + + DRAW_END; +} + +/****************************************************************************/ +/* Cursor */ + + void +gui_mch_draw_hollow_cursor(guicolor_T color) +{ + PhRect_t r; + + /* FIXME: Double width characters */ + + r.ul.x = FILL_X( gui.col ); + r.ul.y = FILL_Y( gui.row ); + r.lr.x = r.ul.x + gui.char_width - 1; + r.lr.y = r.ul.y + gui.char_height - 1; + + DRAW_START; + PgSetStrokeColor( color ); + PgDrawRect( &r, Pg_DRAW_STROKE ); + DRAW_END; +} + + void +gui_mch_draw_part_cursor(int w, int h, guicolor_T color) +{ + PhRect_t r; + + r.ul.x = FILL_X( gui.col ); + r.ul.y = FILL_Y( gui.row ) + gui.char_height - h; + r.lr.x = r.ul.x + w - 1; + r.lr.y = r.ul.y + h - 1; + + DRAW_START; + gui_mch_set_bg_color( color ); + PgDrawRect( &r, Pg_DRAW_FILL ); + DRAW_END; +} + + void +gui_mch_set_blinking(long wait, long on, long off) +{ + blink_waittime = wait; + blink_ontime = on; + blink_offtime = off; +} + + void +gui_mch_start_blink(void) +{ + /* Only turn on the timer on if none of the times are zero */ + if( blink_waittime && blink_ontime && blink_offtime && gui.in_focus) + { + PtSetResource( gui_ph_timer_cursor, Pt_ARG_TIMER_INITIAL, + blink_waittime, 0 ); + blink_state = BLINK_ON; + gui_update_cursor(TRUE, FALSE); + } +} + + void +gui_mch_stop_blink(void) +{ + PtSetResource( gui_ph_timer_cursor, Pt_ARG_TIMER_INITIAL, 0, 0 ); + + if( blink_state == BLINK_OFF ) + gui_update_cursor(TRUE, FALSE); + + blink_state = BLINK_NONE; +} + +/****************************************************************************/ +/* miscellaneous functions */ + + void +gui_mch_beep(void) +{ + PtBeep(); +} + + void +gui_mch_flash(int msec) +{ + PgSetFillXORColor( Pg_BLACK, Pg_WHITE ); + PgSetDrawMode( Pg_DRAWMODE_XOR ); + gui_mch_clear_all(); + gui_mch_flush(); + + ui_delay( (long) msec, TRUE ); + + gui_mch_clear_all(); + PgSetDrawMode( Pg_DRAWMODE_OPAQUE ); + gui_mch_flush(); +} + + void +gui_mch_flush(void) +{ + PgFlush(); +} + + void +gui_mch_set_text_area_pos(int x, int y, int w, int h) +{ + PhArea_t area = {{x, y}, {w, h}}; + + PtSetResource( gui.vimTextArea, Pt_ARG_AREA, &area, 0 ); +} + + int +gui_mch_haskey(char_u *name) +{ + int i; + + for (i = 0; special_keys[i].key_sym != 0; i++) + if (name[0] == special_keys[i].vim_code0 && + name[1] == special_keys[i].vim_code1) + return( OK ); + return( FAIL ); +} + +/****************************************************************************/ +/* Menu */ + +#ifdef FEAT_TOOLBAR +#include "toolbar.phi" + +static PhImage_t *gui_ph_toolbar_images[] = { + &tb_new_phi, + &tb_open_phi, + &tb_save_phi, + &tb_undo_phi, + &tb_redo_phi, + &tb_cut_phi, + &tb_copy_phi, + &tb_paste_phi, + &tb_print_phi, + &tb_help_phi, + &tb_find_phi, + &tb_save_all_phi, + &tb_save_session_phi, + &tb_new_session_phi, + &tb_load_session_phi, + &tb_macro_phi, + &tb_replace_phi, + &tb_close_phi, + &tb_maximize_phi, + &tb_minimize_phi, + &tb_split_phi, + &tb_shell_phi, + &tb_find_prev_phi, + &tb_find_next_phi, + &tb_find_help_phi, + &tb_make_phi, + &tb_jump_phi, + &tb_ctags_phi, + &tb_vsplit_phi, + &tb_maxwidth_phi, + &tb_minwidth_phi +}; + +static PhImage_t * +gui_ph_toolbar_load_icon( char_u *iconfile ) +{ + static PhImage_t external_icon; + PhImage_t *temp_phi = NULL; + + temp_phi = PxLoadImage( iconfile, NULL ); + if( temp_phi != NULL ) + { + /* The label widget will free the image/palette/etc. for us when + * it's destroyed */ + temp_phi->flags |= Ph_RELEASE_IMAGE_ALL; + memcpy( &external_icon, temp_phi, sizeof( external_icon ) ); + free( temp_phi ); + + temp_phi = &external_icon; + } + return( temp_phi ); +} + +/* + * This returns either a builtin icon image, an external image or NULL + * if it can't find either. The caller can't and doesn't need to try and + * free() the returned image, and it can't store the image pointer. + * (When setting the Pt_ARG_LABEL_IMAGE resource, the contents of the + * PhImage_t are copied, and the original PhImage_t aren't needed anymore). + */ +static PhImage_t * +gui_ph_toolbar_find_icon( vimmenu_T *menu ) +{ + char_u full_pathname[ MAXPATHL + 1 ]; + PhImage_t *icon = NULL; + + if( menu->icon_builtin == FALSE ) + { + if( menu->iconfile != NULL ) + /* TODO: use gui_find_iconfile() */ + icon = gui_ph_toolbar_load_icon( menu->iconfile ); + + /* TODO: Restrict loading to just .png? Search for any format? */ + if( ( icon == NULL ) && + ( ( gui_find_bitmap( menu->name, full_pathname, "gif" ) == OK ) || + ( gui_find_bitmap( menu->name, full_pathname, "png" ) == OK ) ) ) + icon = gui_ph_toolbar_load_icon( full_pathname ); + + if( icon != NULL ) + return( icon ); + } + + if( menu->iconidx >= 0 && + ( menu->iconidx < ARRAY_LENGTH( gui_ph_toolbar_images ) ) ) + { + return( gui_ph_toolbar_images[ menu->iconidx ] ); + } + + return( NULL ); +} +#endif + +#if defined( FEAT_MENU ) || defined( PROTO ) + void +gui_mch_enable_menu(int flag) +{ + if( flag != 0 ) + PtRealizeWidget( gui.vimMenuBar ); + else + PtUnrealizeWidget( gui.vimMenuBar ); +} + + void +gui_mch_set_menu_pos(int x, int y, int w, int h) +{ + /* Nothing */ +} + +/* Change the position of a menu button in the parent */ + static void +gui_ph_position_menu( PtWidget_t *widget, int priority ) +{ + PtWidget_t *traverse; + vimmenu_T *menu; + + traverse = PtWidgetChildBack( PtWidgetParent( widget ) ); + + /* Iterate through the list of widgets in traverse, until + * we find the position we want to insert our widget into */ + /* TODO: traverse from front to back, possible speedup? */ + while( traverse != NULL ) + { + PtGetResource( traverse, Pt_ARG_POINTER, &menu, 0 ); + + if( menu != NULL && + priority < menu->priority && + widget != traverse ) + { + /* Insert the widget before the current traverse widget */ + PtWidgetInsert( widget, traverse, 1 ); + return; + } + + traverse = PtWidgetBrotherInFront( traverse ); + } +} + +/* the index is ignored because it's not useful for our purposes */ + void +gui_mch_add_menu(vimmenu_T *menu, int index) +{ + vimmenu_T *parent = menu->parent; + char_u *accel_key; + char_u mnemonic_str[MB_LEN_MAX]; + int n; + PtArg_t args[5]; + + menu->submenu_id = menu->id = NULL; + + if( menu_is_menubar( menu->name ) ) + { + + accel_key = vim_strchr( menu->name, '&' ); + if( accel_key != NULL ) + { + mnemonic_str[0] = accel_key[1]; + mnemonic_str[1] = NUL; + } + + /* Create the menu button */ + n = 0; + PtSetArg( &args[ n++ ], Pt_ARG_TEXT_STRING, menu->dname, 0 ); + PtSetArg( &args[ n++ ], Pt_ARG_ACCEL_TEXT, menu->actext, 0 ); + if( accel_key != NULL ) + PtSetArg( &args[ n++ ], Pt_ARG_ACCEL_KEY, mnemonic_str, 0 ); + PtSetArg( &args[ n++ ], Pt_ARG_POINTER, menu, 0 ); + + if( parent != NULL ) + PtSetArg( &args[ n++ ], Pt_ARG_BUTTON_TYPE, Pt_MENU_RIGHT, 0 ); + + menu->id = PtCreateWidget( PtMenuButton, + (parent == NULL) ? gui.vimMenuBar : parent->submenu_id, + n, args ); + + PtAddCallback( menu->id, Pt_CB_ARM, gui_ph_handle_pulldown_menu, menu ); + + /* Create the actual menu */ + n = 0; + if( parent != NULL ) + PtSetArg( &args[ n++ ], Pt_ARG_MENU_FLAGS, Pt_TRUE, Pt_MENU_CHILD ); + + menu->submenu_id = PtCreateWidget( PtMenu, menu->id, n, args ); + + if( parent == NULL ) + { + PtAddCallback( menu->submenu_id, Pt_CB_UNREALIZED, + gui_ph_handle_menu_unrealized, menu ); + + if( menu->mnemonic != 0 ) + { + PtAddHotkeyHandler( gui.vimWindow, tolower( menu->mnemonic ), + Pk_KM_Alt, 0, menu, gui_ph_handle_pulldown_menu ); + } + } + + gui_ph_position_menu( menu->id, menu->priority ); + + /* Redraw menubar here instead of gui_mch_draw_menubar */ + if( gui.menu_is_active ) + PtRealizeWidget( menu->id ); + } + else if( menu_is_popup( menu->name ) ) + { + menu->submenu_id = PtCreateWidget( PtMenu, gui.vimWindow, 0, NULL ); + PtAddCallback( menu->submenu_id, Pt_CB_UNREALIZED, + gui_ph_handle_menu_unrealized, menu ); + } +} + + void +gui_mch_add_menu_item(vimmenu_T *menu, int index) +{ + vimmenu_T *parent = menu->parent; + char_u *accel_key; + char_u mnemonic_str[MB_LEN_MAX]; + int n; + PtArg_t args[13]; + + n = 0; + PtSetArg( &args[ n++ ], Pt_ARG_POINTER, menu, 0 ); + +#ifdef FEAT_TOOLBAR + if( menu_is_toolbar( parent->name ) ) + { + if( menu_is_separator( menu->name ) ) + { + PtSetArg( &args[ n++ ], Pt_ARG_SEP_FLAGS, + Pt_SEP_VERTICAL, Pt_SEP_ORIENTATION ); + PtSetArg( &args[ n++ ], Pt_ARG_SEP_TYPE, Pt_ETCHED_IN, 0 ); + PtSetArg( &args[ n++ ], Pt_ARG_ANCHOR_FLAGS, + Pt_TRUE, Pt_ANCHOR_TOP_BOTTOM ); + PtSetArg( &args[ n++ ], Pt_ARG_WIDTH, 2, 0 ); + menu->id = PtCreateWidget( PtSeparator, gui.vimToolBar, n, args ); + } + else + { + if( strstr( (const char *) p_toolbar, "text" ) != NULL ) + { + PtSetArg( &args[ n++ ], Pt_ARG_BALLOON_POSITION, + Pt_BALLOON_BOTTOM, 0 ); + PtSetArg( &args[ n++ ], Pt_ARG_TEXT_STRING, menu->dname, 0 ); + PtSetArg( &args[ n++ ], Pt_ARG_TEXT_FONT, "TextFont08", 0 ); + } + if( ( strstr( (const char *) p_toolbar, "icons" ) != NULL ) && + ( gui_ph_toolbar_images != NULL ) ) + { + PtSetArg( &args[ n++ ], Pt_ARG_LABEL_IMAGE, + gui_ph_toolbar_find_icon( menu ), 0 ); + PtSetArg( &args[ n++ ], Pt_ARG_LABEL_TYPE, Pt_TEXT_IMAGE, 0 ); + PtSetArg( &args[ n++ ], Pt_ARG_TEXT_IMAGE_SPACING, 0, 0 ); + } + if( strstr( (const char *) p_toolbar, "tooltips" ) != NULL ) + { + PtSetArg( &args[ n++ ], Pt_ARG_LABEL_BALLOON, + gui_ph_show_tooltip, 0 ); + PtSetArg( &args[ n++ ], Pt_ARG_LABEL_FLAGS, + Pt_TRUE, Pt_SHOW_BALLOON ); + } + PtSetArg( &args[ n++ ], Pt_ARG_MARGIN_HEIGHT, 1, 0 ); + PtSetArg( &args[ n++ ], Pt_ARG_MARGIN_WIDTH, 1, 0 ); + PtSetArg( &args[ n++ ], Pt_ARG_FLAGS, Pt_FALSE, + Pt_HIGHLIGHTED | Pt_GETS_FOCUS ); + PtSetArg( &args[ n++ ], Pt_ARG_FILL_COLOR, Pg_TRANSPARENT, 0 ); + menu->id = PtCreateWidget( PtButton, gui.vimToolBar, n, args ); + + PtAddCallback( menu->id, Pt_CB_ACTIVATE, gui_ph_handle_menu, menu ); + } + /* Update toolbar if it's open */ + if( PtWidgetIsRealized( gui.vimToolBar ) ) + PtRealizeWidget( menu->id ); + } + else +#endif + if( menu_is_separator( menu->name ) ) + { + menu->id = PtCreateWidget( PtSeparator, parent->submenu_id, n, args ); + } + else + { + accel_key = vim_strchr( menu->name, '&' ); + if( accel_key != NULL ) + { + mnemonic_str[0] = accel_key[1]; + mnemonic_str[1] = NUL; + } + + PtSetArg( &args[ n++ ], Pt_ARG_TEXT_STRING, menu->dname, 0 ); + if( accel_key != NULL ) + PtSetArg( &args[ n++ ], Pt_ARG_ACCEL_KEY, mnemonic_str, + 0 ); + + PtSetArg( &args[ n++ ], Pt_ARG_ACCEL_TEXT, menu->actext, 0 ); + + menu->id = PtCreateWidget( PtMenuButton, parent->submenu_id, n, args ); + + PtAddCallback( menu->id, Pt_CB_ACTIVATE, gui_ph_handle_menu, menu ); + +#ifdef USE_PANEL_GROUP + if( gui_ph_is_buffer_item( menu, parent ) == TRUE ) + { + PtAddCallback( menu->id, Pt_CB_DESTROYED, + gui_ph_handle_buffer_remove, menu ); + gui_ph_pg_add_buffer( menu->dname ); + } +#endif + } + + gui_ph_position_menu( menu->id, menu->priority ); +} + + void +gui_mch_destroy_menu(vimmenu_T *menu) +{ + if( menu->submenu_id != NULL ) + PtDestroyWidget( menu->submenu_id ); + if( menu->id != NULL ) + PtDestroyWidget( menu->id ); + + menu->submenu_id = NULL; + menu->id = NULL; +} + + void +gui_mch_menu_grey(vimmenu_T *menu, int grey) +{ + long flags, mask, fields; + + if( menu->id == NULL ) + return; + + flags = PtWidgetFlags( menu->id ); + if( PtWidgetIsClass( menu->id, PtMenuButton ) && + PtWidgetIsClass( PtWidgetParent( menu->id ), PtMenu ) ) + { + fields = Pt_FALSE; + mask = Pt_SELECTABLE | Pt_HIGHLIGHTED; + } + else + { + fields = Pt_TRUE; + mask = Pt_BLOCKED | Pt_GHOST; + } + + if( ! grey ) + fields = ~fields; + + PtSetResource( menu->id, Pt_ARG_FLAGS, fields, + mask ); +} + + void +gui_mch_menu_hidden(vimmenu_T *menu, int hidden) +{ + /* TODO: [un]realize the widget? */ +} + + void +gui_mch_draw_menubar(void) +{ + /* The only time a redraw is needed is when a menu button + * is added to the menubar, and that is detected and the bar + * redrawn in gui_mch_add_menu_item + */ +} + + void +gui_mch_show_popupmenu(vimmenu_T *menu) +{ + PtSetResource( menu->submenu_id, Pt_ARG_POS, &abs_mouse, 0 ); + PtRealizeWidget( menu->submenu_id ); +} + + void +gui_mch_toggle_tearoffs(int enable) +{ + /* No tearoffs yet */ +} + +#endif + +#if defined( FEAT_TOOLBAR ) || defined( PROTO ) + void +gui_mch_show_toolbar(int showit) +{ + if( showit ) + PtRealizeWidget( gui.vimToolBar ); + else + PtUnrealizeWidget( gui.vimToolBar ); +} +#endif + +/****************************************************************************/ +/* Fonts */ + + static GuiFont +gui_ph_get_font( + char_u *font_name, + int_u font_flags, + int_u font_size, + /* Check whether the resulting font has the font flags and size that + * was asked for */ + int_u enforce + ) +{ + char_u *font_tag; + FontQueryInfo info; + int_u style; + + font_tag = alloc( MAX_FONT_TAG ); + if( font_tag != NULL ) + { + if( PfGenerateFontName( font_name, font_flags, font_size, + font_tag ) != NULL ) + { + /* Enforce some limits on the font used */ + style = PHFONT_INFO_FIXED; + + if( enforce & PF_STYLE_BOLD ) + style |= PHFONT_INFO_BOLD; + if( enforce & PF_STYLE_ANTIALIAS ) + style |= PHFONT_INFO_ALIAS; + if( enforce & PF_STYLE_ITALIC ) + style |= PHFONT_INFO_ITALIC; + + PfQueryFontInfo( font_tag, &info ); + + if( info.size == 0 ) + font_size = 0; + + /* Make sure font size matches, and that the font style + * at least has the bits we're checking for */ + if( font_size == info.size && + style == (info.style & style) ) + return( (GuiFont) font_tag ); + } + vim_free( font_tag ); + } + return( NULL ); +} + +/* + * Split up the vim font name + * + * vim_font is in the form of + * <name>:s<height>:a:b:i + * + * a = antialias + * b = bold + * i = italic + * + */ + + static int +gui_ph_parse_font_name( + char_u *vim_font, + char_u **font_name, + int_u *font_flags, + int_u *font_size ) +{ + char_u *mark; + int_u name_len, size; + + mark = vim_strchr( vim_font, ':' ); + if( mark == NULL ) + name_len = STRLEN( vim_font ); + else + name_len = (int_u) ( mark - vim_font ); + + *font_name = vim_strnsave( vim_font, name_len ); + if( *font_name != NULL ) + { + if( mark != NULL ) + { + while( *mark != NUL && *mark++ == ':') + { + switch( tolower( *mark++ ) ) + { + case 'a': *font_flags |= PF_STYLE_ANTIALIAS; break; + case 'b': *font_flags |= PF_STYLE_BOLD; break; + case 'i': *font_flags |= PF_STYLE_ITALIC; break; + + case 's': + size = getdigits( &mark ); + /* Restrict the size to some vague limits */ + if( size < 1 || size > 100 ) + size = 8; + + *font_size = size; + break; + + default: + break; + } + } + } + return( TRUE ); + } + return( FALSE ); +} + + int +gui_mch_init_font(char_u *vim_font_name, int fontset) +{ + char_u *font_tag; + char_u *font_name = NULL; + int_u font_flags = 0; + int_u font_size = 12; + + FontQueryInfo info; + PhRect_t extent; + + if( vim_font_name == NULL ) + { + /* Default font */ + vim_font_name = "PC Term"; + } + + if( STRCMP( vim_font_name, "*" ) == 0 ) + { + font_tag = PtFontSelection( gui.vimWindow, NULL, NULL, + "pcterm12", -1, PHFONT_FIXED, NULL ); + + if( font_tag == NULL ) + return( FAIL ); + + gui_mch_free_font( gui.norm_font ); + gui.norm_font = font_tag; + + PfQueryFontInfo( font_tag, &info ); + font_name = vim_strsave( info.font ); + } + else + { + if( gui_ph_parse_font_name( vim_font_name, &font_name, &font_flags, + &font_size ) == FALSE ) + return( FAIL ); + + font_tag = gui_ph_get_font( font_name, font_flags, font_size, 0 ); + if( font_tag == NULL ) + { + vim_free( font_name ); + return( FAIL ); + } + gui_mch_free_font( gui.norm_font ); + gui.norm_font = font_tag; + } + + gui_mch_free_font( gui.bold_font ); + gui.bold_font = gui_ph_get_font( font_name, font_flags | PF_STYLE_BOLD, + font_size, PF_STYLE_BOLD ); + + gui_mch_free_font( gui.ital_font ); + gui.ital_font = gui_ph_get_font( font_name, font_flags | PF_STYLE_ITALIC, + font_size, PF_STYLE_ITALIC ); + + /* This extent was brought to you by the letter 'g' */ + PfExtentText( &extent, NULL, font_tag, "g", 1 ); + + gui.char_width = extent.lr.x - extent.ul.x + 1; + gui.char_height = (- extent.ul.y) + extent.lr.y + 1; + gui.char_ascent = - extent.ul.y; + + vim_free( font_name ); + return( OK ); +} + + int +gui_mch_adjust_charsize(void) +{ + FontQueryInfo info; + + PfQueryFontInfo( gui.norm_font, &info ); + + gui.char_height = - info.ascender + info.descender + p_linespace; + gui.char_ascent = - info.ascender + p_linespace / 2; + + return( OK ); +} + + GuiFont +gui_mch_get_font(char_u *vim_font_name, int report_error) +{ + char_u *font_name; + char_u *font_tag; + int_u font_size = 12; + int_u font_flags = 0; + + if( gui_ph_parse_font_name( vim_font_name, &font_name, &font_flags, + &font_size ) != FALSE ) + { + font_tag = gui_ph_get_font( font_name, font_flags, font_size, -1 ); + vim_free( font_name ); + + if( font_tag != NULL ) + return( (GuiFont) font_tag ); + } + + if( report_error ) + EMSG2(e_font, vim_font_name ); + + return( FAIL ); +} + + void +gui_mch_set_font(GuiFont font) +{ + PgSetFont( font ); +} + + void +gui_mch_free_font(GuiFont font) +{ + vim_free( font ); +} + |