777 num -= w->hscroll.cap; |
773 num -= w->hscroll.cap; |
778 if (num < 0) num = 0; |
774 if (num < 0) num = 0; |
779 if (num < w->hscroll.pos) w->hscroll.pos = num; |
775 if (num < w->hscroll.pos) w->hscroll.pos = num; |
780 } |
776 } |
781 |
777 |
782 /* Get the count of characters in the string as well as the width in pixels |
778 static void DelChar(Textbuf *tb) |
783 * [IN]buf: string to be checked |
779 { |
784 * [OUT]count: gets set to the count of characters |
780 tb->width -= GetCharacterWidth(tb->buf[tb->caretpos]); |
785 * [OUT]width: gets set to the pixels width */ |
781 memmove(tb->buf + tb->caretpos, tb->buf + tb->caretpos + 1, tb->length - tb->caretpos); |
786 static void GetCurrentStringSize(const char *buf, int *count, int *width) |
782 tb->length--; |
787 { |
783 } |
788 *count = 0; |
784 |
789 *width = -1; |
785 /** |
790 |
786 * Delete a character from a textbuffer, either with 'Delete' or 'Backspace' |
791 do { |
787 * The character is delete from the position the caret is at |
792 if (*++buf == 0) |
788 * @param tb @Textbuf type to be changed |
793 break; |
789 * @param delmode Type of deletion, either @WKC_BACKSPACE or @WKC_DELETE |
794 (*count)++; |
790 * @return Return true on successfull change of Textbuf, or false otherwise |
795 (*width) += _stringwidth_table[(byte)*buf - 32]; |
791 */ |
796 } while (1); |
792 bool DeleteTextBufferChar(Textbuf *tb, int delmode) |
|
793 { |
|
794 if (delmode == WKC_BACKSPACE && tb->caretpos != 0) { |
|
795 tb->caretpos--; |
|
796 tb->caretxoffs -= GetCharacterWidth(tb->buf[tb->caretpos]); |
|
797 |
|
798 DelChar(tb); |
|
799 return true; |
|
800 } else if (delmode == WKC_DELETE && tb->caretpos < tb->length) { |
|
801 DelChar(tb); |
|
802 return true; |
|
803 } |
|
804 |
|
805 return false; |
|
806 } |
|
807 |
|
808 /** |
|
809 * Insert a character to a textbuffer. If maxlength is zero, we don't care about |
|
810 * the screenlength but only about the physical length of the string |
|
811 * @param tb @Textbuf type to be changed |
|
812 * @param key Character to be inserted |
|
813 * @return Return true on successfull change of Textbuf, or false otherwise |
|
814 */ |
|
815 bool InsertTextBufferChar(Textbuf *tb, byte key) |
|
816 { |
|
817 const byte charwidth = GetCharacterWidth(key); |
|
818 if (tb->length < tb->maxlength && (tb->maxwidth == 0 || tb->width + charwidth <= tb->maxwidth)) { |
|
819 memmove(tb->buf + tb->caretpos + 1, tb->buf + tb->caretpos, tb->length - tb->caretpos); |
|
820 tb->buf[tb->caretpos] = key; |
|
821 tb->length++; |
|
822 tb->width += charwidth; |
|
823 |
|
824 tb->caretpos++; |
|
825 tb->caretxoffs += charwidth; |
|
826 return true; |
|
827 } |
|
828 return false; |
|
829 } |
|
830 |
|
831 /** |
|
832 * Handle text navigation with arrow keys left/right. |
|
833 * This defines where the caret will blink and the next characer interaction will occur |
|
834 * @param tb @Textbuf type where navigation occurs |
|
835 * @param navmode Direction in which navigation occurs @WKC_LEFT, @WKC_RIGHT, @WKC_END, @WKC_HOME |
|
836 * @return Return true on successfull change of Textbuf, or false otherwise |
|
837 */ |
|
838 bool MoveTextBufferPos(Textbuf *tb, int navmode) |
|
839 { |
|
840 switch (navmode) { |
|
841 case WKC_LEFT: |
|
842 if (tb->caretpos != 0) { |
|
843 tb->caretpos--; |
|
844 tb->caretxoffs -= GetCharacterWidth(tb->buf[tb->caretpos]); |
|
845 return true; |
|
846 } |
|
847 break; |
|
848 case WKC_RIGHT: |
|
849 if (tb->caretpos < tb->length) { |
|
850 tb->caretxoffs += GetCharacterWidth(tb->buf[tb->caretpos]); |
|
851 tb->caretpos++; |
|
852 return true; |
|
853 } |
|
854 break; |
|
855 case WKC_HOME: |
|
856 tb->caretpos = 0; |
|
857 tb->caretxoffs = 0; |
|
858 return true; |
|
859 case WKC_END: |
|
860 tb->caretpos = tb->length; |
|
861 tb->caretxoffs = tb->width; |
|
862 return true; |
|
863 } |
|
864 |
|
865 return false; |
|
866 } |
|
867 |
|
868 /** |
|
869 * Update @Textbuf type with its actual physical character and screenlength |
|
870 * Get the count of characters in the string as well as the width in pixels. |
|
871 * Useful when copying in a larger amount of text at once |
|
872 * @param tb @Textbuf type which length is calculated |
|
873 */ |
|
874 void UpdateTextBufferSize(Textbuf *tb) |
|
875 { |
|
876 char *buf; |
|
877 tb->length = 0; |
|
878 tb->width = 0; |
|
879 |
|
880 for (buf = tb->buf; *buf != '\0' && tb->length <= tb->maxlength; buf++) { |
|
881 tb->length++; |
|
882 tb->width += GetCharacterWidth((byte)*buf); |
|
883 } |
|
884 |
|
885 tb->caretpos = tb->length; |
|
886 tb->caretxoffs = tb->width; |
797 } |
887 } |
798 |
888 |
799 int HandleEditBoxKey(Window *w, int wid, WindowEvent *we) |
889 int HandleEditBoxKey(Window *w, int wid, WindowEvent *we) |
800 { |
890 { |
801 int width,count; |
|
802 int key = we->keypress.ascii; |
|
803 |
|
804 we->keypress.cont = false; |
891 we->keypress.cont = false; |
805 |
892 |
806 if (we->keypress.keycode == WKC_ESC) { |
893 switch (we->keypress.keycode) { |
807 return 2; |
894 case WKC_ESC: return 2; |
808 } else if (we->keypress.keycode == WKC_RETURN) { |
895 case WKC_RETURN: case WKC_NUM_ENTER: return 1; |
809 return 1; |
896 case (WKC_CTRL | 'V'): |
810 #ifdef WIN32 |
897 if (InsertTextBufferClipboard(&WP(w, querystr_d).text)) |
811 } else if (we->keypress.keycode == (WKC_CTRL | 'V')) { |
|
812 if (IsClipboardFormatAvailable(CF_TEXT)) { |
|
813 const byte* data; |
|
814 HGLOBAL cbuf; |
|
815 |
|
816 OpenClipboard(NULL); |
|
817 cbuf = GetClipboardData(CF_TEXT); |
|
818 data = GlobalLock(cbuf); // clipboard data |
|
819 |
|
820 GetCurrentStringSize(WP(w,querystr_d).buf - 1, &count, &width); |
|
821 |
|
822 /* IS_INT_INSIDE = filter for ascii-function codes like BELL and so on [we need an special filter here later] */ |
|
823 for (; (IS_INT_INSIDE(*data, ' ', 256)) && // valid ASCII char |
|
824 (count < WP(w,querystr_d).maxlen - 1 && // max charcount; always allow for terminating '\0' |
|
825 width + _stringwidth_table[(int)(*data) - 32] <= WP(w,querystr_d).maxwidth); ++data) { // max screensize |
|
826 |
|
827 // append data and update size parameters |
|
828 WP(w,querystr_d).buf[count] = *data; |
|
829 count++; |
|
830 width += _stringwidth_table[*data - 32]; |
|
831 } |
|
832 WP(w,querystr_d).buf[count + 1] = '\0'; |
|
833 |
|
834 GlobalUnlock(cbuf); |
|
835 CloseClipboard(); |
|
836 InvalidateWidget(w, wid); |
898 InvalidateWidget(w, wid); |
837 } |
899 break; |
838 #endif |
900 case WKC_BACKSPACE: case WKC_DELETE: |
839 } else { |
901 if (DeleteTextBufferChar(&WP(w, querystr_d).text, we->keypress.keycode)) |
840 GetCurrentStringSize(WP(w,querystr_d).buf - 1, &count, &width); |
902 InvalidateWidget(w, wid); |
841 |
903 break; |
842 if (we->keypress.keycode == WKC_BACKSPACE) { |
904 case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME: |
843 if (count != 0) { |
905 if (MoveTextBufferPos(&WP(w, querystr_d).text, we->keypress.keycode)) |
844 WP(w,querystr_d).buf[count-1] = 0; |
906 InvalidateWidget(w, wid); |
|
907 break; |
|
908 default: |
|
909 if (IsValidAsciiChar(we->keypress.ascii)) { |
|
910 if (InsertTextBufferChar(&WP(w, querystr_d).text, we->keypress.ascii)) |
845 InvalidateWidget(w, wid); |
911 InvalidateWidget(w, wid); |
846 } |
|
847 } else if (IS_INT_INSIDE((key = we->keypress.ascii), 32, 256)) { |
|
848 if (count < WP(w,querystr_d).maxlen && width + _stringwidth_table[key - 32] <= WP(w,querystr_d).maxwidth) { |
|
849 WP(w,querystr_d).buf[count] = key; |
|
850 WP(w,querystr_d).buf[count + 1] = '\0'; |
|
851 InvalidateWidget(w, wid); |
|
852 } |
|
853 } else // key wasn't caught |
912 } else // key wasn't caught |
854 we->keypress.cont = true; |
913 we->keypress.cont = true; |
855 } |
914 } |
856 |
915 |
857 return 0; |
916 return 0; |
858 } |
917 } |
859 |
918 |
|
919 bool HandleCaret(Textbuf *tb) |
|
920 { |
|
921 /* caret changed? */ |
|
922 bool b = !!(_caret_timer & 0x20); |
|
923 |
|
924 if (b != tb->caret) { |
|
925 tb->caret = b; |
|
926 return true; |
|
927 } |
|
928 return false; |
|
929 } |
|
930 |
860 void HandleEditBox(Window *w, int wid) |
931 void HandleEditBox(Window *w, int wid) |
861 { |
932 { |
862 bool b; |
933 if (HandleCaret(&WP(w, querystr_d).text)) |
863 |
|
864 /* caret changed? */ |
|
865 b = !!(_caret_timer & 0x20); |
|
866 if (b != WP(w,querystr_d).caret) { |
|
867 WP(w,querystr_d).caret = b; |
|
868 InvalidateWidget(w, wid); |
934 InvalidateWidget(w, wid); |
869 } |
|
870 } |
935 } |
871 |
936 |
872 void DrawEditBox(Window *w, int wid) |
937 void DrawEditBox(Window *w, int wid) |
873 { |
938 { |
874 const Widget *wi = w->widget + wid; |
939 const Widget *wi = w->widget + wid; |
875 int x; |
940 const Textbuf *tb = &WP(w,querystr_d).text; |
876 |
941 |
877 GfxFillRect(wi->left+1, wi->top+1, wi->right-1, wi->bottom-1, 215); |
942 GfxFillRect(wi->left+1, wi->top+1, wi->right-1, wi->bottom-1, 215); |
878 x = DoDrawString(WP(w,querystr_d).buf, wi->left+2, wi->top+1, 8); |
943 DoDrawString(tb->buf, wi->left+2, wi->top+1, 8); |
879 if (WP(w,querystr_d).caret) |
944 if (tb->caret) |
880 DoDrawString("_", x, wi->top+1, 12); |
945 DoDrawString("_", wi->left + 2 + tb->caretxoffs, wi->top + 1, 12); |
881 } |
946 } |
882 |
|
883 |
947 |
884 static void QueryStringWndProc(Window *w, WindowEvent *e) |
948 static void QueryStringWndProc(Window *w, WindowEvent *e) |
885 { |
949 { |
886 static bool closed = false; |
950 static bool closed = false; |
887 switch(e->event) { |
951 switch(e->event) { |
888 case WE_PAINT: { |
952 case WE_PAINT: |
889 // int x; |
|
890 |
|
891 SetDParam(0, WP(w,querystr_d).caption); |
953 SetDParam(0, WP(w,querystr_d).caption); |
892 DrawWindowWidgets(w); |
954 DrawWindowWidgets(w); |
893 |
955 |
894 DrawEditBox(w, 5); |
956 DrawEditBox(w, 5); |
895 } break; |
957 break; |
896 |
958 |
897 case WE_CLICK: |
959 case WE_CLICK: |
898 switch(e->click.widget) { |
960 switch(e->click.widget) { |
899 case 3: DeleteWindow(w); break; |
961 case 3: DeleteWindow(w); break; |
900 case 4: |
962 case 4: |
901 press_ok:; |
963 press_ok:; |
902 if (WP(w, querystr_d).orig != NULL && |
964 if (WP(w, querystr_d).orig != NULL && |
903 strcmp(WP(w, querystr_d).buf, WP(w, querystr_d).orig) == 0) { |
965 strcmp(WP(w, querystr_d).text.buf, WP(w, querystr_d).orig) == 0) { |
904 DeleteWindow(w); |
966 DeleteWindow(w); |
905 } else { |
967 } else { |
906 char *buf = WP(w,querystr_d).buf; |
968 char *buf = WP(w,querystr_d).text.buf; |
907 WindowClass wnd_class = WP(w,querystr_d).wnd_class; |
969 WindowClass wnd_class = WP(w,querystr_d).wnd_class; |
908 WindowNumber wnd_num = WP(w,querystr_d).wnd_num; |
970 WindowNumber wnd_num = WP(w,querystr_d).wnd_num; |
909 Window *parent; |
971 Window *parent; |
910 |
972 |
911 // Mask the edit-box as closed, so we don't send out a CANCEL |
973 // Mask the edit-box as closed, so we don't send out a CANCEL |
984 static char _orig_str_buf[lengthof(_edit_str_buf)]; |
1048 static char _orig_str_buf[lengthof(_edit_str_buf)]; |
985 |
1049 |
986 void ShowQueryString(StringID str, StringID caption, uint maxlen, uint maxwidth, WindowClass window_class, WindowNumber window_number) |
1050 void ShowQueryString(StringID str, StringID caption, uint maxlen, uint maxwidth, WindowClass window_class, WindowNumber window_number) |
987 { |
1051 { |
988 Window *w; |
1052 Window *w; |
989 |
1053 uint realmaxlen = maxlen & ~0x1000; |
990 assert(maxlen < lengthof(_edit_str_buf)); |
1054 |
|
1055 assert(realmaxlen < lengthof(_edit_str_buf)); |
991 |
1056 |
992 DeleteWindowById(WC_QUERY_STRING, 0); |
1057 DeleteWindowById(WC_QUERY_STRING, 0); |
993 DeleteWindowById(WC_SAVELOAD, 0); |
1058 DeleteWindowById(WC_SAVELOAD, 0); |
994 |
1059 |
995 w = AllocateWindowDesc(&_query_string_desc); |
1060 w = AllocateWindowDesc(&_query_string_desc); |
996 |
1061 |
997 GetString(_edit_str_buf, str); |
1062 GetString(_edit_str_buf, str); |
998 _edit_str_buf[maxlen] = '\0'; |
1063 _edit_str_buf[realmaxlen] = '\0'; |
999 |
1064 |
1000 if (maxlen & 0x1000) { |
1065 if (maxlen & 0x1000) { |
1001 WP(w, querystr_d).orig = NULL; |
1066 WP(w, querystr_d).orig = NULL; |
1002 maxlen &= ~0x1000; |
|
1003 } else { |
1067 } else { |
1004 strcpy(_orig_str_buf, _edit_str_buf); |
1068 strcpy(_orig_str_buf, _edit_str_buf); |
1005 WP(w, querystr_d).orig = _orig_str_buf; |
1069 WP(w, querystr_d).orig = _orig_str_buf; |
1006 } |
1070 } |
1007 |
1071 |
1008 w->click_state = 1 << 5; |
1072 w->click_state = 1 << 5; |
1009 WP(w,querystr_d).caption = caption; |
1073 WP(w, querystr_d).caption = caption; |
1010 WP(w,querystr_d).wnd_class = window_class; |
1074 WP(w, querystr_d).wnd_class = window_class; |
1011 WP(w,querystr_d).wnd_num = window_number; |
1075 WP(w, querystr_d).wnd_num = window_number; |
1012 WP(w,querystr_d).caret = 0; |
1076 WP(w, querystr_d).text.caret = false; |
1013 WP(w,querystr_d).maxlen = maxlen; |
1077 WP(w, querystr_d).text.maxlength = realmaxlen - 1; |
1014 WP(w,querystr_d).maxwidth = maxwidth; |
1078 WP(w, querystr_d).text.maxwidth = maxwidth; |
1015 WP(w,querystr_d).buf = _edit_str_buf; |
1079 WP(w, querystr_d).text.buf = _edit_str_buf; |
|
1080 UpdateTextBufferSize(&WP(w, querystr_d).text); |
1016 |
1081 |
1017 _query_string_active = true; |
1082 _query_string_active = true; |
1018 } |
1083 } |
1019 |
1084 |
1020 static const Widget _load_dialog_1_widgets[] = { |
1085 static const Widget _load_dialog_1_widgets[] = { |
1337 w = AllocateWindowDesc(_saveload_dialogs[mode]); |
1403 w = AllocateWindowDesc(_saveload_dialogs[mode]); |
1338 w->vscroll.cap = 24; |
1404 w->vscroll.cap = 24; |
1339 w->resize.step_width = 2; |
1405 w->resize.step_width = 2; |
1340 w->resize.step_height = 10; |
1406 w->resize.step_height = 10; |
1341 w->resize.height = w->height - 14 * 10; // Minimum of 10 items |
1407 w->resize.height = w->height - 14 * 10; // Minimum of 10 items |
1342 w->click_state |= (1 << 6); |
1408 SETBIT(w->click_state, 6); |
1343 WP(w,querystr_d).caret = 0; |
1409 WP(w,querystr_d).text.caret = false; |
1344 WP(w,querystr_d).maxlen = lengthof(_edit_str_buf); |
1410 WP(w,querystr_d).text.maxlength = lengthof(_edit_str_buf) - 1; |
1345 WP(w,querystr_d).maxwidth = 240; |
1411 WP(w,querystr_d).text.maxwidth = 240; |
1346 WP(w,querystr_d).buf = _edit_str_buf; |
1412 WP(w,querystr_d).text.buf = _edit_str_buf; |
|
1413 UpdateTextBufferSize(&WP(w, querystr_d).text); |
1347 |
1414 |
1348 if (mode == SLD_SAVE_GAME) { |
1415 if (mode == SLD_SAVE_GAME) { |
1349 GenerateFileName(); |
1416 GenerateFileName(); |
1350 } else if (mode == SLD_SAVE_SCENARIO) { |
1417 } else if (mode == SLD_SAVE_SCENARIO) |
1351 strcpy(_edit_str_buf, "UNNAMED"); |
1418 strcpy(_edit_str_buf, "UNNAMED"); |
1352 } |
|
1353 |
1419 |
1354 // pause is only used in single-player, non-editor mode, non-menu mode. It |
1420 // pause is only used in single-player, non-editor mode, non-menu mode. It |
1355 // will be unpaused in the WE_DESTROY event handler. |
1421 // will be unpaused in the WE_DESTROY event handler. |
1356 if(_game_mode != GM_MENU && !_networking && _game_mode != GM_EDITOR) |
1422 if(_game_mode != GM_MENU && !_networking && _game_mode != GM_EDITOR) |
1357 DoCommandP(0, 1, 0, NULL, CMD_PAUSE); |
1423 DoCommandP(0, 1, 0, NULL, CMD_PAUSE); |