diff --git a/src/Makefile b/src/Makefile old mode 100644 new mode 100755 index e7927173..f4589ff3 --- a/src/Makefile +++ b/src/Makefile @@ -1,6 +1,6 @@ SRC := charset.c child.c config.c ctrls.c minibidi.c std.c term.c termclip.c \ termline.c termmouse.c termout.c winclip.c winctrls.c windialog.c wininput.c \ - winmain.c winprint.c wintext.c wintip.c xcwidth.c \ + winmain.c winprint.c wintext.c wintip.c xcwidth.c winsearch.c \ childxx.cc winxx.cc \ res.rc BIN ?= fatty.exe diff --git a/src/config.c b/src/config.c index 4f179dec..be23d228 100644 --- a/src/config.c +++ b/src/config.c @@ -26,6 +26,9 @@ const config default_cfg = { .tab_bg_colour = 0x000000, .tab_active_bg_colour = 0x323232, .tab_attention_bg_colour = 0x003200, + .search_fg_colour = 0x000000, + .search_bg_colour = 0x00DDDD, + .search_current_colour = 0x0099DD, .cursor_colour = 0xBFBFBF, .transparency = 0, .opaque_when_focused = false, @@ -47,6 +50,7 @@ const config default_cfg = { .window_shortcuts = true, .switch_shortcuts = true, .zoom_shortcuts = true, + .zoom_font_with_window = true, .alt_fn_shortcuts = true, .ctrl_shift_shortcuts = false, .break_string = "", @@ -68,6 +72,7 @@ const config default_cfg = { .scrollback_lines = 10000, .scroll_mod = MDK_SHIFT, .pgupdn_scroll = false, + .search_bar = "", // Terminal .term = "xterm", .answerback = "", @@ -139,6 +144,9 @@ options[] = { // Looks {"ForegroundColour", OPT_COLOUR, offcfg(fg_colour)}, {"BackgroundColour", OPT_COLOUR, offcfg(bg_colour)}, + {"SearchForegroundColour", OPT_COLOUR, offcfg(search_fg_colour)}, + {"SearchBackgroundColour", OPT_COLOUR, offcfg(search_bg_colour)}, + {"SearchCurrentColour", OPT_COLOUR, offcfg(search_current_colour)}, {"CursorColour", OPT_COLOUR, offcfg(cursor_colour)}, {"Transparency", OPT_TRANS, offcfg(transparency)}, {"OpaqueWhenFocused", OPT_BOOL, offcfg(opaque_when_focused)}, @@ -169,6 +177,7 @@ options[] = { {"WindowShortcuts", OPT_BOOL, offcfg(window_shortcuts)}, {"SwitchShortcuts", OPT_BOOL, offcfg(switch_shortcuts)}, {"ZoomShortcuts", OPT_BOOL, offcfg(zoom_shortcuts)}, + {"ZoomFontWithWindow", OPT_BOOL, offcfg(zoom_font_with_window)}, {"AltFnShortcuts", OPT_BOOL, offcfg(alt_fn_shortcuts)}, {"CtrlShiftShortcuts", OPT_BOOL, offcfg(ctrl_shift_shortcuts)}, {"Break", OPT_STRING, offcfg(break_string)}, @@ -192,6 +201,7 @@ options[] = { {"Scrollbar", OPT_SCROLLBAR, offcfg(scrollbar)}, {"ScrollMod", OPT_MOD, offcfg(scroll_mod)}, {"PgUpDnScroll", OPT_BOOL, offcfg(pgupdn_scroll)}, + {"SearchBar", OPT_STRING, offcfg(search_bar)}, // Terminal {"Term", OPT_STRING, offcfg(term)}, diff --git a/src/config.h b/src/config.h index 11b07c2c..ae550700 100644 --- a/src/config.h +++ b/src/config.h @@ -44,6 +44,7 @@ typedef struct { // Looks colour fg_colour, bg_colour, cursor_colour; colour tab_fg_colour, tab_bg_colour, tab_attention_bg_colour, tab_active_bg_colour; + colour search_fg_colour, search_bg_colour, search_current_colour; char transparency; bool opaque_when_focused; char cursor_type; @@ -64,6 +65,7 @@ typedef struct { bool window_shortcuts; bool switch_shortcuts; bool zoom_shortcuts; + bool zoom_font_with_window; bool alt_fn_shortcuts; bool ctrl_shift_shortcuts; string break_string; @@ -84,6 +86,7 @@ typedef struct { char scrollbar; char scroll_mod; bool pgupdn_scroll; + string search_bar; // Terminal string term; string answerback; diff --git a/src/term.c b/src/term.c old mode 100644 new mode 100755 index e86989f7..e5affed8 --- a/src/term.c +++ b/src/term.c @@ -4,11 +4,15 @@ // Adapted from code from PuTTY-0.60 by Simon Tatham and team. // Licensed under the terms of the GNU General Public License v3 or later. +#include +#include + #include "termpriv.h" #include "win.h" #include "charset.h" #include "child.h" +#include "winsearch.h" const cattr CATTR_DEFAULT = {.attr = ATTR_DEFAULT, .truefg = 0, .truebg = 0}; @@ -163,6 +167,9 @@ term_reset(struct term* term) term_clear_scrollback(term); win_reset_colours(); + + term->searched = false; + term->search_window_visible = false; } static void freelines(termlines* lines, int rows) { @@ -255,6 +262,247 @@ term_reconfig(struct term* term) term->vt220_keys = strstr(new_cfg.term, "vt220"); } +bool in_result(struct term* term, pos abspos, pos respos) { + return + (abspos.x + abspos.y * term->cols >= respos.x + respos.y * term->cols) && + (abspos.x + abspos.y * term->cols < respos.x + respos.y * term->cols + term->results.query_length); +} + +bool +in_results_recurse(struct term* term, pos abspos, int lo, int hi) { + if (hi - lo == 0) { + return false; + } + int mid = (lo + hi) / 2; + pos respos = term->results.results[mid]; + if (respos.x + respos.y * term->cols > abspos.x + abspos.y * term->cols) { + return in_results_recurse(term, abspos, lo, mid); + } else if (respos.x + respos.y * term->cols + term->results.query_length <= abspos.x + abspos.y * term->cols) { + return in_results_recurse(term, abspos, mid + 1, hi); + } + return true; +} + +int +in_results(struct term* term, pos scrpos) +{ + if (term->results.length == 0) { + return 0; + } + + pos abspos = { + .x = scrpos.x, + .y = scrpos.y + term->sblines + }; + + int match = in_results_recurse(term, abspos, 0, term->results.length); + match += in_result(term, abspos, term->results.results[term->results.current]); + return match; +} + +void +results_add(struct term* term, pos abspos) +{ + assert(term->results.capacity > 0); + if (term->results.length == term->results.capacity) { + term->results.capacity *= 2; + term->results.results = renewn(term->results.results, term->results.capacity); + } + + term->results.results[term->results.length] = abspos; + ++term->results.length; +} + +void +results_partial_clear(struct term* term, int pos) +{ + int i = term->results.length; + while (i > 0 && term->results.results[i - 1].y >= pos) { + --i; + } + term->results.length = i; +} + +void +term_set_search(struct term* term, wchar * needle) +{ + free(term->results.query); + term->results.query = needle; + + term->results.update_type = FULL_UPDATE; + term->results.query_length = wcslen(needle); +} + +void +circbuf_init(circbuf * cb, int sz) +{ + cb->capacity = sz; + cb->length = 0; + cb->start = 0; + cb->buf = newn(termline*, sz); +} + +void +circbuf_destroy(circbuf * cb) +{ + cb->capacity = 0; + cb->length = 0; + cb->start = 0; + + // Free any termlines we have left. + for (int i = 0; i < cb->capacity; ++i) { + if (cb->buf[i] == NULL) + continue; + release_line(cb->buf[i]); + } + free(cb->buf); + cb->buf = NULL; +} + +void +circbuf_push(circbuf * cb, termline * tl) +{ + int pos = (cb->start + cb->length) % cb->capacity; + + if (cb->length < cb->capacity) { + ++cb->length; + } else { + ++cb->start; + release_line(cb->buf[pos]); + } + cb->buf[pos] = tl; +} + +termline * +circbuf_get(circbuf * cb, int i) +{ + assert(i < cb->length); + return cb->buf[(cb->start + i) % cb->capacity]; +} + +void +term_update_search(struct term* term) +{ + if (term->results.update_type == DISABLE_UPDATE) + return; + if (term->results.update_type == NO_UPDATE) + return; + + int update_type = term->results.update_type; + term->results.update_type = NO_UPDATE; + + if (term->results.query_length == 0) + return; + + circbuf cb; + // Allocate room for the circular buffer of termlines. + int lcurr = 0; + if (update_type == PARTIAL_UPDATE) { + // How much of the backscroll we need to update on a partial update? + // Do a ceil: (x + y - 1) / y + // On query_length - 1 + int pstart = -((term->results.query_length + term->cols - 2) / term->cols) + term->sblines; + lcurr = lcurr > pstart ? lcurr:pstart; + results_partial_clear(term, lcurr); + } else { + term_clear_results(term); + } + int llen = term->results.query_length / term->cols + 1; + if (llen < 2) + llen = 2; + + circbuf_init(&cb, llen); + + // Fill in our starting set of termlines. + for (int i = lcurr; i < term->rows + term->sblines && cb.length < cb.capacity; ++i) { + circbuf_push(&cb, fetch_line(term, i - term->sblines)); + } + + int cpos = term->cols * lcurr; + int npos = 0; + int end = term->cols * (term->rows + term->sblines); + + // Loop over every character and search for query. + while (cpos < end) { + // Determine the current position. + int x = (cpos % term->cols); + int y = (cpos / term->cols); + + // If our current position isn't in the buffer, add it in. + if (y - lcurr >= llen) { + circbuf_push(&cb, fetch_line(term, lcurr + llen - term->sblines)); + ++lcurr; + } + termline * lll = circbuf_get(&cb, y - lcurr); + termchar * chr = lll->chars + x; + + if (npos == 0 && cpos + term->results.query_length >= end) + break; + + if (chr->chr != term->results.query[npos]) { + // Skip the second cell of any wide characters + if (chr->chr == UCSWIDE) { + ++cpos; continue; + } + cpos -= npos - 1; + npos = 0; + continue; + } + + ++npos; + + if (term->results.query_length == npos) { + int start = cpos - npos + 1; + pos respos = { + .x = start % term->cols, + .y = start / term->cols, + }; + results_add(term, respos); + npos = 0; + } + + ++cpos; + } + + circbuf_destroy(&cb); +} + +void +term_schedule_search_update(struct term* term) +{ + if (term->results.update_type != DISABLE_UPDATE) + term->results.update_type = FULL_UPDATE; +} + +void +term_schedule_search_partial_update(struct term* term) +{ + if (term->results.update_type != DISABLE_UPDATE) { + if (term->results.update_type == NO_UPDATE) { + term->results.update_type = PARTIAL_UPDATE; + } + } +} + +void +term_clear_results(struct term* term) +{ + term->results.results = renewn(term->results.results, 16); + term->results.current = 0; + term->results.length = 0; + term->results.capacity = 16; +} + +void +term_clear_search(struct term* term) +{ + term_clear_results(term); + term->results.update_type = NO_UPDATE; + free(term->results.query); + term->results.query = NULL; + term->results.query_length = 0; +} + static void scrollback_push(struct term* term, uchar *line) { @@ -717,6 +965,16 @@ term_paint(struct term* term) if (term->in_vbell || selected) tattr.attr ^= ATTR_REVERSE; + int match = in_results(term, scrpos); + if (match > 0) { + tattr.attr |= TATTR_RESULT; + if (match > 1) { + tattr.attr |= TATTR_CURRESULT; + } + } else { + tattr.attr &= ~TATTR_RESULT; + } + /* 'Real' blinking ? */ if (term->blink_is_real && (tattr.attr & ATTR_BLINK)) { if (term->has_focus && term->tblinker) diff --git a/src/term.h b/src/term.h old mode 100644 new mode 100755 index 89acfdf9..e85d3a35 --- a/src/term.h +++ b/src/term.h @@ -92,22 +92,25 @@ enum { ATTR_ITALIC = 0x00100000u, ATTR_UNDER = 0x00200000u, ATTR_REVERSE = 0x00400000u, - ATTR_STRIKEOUT = 0x400000000u, /* not supported */ + ATTR_STRIKEOUT = 0x40000000u, ATTR_INVISIBLE = 0x00800000u, ATTR_BLINK = 0x01000000u, - ATTR_DOUBLYUND = 0x200000000u, /* not supported */ - ATTR_OVERL = 0x100000000u, /* not supported */ + ATTR_DOUBLYUND = 0x20000000u, + ATTR_OVERL = 0x10000000u, ATTR_PROTECTED = 0x02000000u, ATTR_WIDE = 0x04000000u, ATTR_NARROW = 0x08000000u, - TATTR_RIGHTCURS = 0x10000000u, /* cursor-on-RHS */ - TATTR_PASCURS = 0x20000000u, /* passive cursor (box) */ - TATTR_ACTCURS = 0x40000000u, /* active cursor (block) */ - TATTR_COMBINING = 0x80000000u, /* combining characters */ + TATTR_RIGHTCURS = 0x0100000000u, /* cursor-on-RHS */ + TATTR_PASCURS = 0x0200000000u, /* passive cursor (box) */ + TATTR_ACTCURS = 0x0400000000u, /* active cursor (block) */ + TATTR_COMBINING = 0x0800000000u, /* combining characters */ - DATTR_STARTRUN = 0x80000000u, /* start of redraw run */ - DATTR_MASK = 0xF0000000u, + TATTR_RESULT = 0x1000000000u, /* search result */ + TATTR_CURRESULT = 0x2000000000u, /* current search result */ + + DATTR_STARTRUN = 0x0800000000u, /* start of redraw run */ + DATTR_MASK = 0x0F00000000u, }; /* Line attributes. @@ -226,6 +229,30 @@ typedef struct belltime { uint ticks; } belltime; +enum { + NO_UPDATE = 0, + PARTIAL_UPDATE = 1, + FULL_UPDATE = 2, + DISABLE_UPDATE = 3 +}; + +typedef struct { + termline ** buf; + int start; + int length; + int capacity; +} circbuf; + +typedef struct { + pos * results; + wchar * query; + int query_length; + int capacity; + int current; + int length; + int update_type; +} termresults; + typedef struct { short x, y; cattr attr; @@ -369,6 +396,11 @@ struct term { bidi_cache_entry *pre_bidi_cache, *post_bidi_cache; int bidi_cache_size; + // Search results + termresults results; + bool searched; + bool search_window_visible; + struct child* child; }; @@ -400,4 +432,11 @@ int term_cursor_type(struct term* term); bool term_cursor_blinks(struct term* term); void term_hide_cursor(struct term* term); +void term_set_search(struct term* term, wchar * needle); +void term_schedule_search_partial_update(struct term* term); +void term_schedule_search_update(struct term* term); +void term_update_search(struct term* term); +void term_clear_results(struct term* term); +void term_clear_search(struct term* term); + #endif diff --git a/src/termout.c b/src/termout.c index f155882d..8136b158 100644 --- a/src/termout.c +++ b/src/termout.c @@ -1021,6 +1021,8 @@ do_cmd(struct term* term) child_printf(term->child, "\e]701;%s\e\\", cs_get_locale()); else cs_set_locale(s); + when 7721: // Copy window title to clipboard. + win_copy_title(); when 7770: // Change font size. if (!strcmp(s, "?")) child_printf(term->child, "\e]7770;%u\e\\", win_get_font_size()); @@ -1347,6 +1349,7 @@ term_write(struct term* term, const char *buf, uint len) } } } + term_schedule_search_partial_update(term); win_schedule_update(); if (term->printing) { printer_write(term->printbuf, term->printbuf_pos); diff --git a/src/termpriv.h b/src/termpriv.h old mode 100644 new mode 100755 diff --git a/src/win.h b/src/win.h index f3ce2660..8543f784 100644 --- a/src/win.h +++ b/src/win.h @@ -25,6 +25,7 @@ void win_bell(struct term* term); void win_set_title(struct term* term, char *); void win_save_title(void); void win_restore_title(void); +void win_copy_title(void); colour win_get_colour(colour_i); void win_set_colour(colour_i, colour); diff --git a/src/win.hh b/src/win.hh old mode 100644 new mode 100755 index f931f588..98ef0bf8 --- a/src/win.hh +++ b/src/win.hh @@ -2,6 +2,8 @@ #include +#include + extern "C" { // some typedef for mintty header compat typedef unsigned int uint; diff --git a/src/wininput.c b/src/wininput.c index 11cd3bb5..277f5778 100644 --- a/src/wininput.c +++ b/src/wininput.c @@ -3,7 +3,9 @@ // Based on code from mintty by Andy Koppe // Licensed under the terms of the GNU General Public License v3 or later. +#include "term.h" #include "winpriv.h" +#include "winsearch.h" #include "charset.h" #include "child.h" @@ -56,6 +58,11 @@ win_update_menus(void) clip ? "&Paste\tShift+Ins" : ct_sh ? "&Paste\tCtrl+Shift+V" : "&Paste" ); + ModifyMenu( + menu, IDM_SEARCH, 0, IDM_SEARCH, + alt_fn ? "S&earch\tAlt+F3" : ct_sh ? "S&earch\tCtrl+Shift+H" : "S&earch" + ); + ModifyMenu( menu, IDM_RESET, 0, IDM_RESET, alt_fn ? "&Reset\tAlt+F8" : ct_sh ? "&Reset\tCtrl+Shift+R" : "&Reset" @@ -106,6 +113,7 @@ win_init_menus(void) AppendMenu(menu, MF_ENABLED, IDM_PASTE, 0); AppendMenu(menu, MF_ENABLED, IDM_SELALL, "Select &All"); AppendMenu(menu, MF_SEPARATOR, 0, 0); + AppendMenu(menu, MF_ENABLED, IDM_SEARCH, 0); AppendMenu(menu, MF_ENABLED, IDM_RESET, 0); AppendMenu(menu, MF_SEPARATOR, 0, 0); AppendMenu(menu, MF_ENABLED | MF_UNCHECKED, IDM_DEFSIZE_ZOOM, 0); @@ -250,6 +258,8 @@ win_mouse_click(mouse_button b, LPARAM lp) p.x != last_click_pos.x || p.y != last_click_pos.y || t - last_time > GetDoubleClickTime() || ++count > 3) count = 1; + + SetFocus(wnd); // in case focus was in search bar term_mouse_click(win_active_terminal(), b, mods, p, count); last_pos = (pos){INT_MIN, INT_MIN}; last_click_pos = p; @@ -433,6 +443,7 @@ win_key_down(WPARAM wp, LPARAM lp) if (!ctrl) { switch (key) { when VK_F2: send_syscommand(IDM_NEW); + when VK_F3: send_syscommand(IDM_SEARCH); when VK_F4: send_syscommand(SC_CLOSE); when VK_F8: send_syscommand(IDM_RESET); when VK_F10: send_syscommand(IDM_DEFSIZE_ZOOM); @@ -456,6 +467,7 @@ win_key_down(WPARAM wp, LPARAM lp) when 'S': send_syscommand(IDM_FLIPSCREEN); when 'T': win_tab_create(); when 'W': child_terminate(active_term->child); + when 'H': send_syscommand(IDM_SEARCH); } return 1; } diff --git a/src/winmain.c b/src/winmain.c index d6a64f26..d058f544 100755 --- a/src/winmain.c +++ b/src/winmain.c @@ -5,7 +5,10 @@ // Licensed under the terms of the GNU General Public License v3 or later. #define dont_debug_resize + +#include "term.h" #include "winpriv.h" +#include "winsearch.h" #include "term.h" #include "appinfo.h" @@ -34,7 +37,7 @@ static ATOM class_atom; static int extra_width, extra_height; static bool fullscr_on_max; static bool resizing; -static int zoom_token = 0; +static int zoom_token = 0; // for heuristic handling of Shift zoom (#467, #476) static string border_style = 0; static bool center = false; @@ -377,6 +380,10 @@ win_adapt_term_size(bool sync_size_with_font, bool scale_font_with_size) win_set_font_size(font_size1, false); } + if (win_search_visible()) { + term_height -= SEARCHBAR_HEIGHT; + } + int cols = max(1, term_width / font_width); int rows = max(1, term_height / font_height); if (rows != term->rows || cols != term->cols) { @@ -385,6 +392,10 @@ win_adapt_term_size(bool sync_size_with_font, bool scale_font_with_size) child_resize(term->child, &ws); } win_invalidate_all(); + + win_update_search(); + term_schedule_search_update(term); + win_schedule_update(); } bool @@ -672,10 +683,14 @@ win_proc(HWND wnd, UINT message, WPARAM wp, LPARAM lp) #endif when IDM_FULLSCREEN or IDM_FULLSCREEN_ZOOM: if ((wp & ~0xF) == IDM_FULLSCREEN_ZOOM) - zoom_token = 1; + zoom_token = 4; // override cfg.zoom_font_with_window == 0 else zoom_token = -4; win_maximise(win_is_fullscreen ? 0 : 2); + + term_schedule_search_update(term); + win_update_search(); + when IDM_SEARCH: win_open_search(); when IDM_FLIPSCREEN: term_flip_screen(term); when IDM_OPTIONS: win_open_config(); when IDM_NEW: child_fork(term->child, main_argc, main_argv); @@ -826,13 +841,16 @@ win_proc(HWND wnd, UINT message, WPARAM wp, LPARAM lp) // - triggered by Windows shortcut (with Windows key) // - triggered by Ctrl+Shift+F (zoom_token < 0) if ((zoom_token >= 0) && !(GetKeyState(VK_LWIN) & 0x80)) - zoom_token = 1; + if (zoom_token < 1) // accept overriding zoom_token 4 + zoom_token = 1; #else // - triggered by Windows shortcut (with Windows key) if (!(GetKeyState(VK_LWIN) & 0x80)) - zoom_token = 1; + if (zoom_token < 1) // accept overriding zoom_token 4 + zoom_token = 1; #endif - bool scale_font = (zoom_token > 0) && (GetKeyState(VK_SHIFT) & 0x80); + bool scale_font = (cfg.zoom_font_with_window || zoom_token > 2) + && (zoom_token > 0) && (GetKeyState(VK_SHIFT) & 0x80); win_adapt_term_size(false, scale_font); if (zoom_token > 0) zoom_token = zoom_token >> 1; diff --git a/src/winpriv.h b/src/winpriv.h index 23f94ee0..cb388706 100644 --- a/src/winpriv.h +++ b/src/winpriv.h @@ -50,8 +50,6 @@ bool win_key_up(WPARAM, LPARAM); void win_init_drop_target(void); -void win_copy_title(void); - void win_switch(bool back, bool alternate); void win_set_ime_open(bool); diff --git a/src/winsearch.c b/src/winsearch.c new file mode 100755 index 00000000..5cf7d81e --- /dev/null +++ b/src/winsearch.c @@ -0,0 +1,319 @@ +// winsearch.c (part of mintty) +// Copyright 2015 Kai (kiwiz) +// Licensed under the terms of the GNU General Public License v3 or later. + +#include +#include + +#include "winpriv.h" +#include "term.h" +#include "winsearch.h" + +void +scroll_to_result(struct term* term) +{ + if (term->results.length == 0) { + return; + } + + // If visible, don't do anything. + pos * res = term->results.results + term->results.current; + int y = res->y - term->sblines; + int delta = 0; + if (y < term->disptop) { + delta = y - term->disptop; + } + if (y >= term->disptop + term->rows) { + delta = y - (term->disptop + term->rows - 1); + } + // Scroll if we must! + if (delta != 0) { + term_scroll(term, 0, delta); + } +} + +void +next_result(struct term* term) +{ + if (term->results.length == 0) { + return; + } + if (term->searched) { + term->results.current = (term->results.current + 1) % term->results.length; + } + else { + term->searched = true; + } + scroll_to_result(term); +} + +void +prev_result(struct term* term) +{ + if (term->results.length == 0) { + return; + } + if (term->searched) { + term->results.current = (term->results.current - 1) % term->results.length; + // Handle wrap arounds correctly. + if (term->results.current < 0) { + term->results.current += term->results.length; + } + } + else { + term->searched = true; + } + scroll_to_result(term); +} + +static LRESULT CALLBACK +edit_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) +{ + struct term* term = win_active_terminal(); + + MSG mesg = {.hwnd = hwnd, .message = msg, .wParam = wp, .lParam = lp}; + TranslateMessage(&mesg); + + switch (mesg.message) { + when WM_KEYDOWN or WM_SYSKEYDOWN: + switch (mesg.wParam) { + when VK_ESCAPE: + term_clear_search(term); + win_hide_search(); + win_schedule_update(); + return 0; + when VK_TAB: + // FIXME: Still causes beeping... + SetFocus(wnd); + return 0; + when VK_RETURN: + if (GetKeyState(VK_SHIFT) < 0) { + prev_result(term); + } + else { + next_result(term); + } + win_schedule_update(); + return 0; + } + when WM_CHAR: + switch (mesg.wParam) { + // Necessary to stop the beeping! + when VK_RETURN or VK_ESCAPE or VK_MENU: + return 0; + } + when WM_MBUTTONUP: { + mesg.message = WM_PASTE; + mesg.wParam = 0; + mesg.lParam = 0; + } + } + + return CallWindowProc(default_edit_proc, mesg.hwnd, mesg.message, mesg.wParam, mesg.lParam); +} + +static LRESULT CALLBACK +search_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) +{ + struct term* term = win_active_terminal(); + + bool update = false; + switch (msg) { + when WM_COMMAND: + switch (HIWORD(wp)) { + when BN_CLICKED: // Equivalent to STN_CLICKED + if (lp == (long)search_prev_wnd) { + prev_result(term); + } + if (lp == (long)search_next_wnd) { + next_result(term); + } + if (lp == (long)search_close_wnd) { + term_clear_search(term); + win_hide_search(); + win_schedule_update(); + } + win_schedule_update(); + return 0; + when EN_UPDATE: + update = true; + } + when WM_SHOWWINDOW: + if (wp) { + update = true; + } + } + + if (update && (term->results.update_type != DISABLE_UPDATE)) + { + int len = GetWindowTextLengthW(search_edit_wnd) + 1; + wchar * buf = malloc(sizeof(wchar) * len); + GetWindowTextW(search_edit_wnd, buf, len); + term_set_search(term, buf); + term_update_search(term); + term->searched = false; + win_schedule_update(); + return 0; + } + + return CallWindowProc(DefWindowProc, hwnd, msg, wp, lp); +} + +static void +place_field(int * curpoi, int width, int * pospoi) +{ + if (* pospoi < 0) { + * pospoi = * curpoi; + (* curpoi) += width; + } +} + +void +win_toggle_search(bool show, bool focus) +{ + RECT cr; + GetClientRect(wnd, &cr); + + int width = cr.right - cr.left; + int height = SEARCHBAR_HEIGHT; + int margin = 2; + int button_width = 20; + int edit_width = width - button_width * 3 - margin * 2; + int ctrl_height = height - margin * 2; + + const char * search_bar = cfg.search_bar; + int pos_close = -1; + int pos_prev = -1; + int pos_next = -1; + int pos_edit = -1; + int barpos = margin; + while (search_bar && * search_bar) + switch (* search_bar ++) { + when 'x' or 'X': + place_field(& barpos, button_width, & pos_close); + when '<': + place_field(& barpos, button_width, & pos_prev); + when '>': + place_field(& barpos, button_width, & pos_next); + when 's' or 'S': + place_field(& barpos, edit_width, & pos_edit); + } + place_field(& barpos, button_width, & pos_close); + place_field(& barpos, button_width, & pos_prev); + place_field(& barpos, button_width, & pos_next); + place_field(& barpos, edit_width, & pos_edit); + + // Set up our global variables. + if (!search_initialised) { + RegisterClass(&(WNDCLASS){ + .style = 0, + .lpfnWndProc = search_proc, + .cbClsExtra = 0, + .cbWndExtra = 0, + .hInstance = inst, + .hIcon = NULL, + .hCursor = NULL, + .hbrBackground = (HBRUSH)(COLOR_3DFACE + 1), + .lpszMenuName = NULL, + .lpszClassName = SEARCHBARCLASS + }); + + search_wnd = CreateWindowEx(0, SEARCHBARCLASS, "", WS_CHILD, 0, 0, 0, 0, wnd, 0, inst, NULL); + + search_close_wnd = CreateWindowExW(0, L"BUTTON", L"X", WS_CHILD | WS_VISIBLE, + pos_close, margin, button_width, ctrl_height, + search_wnd, NULL, inst, NULL); + search_prev_wnd = CreateWindowExW(0, L"BUTTON", L"◀", WS_CHILD | WS_VISIBLE, + pos_prev, margin, button_width, ctrl_height, + search_wnd, NULL, inst, NULL); + search_next_wnd = CreateWindowExW(0, L"BUTTON", L"▶", WS_CHILD | WS_VISIBLE, + pos_next, margin, button_width, ctrl_height, + search_wnd, NULL, inst, NULL); + search_edit_wnd = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "", WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL, + 0, 0, 0, 0, + search_wnd, NULL, inst, NULL); + + search_font = CreateFont(ctrl_height - 4, 0, 0, 0, FW_DONTCARE, false, false, false, + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + DEFAULT_QUALITY, FIXED_PITCH | FF_DONTCARE, + cfg.font.name); + SendMessage(search_edit_wnd, WM_SETFONT, (WPARAM)search_font, 1); + + default_edit_proc = (WNDPROC)SetWindowLongPtrW(search_edit_wnd, GWLP_WNDPROC, (long)edit_proc); + + search_initialised = true; + } + + if (show) { + SetWindowPos(search_wnd, 0, + cr.right - width, cr.bottom - height, + width, height, + SWP_NOZORDER); + SetWindowPos(search_edit_wnd, 0, + pos_edit, margin, + edit_width, ctrl_height, + SWP_NOZORDER); + if (focus) { + SendMessage(search_edit_wnd, EM_SETSEL, 0, -1); + SetFocus(search_edit_wnd); + } + } + else { + SetFocus(wnd); + } + + ShowWindow(search_wnd, show ? SW_SHOW : SW_HIDE); +} + +void +win_open_search() +{ + struct term* term = win_active_terminal(); + + win_toggle_search(true, true); + term->search_window_visible = true; + win_adapt_term_size(false, false); +} + +void +win_hide_search(void) +{ + struct term* term = win_active_terminal(); + + win_toggle_search(false, false); + term->search_window_visible = false; + win_adapt_term_size(false, false); + term->searched = false; +} + +void win_update_search(void) +{ + if (win_search_visible()) { + win_toggle_search(true, false); + } +} + +void +win_paint_exclude_search(HDC dc) +{ + if (!win_search_visible()) { + return; + } + RECT cr; + POINT p = {.x = 0, .y = 0}; + GetWindowRect(search_wnd, &cr); + ClientToScreen(wnd, &p); + + cr.left -= p.x; + cr.right -= p.x; + cr.top -= p.y; + cr.bottom -= p.y; + ExcludeClipRect(dc, cr.left, cr.top, cr.right, cr.bottom); +} + +bool win_search_visible(void) +{ + struct term* term = win_active_terminal(); + + return term->search_window_visible; +} diff --git a/src/winsearch.h b/src/winsearch.h new file mode 100755 index 00000000..693b1dea --- /dev/null +++ b/src/winsearch.h @@ -0,0 +1,23 @@ +#ifndef TERMSEARCH_H +#define TERMSEARCH_H + +#define SEARCHBARCLASS "SearchBar" +#define SEARCHBAR_HEIGHT 26 + +bool search_initialised; +HWND search_wnd; +HWND search_close_wnd; +HWND search_prev_wnd; +HWND search_next_wnd; +HWND search_edit_wnd; +WNDPROC default_edit_proc; +HFONT search_font; + +void win_toggle_search(bool show, bool focus); +void win_open_search(void); +void win_hide_search(void); +void win_update_search(void); +void win_paint_exclude_search(HDC dc); +bool win_search_visible(void); + +#endif diff --git a/src/wintext.c b/src/wintext.c old mode 100644 new mode 100755 index 14109ae1..fb007db1 --- a/src/wintext.c +++ b/src/wintext.c @@ -4,7 +4,9 @@ // Adapted from code from PuTTY-0.60 by Simon Tatham and team. // Licensed under the terms of the GNU General Public License v3 or later. +#include "term.h" #include "winpriv.h" +#include "winsearch.h" #include "minibidi.h" @@ -86,14 +88,6 @@ COLORREF colours[COLOUR_NUM]; int g_render_tab_height = 0; -static colour -brighten(colour c) -{ - uint r = red(c), g = green(c), b = blue(c); - uint s = min(85, 255 - max(max(r, g), b)); - return make_colour(r + s, g + s, b + s); -} - static uint colour_dist(colour a, colour b) { @@ -103,6 +97,30 @@ colour_dist(colour a, colour b) 1 * sqr(blue(a) - blue(b)); } +static colour +brighten(colour c) +{ + uint r = red(c), g = green(c), b = blue(c); + +// uint s = min(85, 255 - max(max(r, g), b)); +// colour bright = make_colour(r + s, g + s, b + s); + + float dim = 0.4; // MUST be >= 0 AND <= 1 ! + int r_ = r + (255 - r) * dim; + int g_ = g + (255 - g) * dim; + int b_ = b + (255 - b) * dim; + colour bright = make_colour(r_, g_, b_); + if (colour_dist(c, bright) < 33333) { + dim = 0.2; + r_ = r * (1 - dim); + g_ = g * (1 - dim); + b_ = b * (1 - dim); + bright = make_colour(r_, g_, b_); + } + + return bright; +} + static uint get_font_quality(void) { return @@ -392,6 +410,10 @@ do_update(void) update_state = UPDATE_BLOCKED; dc = GetDC(wnd); + + win_paint_exclude_search(dc); + term_update_search(term); + term_paint(term); ReleaseDC(wnd, dc); @@ -610,6 +632,15 @@ win_text(int x, int y, wchar *text, int len, cattr attr, int lattr) bool has_cursor = attr.attr & (TATTR_ACTCURS | TATTR_PASCURS); colour cursor_colour = 0; + if (attr.attr & TATTR_CURRESULT) { + bg = cfg.search_current_colour; + fg = cfg.search_fg_colour; + } + else if (attr.attr & TATTR_RESULT) { + bg = cfg.search_bg_colour; + fg = cfg.search_fg_colour; + } + if (has_cursor) { cursor_colour = colours[ime_open ? IME_CURSOR_COLOUR_I : CURSOR_COLOUR_I]; diff --git a/src/winxx.cc b/src/winxx.cc index f7c600d1..133e0b40 100755 --- a/src/winxx.cc +++ b/src/winxx.cc @@ -14,10 +14,12 @@ #include +#include + extern "C" { #include "winpriv.h" +#include "winsearch.h" -#include } #define lengthof(array) (sizeof(array) / sizeof(*(array))) @@ -133,8 +135,25 @@ static void set_active_tab(unsigned int index) { term_set_focus(tab.terminal.get(), &tab == active); } active->info.attention = false; + SetFocus(wnd); + + struct term *term = active->terminal.get(); + term->results.update_type = DISABLE_UPDATE; + if (IsWindowVisible(search_wnd) != term->search_window_visible) { + ShowWindow(search_wnd, term->search_window_visible ? SW_SHOW : SW_HIDE); + } + if (term->search_window_visible) { + if (term->results.query) { + SetWindowTextW(search_edit_wnd, term->results.query); + } + else { + SetWindowTextW(search_edit_wnd, L""); + } + } + update_window_state(); win_invalidate_all(); + term->results.update_type = NO_UPDATE; } static unsigned int rel_index(int change) {