1/* 2 * Copyright 2011 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Copyright 1999 M.Kawamura 6 */ 7 8 9#include "CannaLooper.h" 10 11#include <string.h> 12 13#include <Debug.h> 14 15#include <Alert.h> 16#include <FindDirectory.h> 17#include <Input.h> 18#include <Menu.h> 19#include <MenuItem.h> 20#include <Messenger.h> 21#include <Path.h> 22#include <Screen.h> 23 24#include "CannaCommon.h" 25#include "CannaMethod.h" 26#include "KouhoWindow.h" 27#include "PaletteWindow.h" 28 29 30CannaLooper::CannaLooper(CannaMethod* method) 31 : BLooper("Canna Looper"), 32 fOwner(method), 33 fCanna(NULL), 34 fKouhoWindow(NULL), 35 fPaletteWindow(NULL) 36{ 37 fMenu = new BMenu(B_EMPTY_STRING); 38 fMenu->SetFont(be_plain_font); 39 fMenu->AddItem(new BMenuItem("About CannaIM" B_UTF8_ELLIPSIS, 40 new BMessage( B_ABOUT_REQUESTED))); 41 fMenu->AddSeparatorItem(); 42 fMenu->AddItem(new BMenuItem("Convert arrow keys", 43 new BMessage(ARROW_KEYS_FLIPPED))); 44 fMenu->AddItem(new BMenuItem("Reload Init file", 45 new BMessage(RELOAD_INIT_FILE))); 46 47 if (gSettings.convert_arrowkey) { 48 BMenuItem* item = fMenu->FindItem(ARROW_KEYS_FLIPPED); 49 item->SetMarked(true); 50 } 51 52 Run(); 53} 54 55 56status_t 57CannaLooper::Init() 58{ 59 char basePath[B_PATH_NAME_LENGTH + 1]; 60 status_t err = ReadSettings(basePath); 61 if (err != B_NO_ERROR) 62 return err; 63 64 fCanna = new CannaInterface(basePath); 65 66 return fCanna->InitCheck(); 67} 68 69 70void 71CannaLooper::Quit() 72{ 73 // delete palette here 74 SERIAL_PRINT(("CannaLooper: destructor called.\n")); 75 delete fCanna; 76 77 if (fKouhoWindow != NULL) { 78 SERIAL_PRINT(("CannaLooper: Sending QUIT to kouho window...\n")); 79 80 fKouhoWindow->Lock(); 81 fKouhoWindow->Quit(); 82 } 83 84 if (fPaletteWindow) { 85 SERIAL_PRINT(("CannaLooper: Sending QUIT to palette...\n")); 86 87 fPaletteWindow->Lock(); 88 fPaletteWindow->Quit(); 89 } 90 91 fOwner->SetMenu(NULL, BMessenger()); 92 delete fMenu; 93 BLooper::Quit(); 94} 95 96 97status_t 98CannaLooper::ReadSettings(char* basePath) 99{ 100 BPath path; 101 status_t status = find_directory(B_SYSTEM_DATA_DIRECTORY, &path); 102 if (status != B_OK) 103 return status; 104 105 path.Append("Canna"); 106 107 strlcpy(basePath, path.Path(), B_PATH_NAME_LENGTH); 108 strlcat(basePath, "/", B_PATH_NAME_LENGTH); 109 110 font_family family; 111 font_style style; 112 strcpy(family, "VL PGothic"); 113 strcpy(style, "regular"); 114 115 fKouhoFont.SetFamilyAndStyle(family, style); 116 fKouhoFont.SetSize(12); 117 118 return B_OK; 119} 120 121 122void 123CannaLooper::EnqueueMessage(BMessage* msg) 124{ 125 fOwner->EnqueueMessage(msg); 126} 127 128 129void 130CannaLooper::MessageReceived(BMessage* msg) 131{ 132 SERIAL_PRINT(("CannaLooper: Entering MessageReceived() what=%.4s\n", 133 (char*)&msg->what)); 134 135 switch (msg->what) { 136 case B_KEY_DOWN: 137 case NUM_SELECTED_FROM_KOUHO_WIN: 138 _HandleKeyDown(msg); 139 break; 140 141 case B_INPUT_METHOD_EVENT: 142 uint32 opcode, result; 143 msg->FindInt32("be:opcode", (int32*)&opcode); 144 145 switch (opcode) { 146 case B_INPUT_METHOD_LOCATION_REQUEST: 147 _HandleLocationReply(msg); 148 break; 149 150 case B_INPUT_METHOD_STOPPED: 151 SERIAL_PRINT(("CannaLooper: B_INPUT_METHOD_STOPPED " 152 "received\n")); 153 _ForceKakutei(); 154 break; 155 } 156 break; 157 158 case CANNA_METHOD_ACTIVATED: 159 SERIAL_PRINT(("CannaLooper: CANNA_METHOD_ACTIVATED received\n")); 160 _HandleMethodActivated(msg->HasBool("active")); 161 break; 162 163 case MODE_CHANGED_FROM_PALETTE: 164 _ForceKakutei(); 165 int32 mode; 166 msg->FindInt32("mode", &mode); 167 result = fCanna->ChangeMode(mode); 168 _ProcessResult(result); 169 break; 170 171 case B_ABOUT_REQUESTED: 172 { 173 SERIAL_PRINT(("CannaLooper: B_ABOUT_REQUESTED received\n")); 174 175 BAlert* panel = new BAlert( "", "Canna Input Method\n" 176 " by Masao Kawamura 1999\n\n" 177 "Canna\n" 178 " Copyright 1992 NEC Corporation, Tokyo, Japan\n" 179 " Special thanks to T.Murai for porting\n", 180 "OK"); 181 panel->SetFlags(panel->Flags() | B_CLOSE_ON_ESCAPE); 182 panel->Go(); 183 break; 184 } 185 186 case RELOAD_INIT_FILE: 187 _ForceKakutei(); 188 fCanna->Reset(); 189 break; 190 191 case ARROW_KEYS_FLIPPED: 192 { 193 BMenuItem* item = fMenu->FindItem(ARROW_KEYS_FLIPPED); 194 gSettings.convert_arrowkey = !gSettings.convert_arrowkey; 195 item->SetMarked(gSettings.convert_arrowkey); 196 fCanna->SetConvertArrowKey(gSettings.convert_arrowkey); 197 break; 198 } 199 200 default: 201 BLooper::MessageReceived(msg); 202 break; 203 } 204} 205 206 207void 208CannaLooper::_HandleKeyDown(BMessage* msg) 209{ 210 uint32 modifier; 211 int32 key; 212 msg->FindInt32("modifiers", (int32*)&modifier); 213 msg->FindInt32("key", &key); 214 215 if ((modifier & B_COMMAND_KEY) != 0) { 216 EnqueueMessage(DetachCurrentMessage()); 217 return; 218 } 219 220 char character; 221 msg->FindInt8("byte", (int8*)&character); 222 223 // The if clause below is to avoid processing key input which char code 224 // is 0x80 or more. 225 // if mikakutei string exists, dispose current message. 226 // Otherwise, send it to application as usual B_KEY_DOWN message. 227 if ((character & 0x80) != 0) { 228 if (fCanna->MikakuteiLength() != 0) 229 delete DetachCurrentMessage(); 230 else 231 EnqueueMessage(DetachCurrentMessage()); 232 233 return; 234 } 235 236 SERIAL_PRINT(("CannaLooper: HandleKeyDown() calling " 237 "CannaInterface::KeyIn()...\n")); 238 239 uint32 result = fCanna->KeyIn(character, modifier, key); 240 241 SERIAL_PRINT(("CannaLooper: HandleKeyDown() received result = %d from " 242 "CannaInterface.\n", result)); 243 244 _ProcessResult(result); 245} 246 247 248void 249CannaLooper::_ProcessResult(uint32 result) 250{ 251 SERIAL_PRINT(("CannaLooper: _ProcessResult() processing result = %d\n", 252 result)); 253 254 if ((result & GUIDELINE_APPEARED) != 0) { 255 SERIAL_PRINT(("CannaLooper: _ProcessResult() processing " 256 "GUIDELINE_APPEARED\n")); 257 258 if (fCanna->MikakuteiLength() != 0) { 259 // usual guideline i.e. kouho 260 261 BMessage* msg = new BMessage(B_INPUT_METHOD_EVENT); 262 msg->AddInt32("be:opcode", B_INPUT_METHOD_LOCATION_REQUEST); 263 fOwner->EnqueueMessage(msg); 264 265 SERIAL_PRINT(("CannaLooper: B_INPUT_METHOD_LOCATION_REQUEST has " 266 "been sent\n")); 267 } else { 268 // guideline exists, but no mikakutei string - means extend mode 269 // and such. 270 SERIAL_PRINT((" GUIDELINE_APPEARED: calling " 271 "GenerateKouho()...\n")); 272 273 fKouhoWindow->PostMessage(fCanna->GenerateKouhoString()); 274 SERIAL_PRINT((" GUIDELINE_APPEARED: posting KouhoMsg to " 275 "KouhoWindow %x...\n", fKouhoWindow)); 276 277 fKouhoWindow->PostMessage(KOUHO_WINDOW_SHOW_ALONE); 278 } 279 } 280 281 if ((result & GUIDELINE_DISAPPEARED) != 0) { 282 SERIAL_PRINT(("CannaLooper: _ProcessResult() processing " 283 "GUIDELINE_DISAPPEARED\n")); 284 fKouhoWindow->PostMessage(KOUHO_WINDOW_HIDE); 285 } 286 287 if ((result & MODE_CHANGED) != 0) { 288 SERIAL_PRINT(("CannaLooper: _ProcessResult() processing " 289 "MODE_CHANGED\n")); 290 291 BMessage message(PALETTE_WINDOW_BUTTON_UPDATE); 292 message.AddInt32("mode", fCanna->GetMode()); 293 fPaletteWindow->PostMessage(&message); 294 295 SERIAL_PRINT(("CannaLooper: PALETTE_BUTTON_UPDATE has been sent. " 296 "mode = %d\n", fCanna->GetMode())); 297 } 298 299 if ((result & GUIDELINE_CHANGED) != 0) { 300 SERIAL_PRINT(("CannaLooper: _ProcessResult() processing " 301 "GUIDELINE_CHANGED\n")); 302 fKouhoWindow->PostMessage(fCanna->GenerateKouhoString()); 303 } 304 305 if ((result & THROUGH_INPUT) != 0) { 306 SERIAL_PRINT(("CannaLooper: _ProcessResult() processing " 307 "THROUGH_INPUT\n")); 308 EnqueueMessage(DetachCurrentMessage()); 309 } 310 311 if ((result & NEW_INPUT_STARTED) != 0) { 312 SERIAL_PRINT(("CannaLooper: _ProcessResult() processing " 313 "NEW_INPUT_STARTED\n")); 314 SendInputStarted(); 315 } 316 317 if ((result & KAKUTEI_EXISTS) != 0) { 318 SERIAL_PRINT(("CannaLooper: _ProcessResult() processing " 319 "KAKUTEI_EXISTS\n")); 320 321 BMessage* msg = new BMessage(B_INPUT_METHOD_EVENT); 322 msg->AddInt32("be:opcode", B_INPUT_METHOD_CHANGED); 323 324 msg->AddString("be:string", fCanna->GetKakuteiStr()); 325 msg->AddInt32("be:clause_start", 0); 326 msg->AddInt32("be:clause_end", fCanna->KakuteiLength()); 327 msg->AddInt32("be:selection", fCanna->KakuteiLength()); 328 msg->AddInt32("be:selection", fCanna->KakuteiLength()); 329 msg->AddBool("be:confirmed", true); 330 fOwner->EnqueueMessage(msg); 331 332 SERIAL_PRINT(("CannaLooper: B_INPUT_METHOD_CHANGED (confired) has " 333 "been sent\n")); 334 335 // if both kakutei and mikakutei exist, do not send B_INPUT_STOPPED 336 if (!(result & MIKAKUTEI_EXISTS)) 337 SendInputStopped(); 338 } 339 340 if ((result & MIKAKUTEI_EXISTS) != 0) { 341 SERIAL_PRINT(("CannaLooper: _ProcessResult() processing " 342 "MIKAKUTEI_EXISTS\n" )); 343 344 int32 start, finish; 345 BMessage* msg = new BMessage(B_INPUT_METHOD_EVENT); 346 msg->AddInt32("be:opcode", B_INPUT_METHOD_CHANGED); 347 msg->AddString("be:string", fCanna->GetMikakuteiStr()); 348 349 if (fCanna->HasRev()) { 350 fCanna->GetRevPosition( &start, &finish); 351 msg->AddInt32("be:clause_start", 0); 352 msg->AddInt32("be:clause_end", start); 353 msg->AddInt32("be:clause_start", start); 354 msg->AddInt32("be:clause_end", finish); 355 msg->AddInt32("be:clause_start", finish); 356 msg->AddInt32("be:clause_end", fCanna->MikakuteiLength()); 357 } else { 358 start = finish = fCanna->MikakuteiLength(); 359 msg->AddInt32("be:clause_start", 0); 360 msg->AddInt32("be:clause_end", fCanna->MikakuteiLength()); 361 } 362 363 msg->AddInt32("be:selection", start); 364 msg->AddInt32("be:selection", finish); 365 //msg->AddBool("be:confirmed", false); 366 fOwner->EnqueueMessage(msg); 367 368 SERIAL_PRINT(("CannaLooper: B_INPUT_METHOD_CHANGED (non-confirmed) " 369 "has been sent\n")); 370 } 371 372 if ((result & MIKAKUTEI_BECOME_EMPTY) != 0) { 373 SERIAL_PRINT(("CannaLooper: _ProcessResult() processing " 374 "MIKAKUTEI_BECOME_EMPTY\n" )); 375 BMessage* msg = new BMessage(B_INPUT_METHOD_EVENT); 376 msg->AddInt32("be:opcode", B_INPUT_METHOD_CHANGED); 377 378 msg->AddString("be:string", B_EMPTY_STRING); 379 msg->AddInt32("be:clause_start", 0); 380 msg->AddInt32("be:clause_end", 0); 381 msg->AddInt32("be:selection", 0); 382 msg->AddInt32("be:selection", 0); 383 msg->AddBool( "be:confirmed", true); 384 fOwner->EnqueueMessage(msg); 385 386 SERIAL_PRINT(( "CannaLooper: B_INPUT_METHOD_CHANGED (NULL, confired) " 387 "has been sent\n")); 388 389 SendInputStopped(); 390 } 391} 392 393 394void 395CannaLooper::_HandleLocationReply(BMessage* msg) 396{ 397 BPoint where = B_ORIGIN; 398 float height = 0.0; 399 int32 start; 400 401 SERIAL_PRINT(("CannaLooper: B_INPUT_METHOD_LOCATION_REQUEST received\n")); 402 403 start = fCanna->GetRevStartPositionInChar(); 404 405#ifdef DEBUG 406 type_code type; 407 int32 count; 408 msg->GetInfo("be:location_reply", &type, &count); 409 SERIAL_PRINT(("CannaLooper: LOCATION_REPLY has %d locations.\n", count)); 410 SERIAL_PRINT(("CannaLooper: RevStartPosition is %d.\n", start)); 411#endif 412 413 msg->FindPoint("be:location_reply", start, &where); 414 msg->FindFloat("be:height_reply", start, &height); 415 BMessage m(KOUHO_WINDOW_SHOWAT); 416 m.AddPoint("position", where); 417 m.AddFloat("height", height); 418 419 fKouhoWindow->PostMessage(fCanna->GenerateKouhoString()); 420 fKouhoWindow->PostMessage(&m); 421} 422 423 424void 425CannaLooper::_HandleMethodActivated(bool active) 426{ 427 if (active) { 428 if (!fPaletteWindow) { 429 // first time input method activated 430 float x = gSettings.palette_loc.x; 431 float y = gSettings.palette_loc.y; 432 BRect frame(x, y, x + 114, y + 44); 433 fPaletteWindow = new PaletteWindow(frame, this); 434 fPaletteWindow->Show(); 435 fKouhoWindow = new KouhoWindow(&fKouhoFont, this); 436 fKouhoWindow->Run(); 437 } 438 439 fPaletteWindow->PostMessage(PALETTE_WINDOW_SHOW); 440 fOwner->SetMenu(fMenu, this); 441 } else { 442 _ForceKakutei(); 443 fCanna->ChangeMode(CANNA_MODE_HenkanMode); 444 BMessage m(PALETTE_WINDOW_BUTTON_UPDATE); 445 m.AddInt32("mode", CANNA_MODE_HenkanMode); 446 fPaletteWindow->PostMessage(&m); 447 fPaletteWindow->PostMessage(PALETTE_WINDOW_HIDE); 448 fKouhoWindow->PostMessage(KOUHO_WINDOW_HIDE); 449 fOwner->SetMenu(NULL, this); 450 } 451} 452 453 454void 455CannaLooper::_ForceKakutei() 456{ 457 SERIAL_PRINT(( "CannaLooper: _ForceKakutei() called\n" )); 458 459 uint32 result = fCanna->Kakutei(); 460 461 SERIAL_PRINT(("CannaLooper: returned from Kakutei(). result = %d\n", 462 result)); 463 464 if (result != NOTHING_TO_KAKUTEI) 465 _ProcessResult(result); 466 else 467 SendInputStopped(); 468} 469 470 471void 472CannaLooper::SendInputStopped() 473{ 474 BMessage* msg = new BMessage(B_INPUT_METHOD_EVENT); 475 msg->AddInt32("be:opcode", B_INPUT_METHOD_STOPPED); 476 EnqueueMessage(msg); 477 478 SERIAL_PRINT(("CannaLooper: B_INPUT_METHOD_STOPPED has been sent\n")); 479} 480 481 482void 483CannaLooper::SendInputStarted() 484{ 485 BMessage* msg = new BMessage(B_INPUT_METHOD_EVENT); 486 msg->AddInt32("be:opcode", B_INPUT_METHOD_STARTED); 487 msg->AddMessenger("be:reply_to", BMessenger(NULL, this)); 488 EnqueueMessage(msg); 489 490 SERIAL_PRINT(("CannaLooper: B_INPUT_METHOD_STARTED has been sent\n")); 491} 492 493