OSDN Git Service

ckw-0.8.10 original
[ckw/ckw.git] / selection.cpp
1 /*-----------------------------------------------------------------------------
2  * File: selection.cpp
3  *-----------------------------------------------------------------------------
4  * Copyright (c) 2004-2005  Kazuo Ishii <k-ishii@wb4.so-net.ne.jp>
5  *                              - original version
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  *---------------------------------------------------------------------------*/
21 #include "ckw.h"
22
23 static int      gSelectMode = 0;
24 static COORD    gSelectPos = { -1, -1 }; // pick point
25 static SMALL_RECT gSelectRect = { -1, -1, -1, -1 }; // expanded selection area
26
27 static const wchar_t WORD_BREAK_CHARS[] = {
28         L' ',  L'\t', L'\"', L'&', L'\'', L'(', L')', L'*',
29         L',',  L';',  L'<',  L'=', L'>',  L'?', L'@', L'[',
30         L'\\', L']',  L'^',  L'`', L'{',  L'}', L'~',
31         0x3000,
32         0x3001,
33         0x3002,
34         /**/
35         0,
36 };
37
38 /*****************************************************************************/
39
40 #define SCRN_InvalidArea(x,y) \
41         (y < gCSI->srWindow.Top    ||   \
42          y > gCSI->srWindow.Bottom ||   \
43          x < gCSI->srWindow.Left   ||   \
44          x > gCSI->srWindow.Right)
45
46 #define SELECT_GetScrn(x,y) \
47         (gScreen + CSI_WndCols(gCSI) * (y - gCSI->srWindow.Top) + x)
48
49 static void __select_word_expand_left()
50 {
51         if(SCRN_InvalidArea(gSelectRect.Left, gSelectRect.Top))
52                 return;
53         CHAR_INFO* base  = SELECT_GetScrn(gSelectRect.Left, gSelectRect.Top);
54         CHAR_INFO* ptr = base;
55         int c = gSelectRect.Left;
56
57         for( ; c >= gCSI->srWindow.Left ; c--, ptr--) {
58                 if(wcschr(WORD_BREAK_CHARS, ptr->Char.UnicodeChar)) {
59                         c++;
60                         break;
61                 }
62         }
63         if(c < 0)
64                 c = 0;
65
66         if(gSelectRect.Left > c)
67                 gSelectRect.Left = c;
68 }
69
70 static void __select_word_expand_right()
71 {
72         if(SCRN_InvalidArea(gSelectRect.Right, gSelectRect.Bottom))
73                 return;
74         CHAR_INFO* base  = SELECT_GetScrn(gSelectRect.Right, gSelectRect.Bottom);
75         CHAR_INFO* ptr = base;
76         int c = gSelectRect.Right;
77
78         for( ; c <= gCSI->srWindow.Right ; c++, ptr++) {
79                 if(wcschr(WORD_BREAK_CHARS, ptr->Char.UnicodeChar)) {
80                         break;
81                 }
82         }
83
84         if(gSelectRect.Right < c)
85                 gSelectRect.Right = c;
86 }
87
88 static void __select_char_expand()
89 {
90         CHAR_INFO* base;
91
92         if(SCRN_InvalidArea(gSelectRect.Left, gSelectRect.Top)) {
93         }
94         else if(gSelectRect.Left-1 >= gCSI->srWindow.Left) {
95                 base  = SELECT_GetScrn(gSelectRect.Left, gSelectRect.Top);
96                 if(base->Attributes & COMMON_LVB_TRAILING_BYTE)
97                         gSelectRect.Left--;
98         }
99
100         if(SCRN_InvalidArea(gSelectRect.Right, gSelectRect.Bottom)) {
101         }
102         else {
103                 base  = SELECT_GetScrn(gSelectRect.Right, gSelectRect.Bottom);
104                 if(base->Attributes & COMMON_LVB_TRAILING_BYTE)
105                         gSelectRect.Right++;
106         }
107 }
108
109 inline void __select_expand()
110 {
111         if(gSelectMode == 0) {
112                 __select_char_expand();
113         }
114         else if(gSelectMode == 1) {
115                 __select_word_expand_left();
116                 __select_word_expand_right();
117         }
118         else if(gSelectMode == 2) {
119                 gSelectRect.Left = gCSI->srWindow.Left;
120                 gSelectRect.Right = gCSI->srWindow.Right+1;
121         }
122 }
123
124 static void copy_char(wchar_t*& p, CHAR_INFO* src, SHORT start, SHORT end, bool ret = true)
125 {
126         CHAR_INFO* pend = src + end;
127         CHAR_INFO* test = src + start;
128         CHAR_INFO* last = test-1;
129
130         /* search last char */
131         for( ; test <= pend ; test++) {
132                 if(test->Char.UnicodeChar > 0x20)
133                         last = test;
134         }
135         /* copy */
136         for(test = src+start ; test <= last ; test++) {
137                 if(!(test->Attributes & COMMON_LVB_TRAILING_BYTE))
138                         *p++ = test->Char.UnicodeChar;
139         }
140         if(ret && last < pend) {
141                 *p++ = L'\r';
142                 *p++ = L'\n';
143         }
144         *p = 0;
145 }
146
147 static void window_to_charpos(int& x, int& y)
148 {
149         x -= gBorderSize;
150         y -= gBorderSize;
151         if(x < 0) x = 0;
152         if(y < 0) y = 0;
153         x /= gFontW;
154         y /= gFontH;
155         x += gCSI->srWindow.Left;
156         y += gCSI->srWindow.Top;
157         if(x > gCSI->srWindow.Right)  x = gCSI->srWindow.Right+1;
158         if(y > gCSI->srWindow.Bottom) y = gCSI->srWindow.Bottom;
159 }
160
161 /*****************************************************************************/
162
163 #define SELECT_Invalid \
164         (gSelectRect.Top > gSelectRect.Bottom ||   \
165          (gSelectRect.Top == gSelectRect.Bottom && \
166           gSelectRect.Left >= gSelectRect.Right))  \
167
168 /*----------*/
169 BOOL    selectionGetArea(SMALL_RECT& sr)
170 {
171         if(SELECT_Invalid)
172                 return(FALSE);
173         sr = gSelectRect;
174         return(TRUE);
175 }
176
177 /*----------*/
178 void    selectionClear(HWND hWnd)
179 {
180         if(SELECT_Invalid)
181                 return;
182         gSelectRect.Left = gSelectRect.Right = \
183         gSelectRect.Top = gSelectRect.Bottom = 0;
184         InvalidateRect(hWnd, NULL, FALSE);
185 }
186
187 /*----------*/
188 wchar_t* selectionGetString()
189 {
190         if(SELECT_Invalid)
191                 return(NULL);
192
193         int nb, y;
194
195         if(gSelectRect.Top == gSelectRect.Bottom) {
196                 nb = gSelectRect.Right - gSelectRect.Left;
197         }
198         else {
199                 nb = gCSI->srWindow.Right - gSelectRect.Left+1;
200                 for(y = gSelectRect.Top+1 ; y <= gSelectRect.Bottom-1 ; y++)
201                         nb += CSI_WndCols(gCSI);
202                 nb += gSelectRect.Right - gCSI->srWindow.Left;
203         }
204
205         COORD      size = { CSI_WndCols(gCSI), 1 };
206         CHAR_INFO* work = new CHAR_INFO[ size.X ];
207         wchar_t*   buffer = new wchar_t[ nb +32 ];
208         wchar_t*   wp = buffer;
209         COORD      pos = { 0,0 };
210         SMALL_RECT sr = { gCSI->srWindow.Left, 0, gCSI->srWindow.Right, 0 };
211
212         *wp = 0;
213
214         if(gSelectRect.Top == gSelectRect.Bottom) {
215                 sr.Top = sr.Bottom = gSelectRect.Top;
216                 ReadConsoleOutput_Unicode(gStdOut, work, size, pos, &sr);
217                 copy_char(wp, work, gSelectRect.Left, gSelectRect.Right-1, false);
218         }
219         else {
220                 sr.Top = sr.Bottom = gSelectRect.Top;
221                 ReadConsoleOutput_Unicode(gStdOut, work, size, pos, &sr);
222                 copy_char(wp, work, gSelectRect.Left, gCSI->srWindow.Right);
223                 for(y = gSelectRect.Top+1 ; y <= gSelectRect.Bottom-1 ; y++) {
224                         sr.Top = sr.Bottom = y;
225                         ReadConsoleOutput_Unicode(gStdOut, work, size, pos, &sr);
226                         copy_char(wp, work, gCSI->srWindow.Left, gCSI->srWindow.Right);
227                 }
228                 sr.Top = sr.Bottom = gSelectRect.Bottom;
229                 ReadConsoleOutput_Unicode(gStdOut, work, size, pos, &sr);
230                 copy_char(wp, work, gCSI->srWindow.Left, gSelectRect.Right-1, false);
231         }
232
233         delete [] work;
234         return(buffer);
235 }
236
237 /*----------*/
238 void    onLBtnDown(HWND hWnd, int x, int y)
239 {
240         static DWORD    prev_time = 0;
241         static int      prevX = -100;
242         static int      prevY = -100;
243
244         {
245                 /* calc click count */
246                 DWORD now_time = GetTickCount();
247                 DWORD stime;
248                 if(prev_time > now_time)
249                         stime = now_time + ~prev_time+1;
250                 else
251                         stime = now_time - prev_time;
252                 if(stime <= GetDoubleClickTime()) {
253                         int sx = (prevX > x) ? prevX-x : x-prevX;
254                         int sy = (prevY > y) ? prevY-y : y-prevY;
255                         if(sx <= GetSystemMetrics(SM_CXDOUBLECLK) &&
256                            sy <= GetSystemMetrics(SM_CYDOUBLECLK)) {
257                                 if(++gSelectMode > 2)
258                                         gSelectMode = 0;
259                         }
260                         else {
261                                 gSelectMode = 0;
262                         }
263                 }
264                 else {
265                         gSelectMode = 0;
266                 }
267                 prev_time = now_time;
268                 prevX = x;
269                 prevY = y;
270         }
271
272         if(!gScreen || !gCSI)
273                 return;
274         window_to_charpos(x, y);
275         SetCapture(hWnd);
276
277         gSelectPos.X = x;
278         gSelectPos.Y = y;
279         gSelectRect.Left = gSelectRect.Right = x;
280         gSelectRect.Top  = gSelectRect.Bottom = y;
281
282         __select_expand();
283         InvalidateRect(hWnd, NULL, FALSE);
284 }
285
286 /*----------*/
287 void    onLBtnUp(HWND hWnd, int x, int y)
288 {
289         if(hWnd != GetCapture())
290                 return;
291         ReleaseCapture();
292         if(!gScreen || !gCSI)
293                 return;
294         //window_to_charpos(x, y);
295
296         wchar_t* str = selectionGetString();
297         if(!str) return;
298
299         size_t length = wcslen(str) +1;
300         HANDLE hMem;
301         wchar_t* ptr;
302         bool    result = true;
303
304         hMem = GlobalAlloc(GMEM_MOVEABLE, sizeof(wchar_t) * length);
305         if(!hMem) result = false;
306
307         if(result && !(ptr = (wchar_t*) GlobalLock(hMem))) {
308                 result = false;
309         }
310         if(result) {
311                 memcpy(ptr, str, sizeof(wchar_t) * length);
312                 GlobalUnlock(hMem);
313         }
314         if(result && !OpenClipboard(hWnd)) {
315                 Sleep(10);
316                 if(!OpenClipboard(hWnd))
317                         result = false;
318         }
319         if(result) {
320                 if(!EmptyClipboard() ||
321                    !SetClipboardData(CF_UNICODETEXT, hMem))
322                         result = false;
323                 CloseClipboard();
324         }
325         if(!result && hMem) {
326                 GlobalFree(hMem);
327         }
328         delete [] str;
329 }
330
331 /*----------*/
332 void    onMouseMove(HWND hWnd, int x, int y)
333 {
334         if(hWnd != GetCapture())
335                 return;
336         if(!gScreen || !gCSI)
337                 return;
338         window_to_charpos(x, y);
339
340         SMALL_RECT bak = gSelectRect;
341
342         if(y < gSelectPos.Y || (y == gSelectPos.Y && x < gSelectPos.X)) {
343                 gSelectRect.Left   = x;
344                 gSelectRect.Top    = y;
345                 gSelectRect.Right  = gSelectPos.X;
346                 gSelectRect.Bottom = gSelectPos.Y;
347         }
348         else {
349                 gSelectRect.Left   = gSelectPos.X;
350                 gSelectRect.Top    = gSelectPos.Y;
351                 gSelectRect.Right  = x;
352                 gSelectRect.Bottom = y;
353         }
354         __select_expand();
355
356         if(memcmp(&bak, &gSelectRect, sizeof(bak))) {
357                 InvalidateRect(hWnd, NULL, FALSE);
358         }
359 }
360
361 /* EOF */