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 0000000000..f2bd5f09b0 --- /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, |