//
//  AfficheurPanel.m
//  Afficheur
//
//  Created by kichi on 08/11/23.
//  Copyright 2008 Katsuhiko Ichinose. All rights reserved.
//

#import "AfficheurPanel.h"
#import "TextViewHUD.h"
#import "TextFieldHUD.h"
#import "AfficheurPreferences.h"
#import "AfficheurController.h"
#import "Service.h"
#import "ImageView.h"
#import "EmojiView.h"
#import "JaikuIconView.h"
#import "i18n.h"


@implementation AfficheurPanel

#define JAIKU_ICON_MARGIN	2

static NSString *docomo		= @"docomo";
static NSString *au			= @"au";
static NSString *softbank	= @"SoftBank";

static NSString *View		= @"View";
static NSString *Tooltip	= @"Tooltip";

static const unichar EZweb[] =
{
0xEC40,
0xEC41,
0xEC42,
0xEC43,
0xEC44,
0xEC45,
0xEC46,
0xEC47,
0xEC48,
0xEC49,
0xEC4A,
0xEC4B,
0xEC4C,
0xEC4D,
0xEC4E,
0xEC4F,
0xEC50,
0xEC51,
0xEC52,
0xEC53,
0xEC54,
0xEC55,
0xEC56,
0xEC57,
0xEC58,
0xEC59,
0xEC5A,
0xEC5B,
0xEC5C,
0xEC5D,
0xEC5E,
0xEC5F,
0xEC60,
0xEC61,
0xEC62,
0xEC63,
0xEC64,
0xEC65,
0xEC66,
0xEC67,
0xEC68,
0xEC69,
0xEC6A,
0xEC6B,
0xEC6C,
0xEC6D,
0xEC6E,
0xEC6F,
0xEC70,
0xEC71,
0xEC72,
0xEC73,
0xEC74,
0xEC75,
0xEC76,
0xEC77,
0xEC78,
0xEC79,
0xEC7A,
0xEC7B,
0xEC7C,
0xEC7D,
0xEC7E,
0xEC80,
0xEC81,
0xEC82,
0xEC83,
0xEC84,
0xEC85,
0xEC86,
0xEC87,
0xEC88,
0xEC89,
0xEC8A,
0xEC8B,
0xEC8C,
0xEC8D,
0xEC8E,
0xEC8F,
0xEC90,
0xEC91,
0xEC92,
0xEC93,
0xEC94,
0xEC95,
0xEC96,
0xEC97,
0xEC98,
0xEC99,
0xEC9A,
0xEC9B,
0xEC9C,
0xEC9D,
0xEC9E,
0xEC9F,
0xECA0,
0xECA1,
0xECA2,
0xECA3,
0xECA4,
0xECA5,
0xECA6,
0xECA7,
0xECA8,
0xECA9,
0xECAA,
0xECAB,
0xECAC,
0xECAD,
0xECAE,
0xECAF,
0xECB0,
0xECB1,
0xECB2,
0xECB3,
0xECB4,
0xECB5,
0xECB6,
0xECB7,
0xECB8,
0xECB9,
0xECBA,
0xECBB,
0xECBC,
0xECBD,
0xECBE,
0xECBF,
0xECC0,
0xECC1,
0xECC2,
0xECC3,
0xECC4,
0xECC5,
0xECC6,
0xECC7,
0xECC8,
0xECC9,
0xECCA,
0xECCB,
0xECCC,
0xECCD,
0xECCE,
0xECCF,
0xECD0,
0xECD1,
0xECD2,
0xECD3,
0xECD4,
0xECD5,
0xECD6,
0xECD7,
0xECD8,
0xECD9,
0xECDA,
0xECDB,
0xECDC,
0xECDD,
0xECDE,
0xECDF,
0xECE0,
0xECE1,
0xECE2,
0xECE3,
0xECE4,
0xECE5,
0xECE6,
0xECE7,
0xECE8,
0xECE9,
0xECEA,
0xECEB,
0xECEC,
0xECED,
0xECEE,
0xECEF,
0xECF0,
0xECF1,
0xECF2,
0xECF3,
0xECF4,
0xECF5,
0xECF6,
0xECF7,
0xECF8,
0xECF9,
0xECFA,
0xECFB,
0xECFC,
0xED40,
0xED41,
0xED42,
0xED43,
0xED44,
0xED45,
0xED46,
0xED47,
0xED48,
0xED49,
0xED4A,
0xED4B,
0xED4C,
0xED4D,
0xED4E,
0xED4F,
0xED50,
0xED51,
0xED52,
0xED53,
0xED54,
0xED55,
0xED56,
0xED57,
0xED58,
0xED59,
0xED5A,
0xED5B,
0xED5C,
0xED5D,
0xED5E,
0xED5F,
0xED60,
0xED61,
0xED62,
0xED63,
0xED64,
0xED65,
0xED66,
0xED67,
0xED68,
0xED69,
0xED6A,
0xED6B,
0xED6C,
0xED6D,
0xED6E,
0xED6F,
0xED70,
0xED71,
0xED72,
0xED73,
0xED74,
0xED75,
0xED76,
0xED77,
0xED78,
0xED79,
0xED7A,
0xED7B,
0xED7C,
0xED7D,
0xED7E,
0xED80,
0xED81,
0xED82,
0xED83,
0xED84,
0xED85,
0xED86,
0xED87,
0xED88,
0xED89,
0xED8A,
0xED8B,
0xED8C,
0xED8D,
0xED8E,
0xED8F,
0xED90,
0xED91,
0xED92,
0xED93,
0xEF40,
0xEF41,
0xEF42,
0xEF43,
0xEF44,
0xEF45,
0xEF46,
0xEF47,
0xEF48,
0xEF49,
0xEF4A,
0xEF4B,
0xEF4C,
0xEF4D,
0xEF4E,
0xEF4F,
0xEF50,
0xEF51,
0xEF52,
0xEF53,
0xEF54,
0xEF55,
0xEF56,
0xEF57,
0xEF58,
0xEF59,
0xEF5A,
0xEF5B,
0xEF5C,
0xEF5D,
0xEF5E,
0xEF5F,
0xEF60,
0xEF61,
0xEF62,
0xEF63,
0xEF64,
0xEF65,
0xEF66,
0xEF67,
0xEF68,
0xEF69,
0xEF6A,
0xEF6B,
0xEF6C,
0xEF6D,
0xEF6E,
0xEF6F,
0xEF70,
0xEF71,
0xEF72,
0xEF73,
0xEF74,
0xEF75,
0xEF76,
0xEF77,
0xEF78,
0xEF79,
0xEF7A,
0xEF7B,
0xEF7C,
0xEF7D,
0xEF7E,
0xEF80,
0xEF81,
0xEF82,
0xEF83,
0xEF84,
0xEF85,
0xEF86,
0xEF87,
0xEF88,
0xEF89,
0xEF8A,
0xEF8B,
0xEF8C,
0xEF8D,
0xEF8E,
0xEF8F,
0xEF90,
0xEF91,
0xEF92,
0xEF93,
0xEF94,
0xEF95,
0xEF96,
0xEF97,
0xEF98,
0xEF99,
0xEF9A,
0xEF9B,
0xEF9C,
0xEF9D,
0xEF9E,
0xEF9F,
0xEFA0,
0xEFA1,
0xEFA2,
0xEFA3,
0xEFA4,
0xEFA5,
0xEFA6,
0xEFA7,
0xEFA8,
0xEFA9,
0xEFAA,
0xEFAB,
0xEFAC,
0xEFAD,
0xEFAE,
0xEFAF,
0xEFB0,
0xEFB1,
0xEFB2,
0xEFB3,
0xEFB4,
0xEFB5,
0xEFB6,
0xEFB7,
0xEFB8,
0xEFB9,
0xEFBA,
0xEFBB,
0xEFBC,
0xEFBD,
0xEFBE,
0xEFBF,
0xEFC0,
0xEFC1,
0xEFC2,
0xEFC3,
0xEFC4,
0xEFC5,
0xEFC6,
0xEFC7,
0xEFC8,
0xEFC9,
0xEFCA,
0xEFCB,
0xEFCC,
0xEFCD,
0xEFCE,
0xEFCF,
0xEFD0,
0xEFD1,
0xEFD2,
0xEFD3,
0xEFD4,
0xEFD5,
0xEFD6,
0xEFD7,
0xEFD8,
0xEFD9,
0xEFDA,
0xEFDB,
0xEFDC,
0xEFDD,
0xEFDE,
0xEFDF,
0xEFE0,
0xEFE1,
0xEFE2,
0xEFE3,
0xEFE4,
0xEFE5,
0xEFE6,
0xEFE7,
0xEFE8,
0xEFE9,
0xEFEA,
0xEFEB,
0xEFEC,
0xEFED,
0xEFEE,
0xEFEF,
0xEFF0,
0xEFF1,
0xEFF2,
0xEFF3,
0xEFF4,
0xEFF5,
0xEFF6,
0xEFF7,
0xEFF8,
0xEFF9,
0xEFFA,
0xEFFB,
0xEFFC,
0xF040,
0xF041,
0xF042,
0xF043,
0xF044,
0xF045,
0xF046,
0xF047,
0xF048,
0xF049,
0xF04A,
0xF04B,
0xF04C,
0xF04D,
0xF04E,
0xF04F,
0xF050,
0xF051,
0xF052,
0xF053,
0xF054,
0xF055,
0xF056,
0xF057,
0xF058,
0xF059,
0xF05A,
0xF05B,
0xF05C,
0xF05D,
0xF05E,
0xF05F,
0xF060,
0xF061,
0xF062,
0xF063,
0xF064,
0xF065,
0xF066,
0xF067,
0xF068,
0xF069,
0xF06A,
0xF06B,
0xF06C,
0xF06D,
0xF06E,
0xF06F,
0xF070,
0xF071,
0xF072,
0xF073,
0xF074,
0xF075,
0xF076,
0xF077,
0xF078,
0xF079,
0xF07A,
0xF07B,
0xF07C,
0xF07D,
0xF07E,
0xF080,
0xF081,
0xF082,
0xF083,
0xF084,
0xF085,
0xF086,
0xF087,
0xF088,
0xF089,
0xF08A,
0xF08B,
0xF08C,
0xF08D,
0xF08E,
0xF08F,
0xF090,
0xF091,
0xF092,
0xF093,
0xF094,
0xF095,
0xF096,
0xF097,
0xF098,
0xF099,
0xF09A,
0xF09B,
0xF09C,
0xF09D,
0xF09E,
0xF09F,
0xF0A0,
0xF0A1,
0xF0A2,
0xF0A3,
0xF0A4,
0xF0A5,
0xF0A6,
0xF0A7,
0xF0A8,
//0xF0A9,
//0xF0AA,
//0xF0AB,
0xF0AC,
0xF0AD,
0xF0AE,
0xF0AF,
0xF0B0,
0xF0B1,
0xF0B2,
0xF0B3,
0xF0B4,
0xF0B5,
0xF0B6,
0xF0B7,
0xF0B8,
0xF0B9,
0xF0BA,
0xF0BB,
0xF0BC,
0xF0BD,
0xF0BE,
0xF0BF,
0xF0C0,
0xF0C1,
0xF0C2,
0xF0C3,
0xF0C4,
0xF0C5,
0xF0C6,
0xF0C7,
0xF0C8,
0xF0C9,
0xF0CA,
0xF0CB,
0xF0CC,
0xF0CD,
0xF0CE,
0xF0CF,
0xF0D0,
0xF0D1,
0xF0D2,
0xF0D3,
0xF0D4,
0xF0D5,
0xF0D6,
0xF0D7,
0xF0D8,
0xF0D9,
0xF0DA,
0xF0DB,
0xF0DC,
0xF0DD,
0xF0DE,
0xF0DF,
0xF0E0,
0xF0E1,
0xF0E2,
0xF0E3,
0xF0E4,
0xF0E5,
0xF0E6,
0xF0E7,
0xF0E8,
0xF0E9,
0xF0EA,
0xF0EB,
0xF0EC,
0xF0ED,
0xF0EE,
0xF0EF,
0xF0F0,
0xF0F1,
0xF0F2,
0xF0F3,
0xF0F4,
0xF0F5,
0xF0F6,
0xF0F7,
0xF0F8,
0xF0F9,
0xF0FA,
0xF0FB,
0xF0FC,
0
};

static const unichar SoftBank[] =
{
0xE001,
0xE002,
0xE003,
0xE004,
0xE005,
0xE006,
0xE007,
0xE008,
0xE009,
0xE00A,
0xE00B,
0xE00C,
0xE00D,
0xE00E,
0xE00F,
0xE010,
0xE011,
0xE012,
0xE013,
0xE014,
0xE015,
0xE016,
0xE017,
0xE018,
0xE019,
0xE01A,
0xE01B,
0xE01C,
0xE01D,
0xE01E,
0xE01F,
0xE020,
0xE021,
0xE022,
0xE023,
0xE024,
0xE025,
0xE026,
0xE027,
0xE028,
0xE029,
0xE02A,
0xE02B,
0xE02C,
0xE02D,
0xE02E,
0xE02F,
0xE030,
0xE031,
0xE032,
0xE033,
0xE034,
0xE035,
0xE036,
0xE037,
0xE038,
0xE039,
0xE03A,
0xE03B,
0xE03C,
0xE03D,
0xE03E,
0xE03F,
0xE040,
0xE041,
0xE042,
0xE043,
0xE044,
0xE045,
0xE046,
0xE047,
0xE048,
0xE049,
0xE04A,
0xE04B,
0xE04C,
0xE04D,
0xE04E,
0xE04F,
0xE050,
0xE051,
0xE052,
0xE053,
0xE054,
0xE055,
0xE056,
0xE057,
0xE058,
0xE059,
0xE05A,
0xE101,
0xE102,
0xE103,
0xE104,
0xE105,
0xE106,
0xE107,
0xE108,
0xE109,
0xE10A,
0xE10B,
0xE10C,
0xE10D,
0xE10E,
0xE10F,
0xE110,
0xE111,
0xE112,
0xE113,
0xE114,
0xE115,
0xE116,
0xE117,
0xE118,
0xE119,
0xE11A,
0xE11B,
0xE11C,
0xE11D,
0xE11E,
0xE11F,
0xE120,
0xE121,
0xE122,
0xE123,
0xE124,
0xE125,
0xE126,
0xE127,
0xE128,
0xE129,
0xE12A,
0xE12B,
0xE12C,
0xE12D,
0xE12E,
0xE12F,
0xE130,
0xE131,
0xE132,
0xE133,
0xE134,
0xE135,
0xE136,
0xE137,
0xE138,
0xE139,
0xE13A,
0xE13B,
0xE13C,
0xE13D,
0xE13E,
0xE13F,
0xE140,
0xE141,
0xE142,
0xE143,
0xE144,
0xE145,
0xE146,
0xE147,
0xE148,
0xE149,
0xE14A,
0xE14B,
0xE14C,
0xE14D,
0xE14E,
0xE14F,
0xE150,
0xE151,
0xE152,
0xE153,
0xE154,
0xE155,
0xE156,
0xE157,
0xE158,
0xE159,
0xE15A,
0xE201,
0xE202,
0xE203,
0xE204,
0xE205,
0xE206,
0xE207,
0xE208,
0xE209,
0xE20A,
0xE20B,
0xE20C,
0xE20D,
0xE20E,
0xE20F,
0xE210,
0xE211,
0xE212,
0xE213,
0xE214,
0xE215,
0xE216,
0xE217,
0xE218,
0xE219,
0xE21A,
0xE21B,
0xE21C,
0xE21D,
0xE21E,
0xE21F,
0xE220,
0xE221,
0xE222,
0xE223,
0xE224,
0xE225,
0xE226,
0xE227,
0xE228,
0xE229,
0xE22A,
0xE22B,
0xE22C,
0xE22D,
0xE22E,
0xE22F,
0xE230,
0xE231,
0xE232,
0xE233,
0xE234,
0xE235,
0xE236,
0xE237,
0xE238,
0xE239,
0xE23A,
0xE23B,
0xE23C,
0xE23D,
0xE23E,
0xE23F,
0xE240,
0xE241,
0xE242,
0xE243,
0xE244,
0xE245,
0xE246,
0xE247,
0xE248,
0xE249,
0xE24A,
0xE24B,
0xE24C,
0xE24D,
0xE24E,
0xE24F,
0xE250,
0xE251,
0xE252,
0xE253,
0xE254,
0xE255,
0xE256,
0xE257,
0xE258,
0xE259,
0xE25A,
0xE301,
0xE302,
0xE303,
0xE304,
0xE305,
0xE306,
0xE307,
0xE308,
0xE309,
0xE30A,
0xE30B,
0xE30C,
0xE30D,
0xE30E,
0xE30F,
0xE310,
0xE311,
0xE312,
0xE313,
0xE314,
0xE315,
0xE316,
0xE317,
0xE318,
0xE319,
0xE31A,
0xE31B,
0xE31C,
0xE31D,
0xE31E,
0xE31F,
0xE320,
0xE321,
0xE322,
0xE323,
0xE324,
0xE325,
0xE326,
0xE327,
0xE328,
0xE329,
0xE32A,
0xE32B,
0xE32C,
0xE32D,
0xE32E,
0xE32F,
0xE330,
0xE331,
0xE332,
0xE333,
0xE334,
0xE335,
0xE336,
0xE337,
0xE338,
0xE339,
0xE33A,
0xE33B,
0xE33C,
0xE33D,
0xE33E,
0xE33F,
0xE340,
0xE341,
0xE342,
0xE343,
0xE344,
0xE345,
0xE346,
0xE347,
0xE348,
0xE349,
0xE34A,
0xE34B,
0xE34C,
0xE34D,
0xE401,
0xE402,
0xE403,
0xE404,
0xE405,
0xE406,
0xE407,
0xE408,
0xE409,
0xE40A,
0xE40B,
0xE40C,
0xE40D,
0xE40E,
0xE40F,
0xE410,
0xE411,
0xE412,
0xE413,
0xE414,
0xE415,
0xE416,
0xE417,
0xE418,
0xE419,
0xE41A,
0xE41B,
0xE41C,
0xE41D,
0xE41E,
0xE41F,
0xE420,
0xE421,
0xE422,
0xE423,
0xE424,
0xE425,
0xE426,
0xE427,
0xE428,
0xE429,
0xE42A,
0xE42B,
0xE42C,
0xE42D,
0xE42E,
0xE42F,
0xE430,
0xE431,
0xE432,
0xE433,
0xE434,
0xE435,
0xE436,
0xE437,
0xE438,
0xE439,
0xE43A,
0xE43B,
0xE43C,
0xE43D,
0xE43E,
0xE43F,
0xE440,
0xE441,
0xE442,
0xE443,
0xE444,
0xE445,
0xE446,
0xE447,
0xE448,
0xE449,
0xE44A,
0xE44B,
0xE44C,
0xE501,
0xE502,
0xE503,
0xE504,
0xE505,
0xE506,
0xE507,
0xE508,
0xE509,
0xE50A,
0xE50B,
0xE50C,
0xE50D,
0xE50E,
0xE50F,
0xE510,
0xE511,
0xE512,
0xE513,
0xE514,
0xE515,
0xE516,
0xE517,
0xE518,
0xE519,
0xE51A,
0xE51B,
0xE51C,
0xE51D,
0xE51E,
0xE51F,
0xE520,
0xE521,
0xE522,
0xE523,
0xE524,
0xE525,
0xE526,
0xE527,
0xE528,
0xE529,
0xE52A,
0xE52B,
0xE52C,
0xE52D,
0xE52E,
0xE52F,
0xE530,
0xE531,
0xE532,
0xE533,
0xE534,
0xE535,
0xE536,
0xE537,
0xE538,
0xE539,
0xE53A,
0xE53B,
0xE53C,
0xE53D,
0xE53E,
0
};

static const int JaikuIcon[] =
{
344,
345,
346,
347,
341,
342,
343,
348,
349,
379,
378,
371,
370,
373,
372,
375,
374,
377,
376,
319,
318,
313,
312,
311,
310,
317,
316,
315,
314,
393,
392,
391,
390,
397,
396,
395,
394,
399,
398,
368,
369,
366,
367,
364,
365,
362,
363,
360,
361,
308,
309,
301,
302,
303,
304,
305,
306,
307,
380,
381,
382,
383,
384,
385,
386,
387,
388,
389,
339,
338,
335,
334,
337,
336,
331,
330,
333,
332,
402,
400,
322,
323,
320,
321,
326,
327,
324,
325,
328,
329,
357,
356,
355,
354,
353,
352,
350,
359,
358,
0
};

// NIB file
static NSString *NibFile			= @"Panel";

+ (const int *)tableOfJaikuIcon
{
	return JaikuIcon;
}

- (void)loadNib
{
	//LOG(@"[%@ loadNib]", [self className]);
	[NSBundle loadNibNamed:NibFile owner:self];
}

- (void)getMainThread:(id)object
{
	_mainThread = [NSThread currentThread];
}

- (id)initWithPreferences:(AfficheurPreferences *)preferences
{
	//LOG(@"[%@ init]", [self className]);
	self = [super init];
	if (self)
	{
		_height = 0;
		_preferences = preferences;
		_doCommand = 0;
		_textViewHUD = [[TextViewHUD alloc] initWithController:self];
		_undoOldText = nil;
		_undoOldRange = NSMakeRange(NSNotFound, 0);
		_undoOldOldRange = NSMakeRange(NSNotFound, 0);
		_emojiViews = nil;
		_inReplyTo = nil;
		_inReplyToService = @"";
		_inReplyToText = nil;
		_inReplyReply = nil;
		_lockClick = [[NSLock alloc] init];
		_saveState = NO;
		[self getMainThread:nil];
		_lockText = [[NSLock alloc] init];
		_lockedText = NO;
		_queueText = [[NSMutableArray alloc] init];
		_lockReply = [[NSLock alloc] init];
		_keyView = NO;
		_setUpDoCoMo = NO;
		_setUpEZweb = NO;
		_setUpSoftBank = NO;
		_animatingEmoji = NO;
		_completePosting = NO;
		_commandHandling = NO;
		_stringSetting = NO;
		_adjustPanel = [_preferences generalAdjustPanel];
		[self loadNib];
	}
	return self;
}

- (void)dealloc
{
	if (_undoOldText)
	{
		[_undoOldText release];
	}
	if (_inReplyTo)
	{
		[_inReplyTo release];
	}
	if (_inReplyToService)
	{
		[_inReplyToService release];
	}
	if (_inReplyToText)
	{
		[_inReplyToText release];
	}
	if (_inReplyReply)
	{
		[_inReplyReply release];
	}
	[_textViewHUD release];
	[_emojiViews release];
	[_lockClick release];
	[_lockText release];
	[_queueText release];
	[_lockReply release];
	[super dealloc];
}

- (void)performEnablePopupButoon
{
	[_spinner setHidden:YES];
	[_spinner stopAnimation:self];
	if ([_preferences generalUseEmoji])
	{
		[_buttonEmoji setHidden:NO];
		[_buttonEmoji setEnabled:YES];
	}
	else
	{
		[_buttonEmoji2 setHidden:NO];
		[_buttonEmoji2 setEnabled:YES];
	}
}

- (void)enablePopupButoon
{
	if (_setUpDoCoMo && _setUpEZweb && _setUpSoftBank && _setUpJaiku)
	{
		[self performSelectorOnMainThread:@selector(performEnablePopupButoon)
							   withObject:nil
							waitUntilDone:YES];
	}
}

- (void)addSubviewDoCoMo:(id)object
{
	@try
	{
		EmojiView *view = [object valueForKey:View];
		NSString *tooltip = [object valueForKey:Tooltip];
		if (view)
		{
			[view setMouseUpAction:@selector(inputEmoji:) withTarget:self];
			if (tooltip)
			{
				[view setToolTip:tooltip];
			}
			[_viewDoCoMo addSubview:view];
		}
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ addSubviewDoCoMo] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
}

- (void)addSubviewEZweb:(id)object
{
	@try
	{
		EmojiView *view = [object valueForKey:View];
		NSString *tooltip = [object valueForKey:Tooltip];
		if (view)
		{
			[view setMouseUpAction:@selector(inputEmoji:) withTarget:self];
			if (tooltip)
			{
				[view setToolTip:tooltip];
			}
			[_viewEZweb addSubview:view];
		}
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ addSubviewEZweb] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
}

- (void)addSubviewSoftBank:(id)object
{
	@try
	{
		EmojiView *view = [object valueForKey:View];
		NSString *tooltip = [object valueForKey:Tooltip];
		if (view)
		{
			[view setMouseUpAction:@selector(inputEmoji:) withTarget:self];
			if (tooltip)
			{
				[view setToolTip:tooltip];
			}
			[_viewSoftBank addSubview:view];
		}
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ addSubviewSoftBank] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
}

- (void)addSubviewJaiku:(id)object
{
	@try
	{
		JaikuIconView *view = [object valueForKey:View];
		NSString *tooltip = [object valueForKey:Tooltip];
		[view setMouseUpAction:@selector(setJaikuIcon:) withTarget:self];
		if (view)
		{
			if (tooltip)
			{
				[view setToolTip:tooltip];
			}
			[_viewJaiku addSubview:view];
		}
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ addSubviewJaiku] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
}

- (void)setUpEmojiViewDoCoMo
{
	@try
	{
		if ([_preferences generalUseEmoji])
		{
			NSMutableDictionary *object = [NSMutableDictionary dictionary];
			unichar emoji;
			for (emoji = 0xE63E; emoji <= 0xE757; emoji++)
			{
				int x = 11 + (((emoji - 0xE63E) % 20) * 16);
				int y = (14 + 16 * 14) - (((emoji - 0xE63E) / 20) * 16);
				NSString *tooltip = [[_controller service:Twitter] toolTipDoCoMo:emoji];
				EmojiView *view = [[EmojiView alloc] initWithFrame:NSMakeRect(x, y, 16, 16)
														   unicode:emoji];
				if (view && [view cell])
				{
					[object setObject:view forKey:View];
					if (tooltip)
					{
						[object setObject:tooltip forKey:Tooltip];
					}
					else
					{
						[object removeObjectForKey:Tooltip];
					}
					[self performSelectorOnMainThread:@selector(addSubviewDoCoMo:)
										   withObject:object
										waitUntilDone:YES];
				}
			}
		}
		_setUpDoCoMo = YES;
		LOG(@"[%@ setUpEmojiViewDoCoMo] done", [self className]);
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ setUpEmojiViewDoCoMo] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
}

- (void)setUpEmojiViewEZweb
{
	@try
	{
		if ([_preferences generalUseEmoji])
		{
			NSMutableDictionary *object = [NSMutableDictionary dictionary];
			unichar *unicode = (unichar *)EZweb;
			int i = 0;
			while (*unicode)
			{
				if ([NSImage imageNamed:[NSString stringWithFormat:@"emoji%04X", *unicode]])
				{
					int x = 11 + ((i % 20) * 16);
					int y = (14 + 16 * 32) - ((i / 20) * 16);
					NSString *tooltip = [[_controller service:Twitter] toolTipEZweb:*unicode];
					EmojiView *view = [[EmojiView alloc] initWithFrame:NSMakeRect(x, y, 16, 16)
															   unicode:*unicode];
					if (view && [view cell])
					{
						[object setObject:view forKey:View];
						if (tooltip)
						{
							[object setObject:tooltip forKey:Tooltip];
						}
						else
						{
							[object removeObjectForKey:Tooltip];
						}
						[self performSelectorOnMainThread:@selector(addSubviewEZweb:)
											   withObject:object
											waitUntilDone:YES];
					}
					i++;
				}
				unicode++;
			}
		}
		_setUpEZweb = YES;
		LOG(@"[%@ setUpEmojiViewEZweb] done", [self className]);
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ setUpEmojiViewEZweb] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
}

- (void)setUpEmojiViewSoftBank
{
	@try
	{
		if ([_preferences generalUseEmoji])
		{
			NSMutableDictionary *object = [NSMutableDictionary dictionary];
			unichar *unicode = (unichar *)SoftBank;
			int i = 0;
			while (*unicode)
			{
				if ([NSImage imageNamed:[NSString stringWithFormat:@"emoji%04X", *unicode]])
				{
					int x = 11 + ((i % 20) * 16);
					int y = (14 + 16 * 24) - ((i / 20) * 16);
					NSString *tooltip = [[_controller service:Twitter] toolTipSoftBank:*unicode];
					EmojiView *view = [[EmojiView alloc] initWithFrame:NSMakeRect(x, y, 16, 16)
															   unicode:*unicode];
					if (view && [view cell])
					{
						[object setObject:view forKey:View];
						if (tooltip)
						{
							[object setObject:tooltip forKey:Tooltip];
						}
						else
						{
							[object removeObjectForKey:Tooltip];
						}
						[self performSelectorOnMainThread:@selector(addSubviewSoftBank:)
											   withObject:object
											waitUntilDone:YES];
					}
					i++;
				}
				unicode++;
			}
		}
		_setUpSoftBank = YES;
		LOG(@"[%@ setUpEmojiViewSoftBank] done", [self className]);
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ setUpEmojiViewSoftBank] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
}


- (void)setUpEmojiViewJaiku
{
	@try
	{
		NSMutableDictionary *object = [NSMutableDictionary dictionary];
		int *iconCode = (int *)JaikuIcon;
		int i = 0;
		while (*iconCode)
		{
			NSString *name = [[_controller service:Jaiku] imageNameJaiku:*iconCode];
			NSImage *image = [NSImage imageNamed:name];
			//LOG(@"[%@ setUpEmojiViewJaiku]\n%@\n%@", [self className], name, image);
			if (image)
			{
				int x = 10 + ((i % 10) * 32);
				int y = (14 + 32 * 9) - ((i / 10) * 32);
				NSString *tooltip = [[_controller service:Jaiku] toolTipJaiku:*iconCode];
				JaikuIconView *view = [[JaikuIconView alloc]
									   initWithFrame:NSMakeRect(x, y, 32, 32)
									   iconCode:*iconCode
									   image:image];
				if (view && [view cell])
				{
					[object setObject:view forKey:View];
					if (tooltip)
					{
						[object setObject:tooltip forKey:Tooltip];
					}
					else
					{
						[object removeObjectForKey:Tooltip];
						EXPLOG(@"[%@ setUpEmojiViewJaiku] %04X", [self className], *iconCode);
					}
					[self performSelectorOnMainThread:@selector(addSubviewJaiku:)
										   withObject:object
										waitUntilDone:YES];
				}
				i++;
			}
			iconCode++;
		}
		_setUpJaiku = YES;
		LOG(@"[%@ setUpEmojiViewJaiku] done", [self className]);
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ setUpEmojiViewJaiku] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
}

- (void)setUpEmojiViews
{
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	@try
	{
		[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
		[self setUpEmojiViewDoCoMo];
		[self setUpEmojiViewEZweb];
		[self setUpEmojiViewSoftBank];
		[self setUpEmojiViewJaiku];
		[self enablePopupButoon];
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ setUpEmojiViews] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
	[pool release];
	[NSThread exit];
}

- (void)setEmojiView:(NSTimer *)theTimer
{
	if (theTimer && theTimer.userInfo)
	{
		[theTimer.userInfo setHidden:NO];
	}
	if ([_preferences generalUseEmoji])
	{
		[_buttonEmoji setEnabled:YES];
	}
	else
	{
		[_buttonEmoji2 setEnabled:YES];
	}
	_animatingEmoji = NO;
}

- (NSRect)frameRect:(NSString *)selected
{
	float height = 164 + (_height - 164);
	if ([selected isEqualToString:DOCOMO])
	{
		height = 444 + (_height - 164);
	}
	else if ([selected isEqualToString:AU])
	{
		height = 732 + (_height - 164);
	}
	else if ([selected isEqualToString:SOFTBANK])
	{
		height = 604 + (_height - 164);
	}
	else if ([selected isEqualToString:JAIKU])
	{
		height = 524 + (_height - 164);
	}
	NSRect rect = [_panel frame];
	int		y = rect.origin.y + (rect.size.height - height);
	return NSMakeRect(rect.origin.x, y, rect.size.width, height);
}

- (BOOL)isAnimateEmoji:(NSString *)selected
{
	BOOL animate = NO;
	if ([selected isEqualToString:DOCOMO])
	{
		animate = [_viewDoCoMo isHidden];
	}
	else if ([selected isEqualToString:AU])
	{
		animate = [_viewEZweb isHidden];
	}
	else if ([selected isEqualToString:SOFTBANK])
	{
		animate = [_viewSoftBank isHidden];
	}
	else if ([selected isEqualToString:JAIKU])
	{
		animate = [_viewJaiku isHidden];
	}
	else
	{
		animate = (![_viewDoCoMo isHidden]) || (![_viewEZweb isHidden]) ||
		(![_viewSoftBank isHidden]) || (![_viewJaiku isHidden]);
	}
	return animate;
}

- (void)buttonEmoji:(NSString *)selected
{
	LOG(@"[%@ buttonEmoji]\n%@", [self className], selected);
	BOOL animate = [self isAnimateEmoji:selected];
	NSView *view = [_emojiViews valueForKey:selected];
	if (view != _viewDoCoMo)
	{
		[_viewDoCoMo setHidden:YES];
	}
	if (view != _viewEZweb)
	{
		[_viewEZweb setHidden:YES];
	}
	if (view != _viewSoftBank)
	{
		[_viewSoftBank setHidden:YES];
	}
	if (view != _viewJaiku)
	{
		[_viewJaiku setHidden:YES];
	}
	if (animate)
	{
		if ([_preferences generalUseEmoji])
		{
			[_buttonEmoji setEnabled:NO];
		}
		else
		{
			[_buttonEmoji2 setEnabled:NO];
		}
		_animatingEmoji = YES;
		NSRect	rect = [self frameRect:selected];
		NSTimeInterval interval = [_panel animationResizeTime:rect];
		[_panel setFrame:rect display:YES animate:YES];
		[NSTimer scheduledTimerWithTimeInterval:interval
										 target:self
									   selector:@selector(setEmojiView:)
									   userInfo:view
										repeats:false];
	}
}

- (void)inputEmoji:(id)sender
{
	NSString *text = [NSString stringWithFormat:@"{emoji:%04X}", [sender unicode]];
	LOG(@"[%@ inputEmoji] %@", [self className], text);
	id first = [[_text window] firstResponder];
	if (![first isEqual:_textViewHUD])
	{
		NSRange range = NSMakeRange([[_textViewHUD string] length], 0);
		if ([[NSThread currentThread] isEqual:_mainThread])
		{
			[self makeFirstResponderText];
			[self setSelectedRangeText:range];
		}
	}
	[_textViewHUD insertText:text];
}

- (void)setButtonEmoji:(NSString *)emoji
{
	//LOG(@"[%@ setButtonEmoji] %@", [self className], emoji);
	NSString *select = nil;
	if ([emoji isEqualToString:DOCOMO])
	{
		//LOG(@"[%@ setButtonEmoji] %@", [self className], emoji);
		select = DOCOMO;
	}
	else if ([emoji isEqualToString:AU])
	{
		//LOG(@"[%@ setButtonEmoji] %@", [self className], emoji);
		select = AU;
	}
	else if ([emoji isEqualToString:SOFTBANK])
	{
		//LOG(@"[%@ setButtonEmoji] %@", [self className], emoji);
		select = SOFTBANK;
	}
	else if ([emoji isEqualToString:JAIKU])
	{
		//LOG(@"[%@ setButtonEmoji] %@", [self className], emoji);
		select = JAIKU;
	}
	else
	{
		//LOG(@"[%@ setButtonEmoji] %@", [self className], EMOJI);
		select = EMOJI;
	}
	if (select)
	{
		if ([_preferences generalUseEmoji])
		{
			[_buttonEmoji selectItemWithTitle:select];
		}
		else
		{
			[_buttonEmoji2 selectItemWithTitle:select];
		}
		[self buttonEmoji:select];
	}
}


- (BOOL)isEnabledButtonEmoji
{
	return (_setUpDoCoMo && _setUpEZweb && _setUpSoftBank && !_animatingEmoji);
}

- (BOOL)isEnabledCopy;
{
	return ([_textViewHUD selectedRange].length > 0);
}

- (void)performSetJaikuIconWithCode:(id)object
{
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	@try
	{
		int iconCode = [object intValue];
		int icon = [_viewJaikuIcon iconCode];
		if (iconCode != icon)
		{
			LOG(@"[%@ performSetJaikuIcon] do", [self className]);
		//	id prepare = [[[_text window] undoManager] prepareWithInvocationTarget:self];
		//	[prepare setJaikuIconWithCode:icon];
			if (iconCode)
			{
				NSString *name = [[_controller service:Jaiku] imageNameJaiku:iconCode];
				NSImage *image = [NSImage imageNamed:name];
				[_viewJaikuIcon setIconCode:iconCode];
				[_viewJaikuIcon setImage:image];
				[_viewJaikuIcon setToolTip:CLEAR_JAIKU_ICON];
				[_viewJaikuIcon setEnabled:YES];
				if (icon == 0)
				{
					NSRect frame = [_text frame];
					float width = [_viewJaikuIcon frame].size.width + JAIKU_ICON_MARGIN;
					frame.origin.x += width;
					frame.size.width -= width;
					[_text setFrame:frame];
				}
				[_viewJaikuIcon setHidden:NO];
			}
			else
			{
				[_viewJaikuIcon setToolTip:nil];
				[_viewJaikuIcon setHidden:YES];	
				[_viewJaikuIcon setEnabled:NO];
				[_viewJaikuIcon setIconCode:0];
				[_viewJaikuIcon setImage:nil];
				if (icon != 0)
				{
					NSRect frame = [_text frame];
					float width = [_viewJaikuIcon frame].size.width + JAIKU_ICON_MARGIN;
					frame.origin.x -= width;
					frame.size.width += width;
					[_text setFrame:frame];
				}
			}
		}
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ performSetJaikuIconWithCode] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
	[pool release];
	[NSThread exit];
}

- (void)setJaikuIconWithCode:(int)iconCode
{
	LOG(@"[%@ setJaikuIcon] %d", [self className], iconCode);
	int icon = [_viewJaikuIcon iconCode];
	if (iconCode != icon)
	{
		LOG(@"[%@ setJaikuIcon] do", [self className]);
		id prepare = [[[_text window] undoManager] prepareWithInvocationTarget:self];
		[prepare setJaikuIconWithCode:icon];
		[NSThread detachNewThreadSelector:@selector(performSetJaikuIconWithCode:)
							 toTarget:self
						   withObject:[NSNumber numberWithInt:iconCode]];
	}
}

- (void)setJaikuIcon:(id)sender
{
	[self setJaikuIconWithCode:[sender iconCode]];
}

- (void)clearJaikuIcon:(id)sender
{
	int icon = [_viewJaikuIcon iconCode];
	id prepare = [[[_text window] undoManager] prepareWithInvocationTarget:self];
	[prepare setJaikuIconWithCode:icon];
	[self setJaikuIconWithCode:0];
}

- (void)applicationWillTerminate
{
	if (![_viewDoCoMo isHidden] || ![_viewEZweb isHidden] ||
		![_viewSoftBank isHidden] || ![_viewJaiku isHidden])
	{
		LOG(@"[%@ applicationWillTerminate]", [self className]);
		[_viewDoCoMo setHidden:YES];
		[_viewEZweb setHidden:YES];
		[_viewSoftBank setHidden:YES];
		[_viewJaiku setHidden:YES];
		NSRect	rect = [self frameRect:@""];
		[_panel setFrame:rect display:YES animate:NO];
	}
	[self setStringText:@""];
}

- (BOOL)isDisplay
{
	return [_panel isVisible];
}

- (BOOL)isKeyView
{
	return _keyView;
}

- (void)setController:(AfficheurController *)controller
{
	_controller = controller;
}

- (BOOL)canHide
{
	return [_panel canHide];
}

- (void)setCanHide:(BOOL)canHide
{
	[_panel setCanHide:canHide];
}

- (NSWindow *)panel
{
	return _panel;
}

- (void)panelOrderOut:(id)sender
{
	//LOG(@"[%@ panelOrderOut]", [self className]);
	[_panel orderOut:sender];
}

- (void)panelOrderFront:(id)sender
{
	//LOG(@"[%@ panelOrderFront]", [self className]);
	[_panel orderFront:self];//sender];
}

- (void)panelMakeKeyAndOrderFront:(id)sender
{
	//LOG(@"[%@ panelMakeKeyAndOrderFront]", [self className]);
	[_panel makeKeyAndOrderFront:sender];
}

- (void)performLockText:(id)object
{
	@synchronized(_lockText)
	{
		if (!_lockedText)
		{
			_lockedText = YES;
			[_lockText lock];
		}
	}
}

- (void)lockText
{
	if ([[NSThread currentThread] isEqual:_mainThread])
	{
		[self performLockText:nil];
	}
	else
	{
		[self performSelectorOnMainThread:@selector(performLockText:)
							   withObject:nil
							waitUntilDone:YES];
	}
}

- (void)performUnlockText:(id)object
{
	@synchronized(_lockText)
	{
		if (_lockedText)
		{
			_lockedText = NO;
			[_lockText unlock];
		}
	}
}

- (void)unlockText
{
	if ([[NSThread currentThread] isEqual:_mainThread])
	{
		[self performUnlockText:nil];
	}
	else
	{
		[self performSelectorOnMainThread:@selector(performUnlockText:)
							   withObject:nil
							waitUntilDone:YES];
	}
}

- (void)performSetStringText:(NSString *)string
				   withRange:(NSRange)aRange
{
	LOG(@"[%@ performSetStringText:withRange]", [self className]);
	//LOG(@"[%@ performSetStringText]\n%@", [self className], [string className]);
	//LOG(@"[%@ performSetStringText]\n'%@'", [self className], string);
	@try
	{
		NSString *oldString = [_textViewHUD string];
		NSRange oldRange = [_textViewHUD selectedRange];
		if ((aRange.location >= NSNotFound) ||
			![string isEqualToString:oldString] ||
			!NSEqualRanges(aRange, oldRange))
		{
			//[_textViewHUD selectAll:self];
			NSRange range = NSMakeRange(0, [oldString length]);
			if ([_textViewHUD shouldChangeTextInRange:range replacementString:string])
			{
				id prepare = [[[_textViewHUD window] undoManager] prepareWithInvocationTarget:self];
				[prepare  performSetStringText:[[oldString copy] autorelease]
									 withRange:oldRange];
				LOG(@"[%@ performSetStringText] should\n'%@'\n%d, %d", [self className], string, aRange.location, aRange.length);
				//LOG(@"[%@ performSetStringText] should\n'%@'\n%d, %d", [self className], string, range.location, range.length);
				_stringSetting = YES;
				@try
				{
					[_textViewHUD replaceCharactersInRange:range withString:string];
					[_textViewHUD didChangeText];
					if (aRange.location >= NSNotFound)
					{
						[_textViewHUD selectAll:self];
					}
					else
					{
						if (aRange.location > [string length])
						{
							aRange.location = [string length];
						}
						if (aRange.location + aRange.length > [string length])
						{
							aRange.length = [string length] - aRange.location;
						}
						[_textViewHUD setSelectedRange:aRange];
					}
				}
				@catch (NSException *exception)
				{
					EXPLOG(@"[%@ performSetStringText] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
				}
				@finally
				{
					_stringSetting = NO;
				}
			}
		}
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ performSetStringText] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
}

- (void)performSetStringText:(NSString *)string
{
	[self performSetStringText:string
					 withRange:NSMakeRange(-1, -1)];
}

- (void)setStringText:(NSString *)string
	withWaitUntilDone:(BOOL)waitUntilDone
{
	//LOG(@"[%@ setTextString]", [self className]);
	if ([[NSThread currentThread] isEqual:_mainThread])
	{
		//LOG(@"[%@ setTextString]", [self className]);
		[self performSetStringText:string];
	}
	else
	{
		//LOG(@"[%@ setTextString] OnMainThread", [self className]);
		[self performSelectorOnMainThread:@selector(performSetStringText:)
							   withObject:string
							waitUntilDone:waitUntilDone];
	}
}

- (void)setStringText:(NSString *)string
{
	//LOG(@"[%@ setTextString]", [self className]);
	[self setStringText:string withWaitUntilDone:YES];
}

- (void)performInsertStringText:(NSString *)string
{
	LOG(@"[%@ performInsertStringText]", [self className]);
	//LOG(@"[%@ performInsertStringText]\n%@", [self className], [string className]);
	//LOG(@"[%@ performInsertStringText]\n'%@'", [self className], string);
	@try
	{
		NSString *oldString = [_textViewHUD string];
		NSRange oldRange = [_textViewHUD selectedRange];
		if ([_textViewHUD shouldChangeTextInRange:oldRange replacementString:string])
		{
			id prepare = [[[_textViewHUD window] undoManager] prepareWithInvocationTarget:self];
			[prepare  performSetStringText:[[oldString copy] autorelease]
								 withRange:oldRange];
			LOG(@"[%@ performInsertStringText] should\n'%@'", [self className], string);
			_stringSetting = YES;
			@try
			{
				[_textViewHUD replaceCharactersInRange:oldRange withString:string];
				[_textViewHUD didChangeText];
			}
			@catch (NSException *exception)
			{
				EXPLOG(@"[%@ performInsertStringText] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
			}
			@finally
			{
				_stringSetting = NO;
			}
		}
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ performInsertStringText] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
}

- (void)insertStringText:(NSString *)string
	   withWaitUntilDone:(BOOL)waitUntilDone
{
	//LOG(@"[%@ insertTextString]", [self className]);
	if ([[NSThread currentThread] isEqual:_mainThread])
	{
		//LOG(@"[%@ insertTextString]", [self className]);
		[self performInsertStringText:string];
	}
	else
	{
		//LOG(@"[%@ insertTextString] OnMainThread", [self className]);
		[self performSelectorOnMainThread:@selector(performInsertStringText:)
							   withObject:string
							waitUntilDone:waitUntilDone];
	}
}

- (void)insertStringText:(NSString *)string
{
	//LOG(@"[%@ insertTextString]", [self className]);
	[self insertStringText:string withWaitUntilDone:YES];
}

- (void)performSetEnabledText:(id)enabled
{
	[_text setEnabled:[enabled boolValue]];
}

- (void)setEnabledText:(BOOL)enabled
{
	//LOG(@"[%@ setTextEnabled]", [self className]);
	if ([[NSThread currentThread] isEqual:_mainThread])
	{
		//LOG(@"[%@ setTextString]", [self className]);
		[self performSetEnabledText:[NSNumber numberWithBool:enabled]];
	}
	else
	{
		//LOG(@"[%@ setTextString] OnMainThread", [self className]);
		[self performSelectorOnMainThread:@selector(performSetEnabledText:)
							   withObject:[NSNumber numberWithBool:enabled]
							waitUntilDone:YES];
	}
}

- (BOOL)isEnabledText
{
	//LOG(@"[%@ setTextEnabled]", [self className]);
	return [_text isEnabled];
}

- (NSRange)selectedRangeText
{
	return [_textViewHUD selectedRange];
}

- (void)setSelectedRangeText:(NSRange)range
{
	[_textViewHUD setSelectedRange:range];
}

- (void)performMakeFirstResponderText
{
	[[_text window] makeFirstResponder:_text];
}

- (BOOL)makeFirstResponderText
{
	//LOG(@"[%@ makeFirstResponderText]", [self className]);
	NSResponder *firstResponder = [self firstResponder];
	//LOG(@"[%@ makeFirstResponderText]\n%p: %@\n%p: %@", [self className], firstResponder, firstResponder, _text, _text);
	if (![firstResponder isEqual:_text] && ![firstResponder isEqual:_textViewHUD])
	{
		LOG(@"[%@ makeFirstResponderText] make", [self className]);
		if ([[NSThread currentThread] isEqual:_mainThread])
		{
			[self performMakeFirstResponderText];
		}
		else
		{
			[self performSelectorOnMainThread:@selector(performMakeFirstResponderText)
								   withObject:nil
								waitUntilDone:YES];
		}
		firstResponder = [self firstResponder];
		return ([firstResponder isEqual:_text] || [firstResponder isEqual:_textViewHUD]);
	}
	return NO;
}

- (NSResponder *)firstResponder
{
	return [[_text window] firstResponder];
}

- (BOOL)isFirstResponder
{
	NSResponder *first = [self firstResponder];
	//LOG(@"[%@ isFirstResponder]\n%@", [self className], first);
	return (first == _textViewHUD);
}

- (void)setComplete
{
	[_textViewHUD setCompleteItem:[_preferences complete]];
}

- (NSString *)inReplyTo
{
	return _inReplyTo;
}

- (NSString *)inReplyToText
{
	return _inReplyToText;
}

- (NSString *)inReplyToService
{
	return _inReplyToService;
}

- (int)iconCode
{
	return [_viewJaikuIcon iconCode];
}

- (void)setCheckBox:(NSString *)service
{
	LOG(@"[%@ setCheckBox] %@", [self className], service);
	[_lockClick lock];
	LOG(@"[%@ setCheckBox] locked", [self className]);
	@try
	{
		UInt32 state = ([service isEqualToString:Twitter] ? NSOnState : NSOffState);
		if ([_checkTwitter state] != state)
		{
			[_checkTwitter performClick:nil];
		}
		
		state = ([service isEqualToString:Jaiku] ? NSOnState : NSOffState);
		if ([_checkJaiku state] != state)
		{
			[_checkJaiku performClick:nil];
		}
		
		state = ([service isEqualToString:Tumblr] ? NSOnState : NSOffState);
		if ([_checkTumblr state] != state)
		{
			[_checkTumblr performClick:nil];
		}
		
//		state = ([service isEqualToString:Nowa] ? NSOnState : NSOffState);
//		if ([_checkNowa state] != state)
//		{
//			[_checkNowa performClick:nil];
//		}
		
		state = ([service isEqualToString:Wassr] ? NSOnState : NSOffState);
		if ([_checkWassr state] != state)
		{
			[_checkWassr performClick:nil];
		}
		
		state = ([service isEqualToString:Identica] ? NSOnState : NSOffState);
		if ([_checkIdentica state] != state)
		{
			[_checkIdentica performClick:nil];
		}
		
//		state = ([service isEqualToString:Jisko] ? NSOnState : NSOffState);
//		if ([_checkJisko state] != state)
//		{
//			[_checkJisko performClick:nil];
//		}
		
//		state = ([service isEqualToString:Chuitter] ? NSOnState : NSOffState);
//		if ([_checkChuitter state] != state)
//		{
//			[_checkChuitter performClick:nil];
//		}
		
		state = ([service isEqualToString:FriendFeed] ? NSOnState : NSOffState);
		if ([_checkFriendFeed state] != state)
		{
			[_checkFriendFeed performClick:nil];
		}
		
		state = ([service isEqualToString:FaceBook] ? NSOnState : NSOffState);
		if ([_checkFaceBook state] != state)
		{
			[_checkFaceBook performClick:nil];
		}
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ setCheckBox] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
	@finally
	{
		[_lockClick unlock];
	}
	LOG(@"[%@ setCheckBox] done", [self className]);
}

- (void)setCheckBox:(id)checkBox
		   withUser:(SEL)user
			   pass:(SEL)pass
{
	NSString *userStr = [_preferences performSelector:user];
	NSString *passStr = [_preferences performSelector:pass];
	if (!userStr || [userStr isKindOfClass:[NSNull class]] || [userStr isEqualToString:@""] ||
		!passStr || [passStr isKindOfClass:[NSNull class]] || [passStr isEqualToString:@""])
	{
		[checkBox setEnabled:NO];
	}
	else
	{
		[checkBox setEnabled:YES];
	}
}

- (void)setCheckBox:(id)checkBox
		   withUser:(SEL)user
			   pass:(SEL)pass
			  token:(SEL)token
{
	NSString *userStr = [_preferences performSelector:user];
	NSString *passStr = [_preferences performSelector:pass];
	NSString *tokenStr = [_preferences performSelector:token];
	if (userStr && ![userStr isKindOfClass:[NSNull class]] && ![userStr isEqualToString:@""] &&
		((passStr && ![passStr isKindOfClass:[NSNull class]] && ![passStr isEqualToString:@""]) ||
		 (tokenStr && ![tokenStr isKindOfClass:[NSNull class]] && ![tokenStr isEqualToString:@""])))
	{
		[checkBox setEnabled:YES];
	}
	else
	{
		[checkBox setEnabled:NO];
	}
}

- (void)setCheckBoxEnabled
{
	[self setCheckBox:_checkTwitter    withUser:@selector(accountTwitterUser)    pass:@selector(accountTwitterPass) token:@selector(accountTwitterOAuthKey)];
	[self setCheckBox:_checkJaiku      withUser:@selector(accountJaikuUser)      pass:@selector(accountJaikuOAuthKey)];
	[self setCheckBox:_checkTumblr     withUser:@selector(accountTumblrUser)     pass:@selector(accountTumblrPass)];
//	[self setCheckBox:_checkNowa       withUser:@selector(accountNowaUser)       pass:@selector(accountNowaPass)];
	[self setCheckBox:_checkWassr      withUser:@selector(accountWassrUser)      pass:@selector(accountWassrPass)];
	[self setCheckBox:_checkIdentica   withUser:@selector(accountIdenticaUser)   pass:@selector(accountIdenticaPass)];
//	[self setCheckBox:_checkJisko      withUser:@selector(accountJiskoUser)      pass:@selector(accountJiskoPass)];
//	[self setCheckBox:_checkChuitter   withUser:@selector(accountChuitterUser)   pass:@selector(accountChuitterPass)];
	[self setCheckBox:_checkFriendFeed withUser:@selector(accountFriendFeedUser) pass:@selector(accountFriendFeedOAuthKey)];
	[self setCheckBox:_checkFaceBook   withUser:@selector(accountFaceBookUser)   pass:@selector(accountFaceBookOAuthToken)];
}

- (BOOL)isCheckBoxEnabled:(NSString *)service
{
	if ([service isEqualToString:Twitter])
	{
		return [_checkTwitter isEnabled];
	}
	else if ([service isEqualToString:Jaiku])
	{
		return [_checkJaiku isEnabled];
	}
	else if ([service isEqualToString:Tumblr])
	{
		return [_checkTumblr isEnabled];
	}
//	else if ([service isEqualToString:Nowa])
//	{
//		return [_checkNowa isEnabled];
//	}
	else if ([service isEqualToString:Wassr])
	{
		return [_checkWassr isEnabled];
	}
	else if ([service isEqualToString:Identica])
	{
		return [_checkIdentica isEnabled];
	}
//	else if ([service isEqualToString:Jisko])
//	{
//		return [_checkJisko isEnabled];
//	}
//	else if ([service isEqualToString:Chuitter])
//	{
//		return [_checkChuitter isEnabled];
//	}
	else if ([service isEqualToString:FriendFeed])
	{
		return [_checkFriendFeed isEnabled];
	}
	else if ([service isEqualToString:FaceBook])
	{
		return [_checkFaceBook isEnabled];
	}
	return NO;
}

- (void)setTinyURL:(id)tinyURLize
{
	if ([_checkTinyURLize state] != [tinyURLize boolValue])
	{
		[_checkTinyURLize performClick:nil];
	}
}

- (void)saveCheckBoxs
{
	LOG(@"[%@ saveCheckBoxs]", [self className]);
	[_controller saveState:[_checkTwitter    state] andEnabled:[_checkTwitter    isEnabled] service:Twitter];
	[_controller saveState:[_checkJaiku      state] andEnabled:[_checkJaiku      isEnabled] service:Jaiku];
	[_controller saveState:[_checkTumblr     state] andEnabled:[_checkTumblr     isEnabled] service:Tumblr];
//	[_controller saveState:[_checkNowa       state] andEnabled:[_checkNowa       isEnabled] service:Nowa];
	[_controller saveState:[_checkWassr      state] andEnabled:[_checkWassr      isEnabled] service:Wassr];
	[_controller saveState:[_checkIdentica   state] andEnabled:[_checkIdentica   isEnabled] service:Identica];
//	[_controller saveState:[_checkJisko      state] andEnabled:[_checkJisko      isEnabled] service:Jisko];
//	[_controller saveState:[_checkChuitter   state] andEnabled:[_checkChuitter   isEnabled] service:Chuitter];
	[_controller saveState:[_checkFriendFeed state] andEnabled:[_checkFriendFeed isEnabled] service:FriendFeed];
	[_controller saveState:[_checkFaceBook   state] andEnabled:[_checkFaceBook   isEnabled] service:FaceBook];
	LOG(@"[%@ saveCheckBoxs] done", [self className]);
}

- (void)saveTinyURLize
{
	_saveTinyURLize = [_checkTinyURLize state];
}

- (void)restoreCheckBoxs
{
	LOG(@"[%@ restoreCheckBoxs]", [self className]);
	[_lockClick lock];
	LOG(@"[%@ restoreCheckBoxs] locked", [self className]);
	@try
	{
		//LOG(@"[%@ restoreCheckBoxs] Twitter", [self className]);
		[_checkTwitter setEnabled:[_controller enable:Twitter]];
		if ([_checkTwitter state] != [_controller status:Twitter])
		{
			//LOG(@"[%@ restoreCheckBoxs] performTwitter", [self className]);
			[_checkTwitter performClick:nil];
		}
		//LOG(@"[%@ restoreCheckBoxs] Jaiku", [self className]);
		[_checkJaiku setEnabled:[_controller enable:Jaiku]];
		if ([_checkJaiku state] != [_controller status:Jaiku])
		{
			//LOG(@"[%@ restoreCheckBoxs] performJaiku", [self className]);
			[_checkJaiku performClick:nil];
		}
		//LOG(@"[%@ restoreCheckBoxs] Tumblr", [self className]);
		[_checkTumblr setEnabled:[_controller enable:Tumblr]];
		if ([_checkTumblr state] != [_controller status:Tumblr])
		{
			//LOG(@"[%@ restoreCheckBoxs] performTumblr", [self className]);
			[_checkTumblr performClick:nil];
		}
//		[_checkNowa setEnabled:[_controller enable:Nowa]];
//		if ([_checkNowa state] != [_controller status:Nowa])
//		{
//			[_checkNowa performClick:nil];
//		}
		//LOG(@"[%@ restoreCheckBoxs] Wassr", [self className]);
		[_checkWassr setEnabled:[_controller enable:Wassr]];
		if ([_checkWassr state] != [_controller status:Wassr])
		{
			//LOG(@"[%@ restoreCheckBoxs] performWassr", [self className]);
			[_checkWassr performClick:nil];
		}
		//LOG(@"[%@ restoreCheckBoxs] Identica", [self className]);
		[_checkIdentica setEnabled:[_controller enable:Identica]];
		if ([_checkIdentica state] != [_controller status:Identica])
		{
			//LOG(@"[%@ restoreCheckBoxs] performIdentica", [self className]);
			[_checkIdentica performClick:nil];
		}
//		//LOG(@"[%@ restoreCheckBoxs] Jisko", [self className]);
//		[_checkJisko setEnabled:[_controller enable:Jisko]];
//		if ([_checkJisko state] != [_controller status:Jisko])
//		{
//			//LOG(@"[%@ restoreCheckBoxs] performJisko", [self className]);
//			[_checkJisko performClick:nil];
//		}
//		//LOG(@"[%@ restoreCheckBoxs] Chuitter", [self className]);
//		[_checkChuitter setEnabled:[_controller enable:Chuitter]];
//		if ([_checkChuitter state] != [_controller status:Chuitter])
//		{
//			//LOG(@"[%@ restoreCheckBoxs] performChuitter", [self className]);
//			[_checkChuitter performClick:nil];
//		}
		//LOG(@"[%@ restoreCheckBoxs] FriendFeed", [self className]);
		[_checkFriendFeed setEnabled:[_controller enable:FriendFeed]];
		if ([_checkFriendFeed state] != [_controller status:FriendFeed])
		{
			//LOG(@"[%@ restoreCheckBoxs] performFriendFeed", [self className]);
			[_checkFriendFeed performClick:nil];
		}
		//LOG(@"[%@ restoreCheckBoxs] FaceBook", [self className]);
		[_checkFaceBook setEnabled:[_controller enable:FaceBook]];
		if ([_checkFaceBook state] != [_controller status:FaceBook])
		{
			//LOG(@"[%@ restoreCheckBoxs] performFaceBook", [self className]);
			[_checkFaceBook performClick:nil];
		}
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ restoreCheckBoxs] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
	@finally
	{
		[_lockClick unlock];
	}
	LOG(@"[%@ restoreCheckBoxs] done", [self className]);
}

- (void)restoreTinyURLize
{
	[self setTinyURL:[NSNumber numberWithBool:_saveTinyURLize]];
}

- (void)clearReply
{
	[_reply setStringValue:@""];
}

- (void)performSetReply
{
	LOG(@"[%@ performSetReply]", [self className]);
	if (![_lockReply tryLock])
	{
		LOG(@"[%@ performSetReply] WAIT", [self className]);
		[_lockReply lock];
		LOG(@"[%@ performSetReply] RESUME", [self className]);
	}
	@try
	{
		if (_inReplyToText && ![_inReplyToText isEqualToString:@""])
		{
			NSString *text = [NSString stringWithFormat:@"%@%@", _inReplyToText, [_text stringValue]];
			[self setStringText:text];
		}
		if (_inReplyTo)
		{
			if (!_saveState)
			{
				_saveState = YES;
				[self performSelectorOnMainThread:@selector(saveCheckBoxs)
									   withObject:nil
									waitUntilDone:YES];
				[self performSelectorOnMainThread:@selector(saveTinyURLize)
									   withObject:nil
									waitUntilDone:YES];
			}
			[self performSelectorOnMainThread:@selector(setCheckBox:)
								   withObject:_inReplyToService
								waitUntilDone:YES];
			[self performSelectorOnMainThread:@selector(setTinyURL:)
								   withObject:[NSNumber numberWithBool:_inReplyTinyURLize]
								waitUntilDone:YES];
		}
		else
		{
			[self performSelectorOnMainThread:@selector(clearReply)
								   withObject:nil
								waitUntilDone:YES];
			if (_saveState)
			{
				[self performSelectorOnMainThread:@selector(restoreCheckBoxs)
									   withObject:nil
									waitUntilDone:YES];
				[self performSelectorOnMainThread:@selector(restoreTinyURLize)
									   withObject:nil
									waitUntilDone:YES];
				_saveState = NO;
			}
		}
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ performSetReply] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
	@finally
	{
		[_lockReply unlock];
	}
	LOG(@"[%@ performSetReply] done", [self className]);
}

- (void)performReplySetReply:(NSAttributedString *)reply
{
	LOG(@"[%@ performReplySetReply]", [self className]);
	//[_reply setStringValue:reply];
	[_reply setAttributedStringValue:reply];
	if (!reply || (_keyView && [self isFirstResponder]))
	{
		[self performSetReply];
	}
	else if ([self isDisplay])
	{
		[self panelMakeKeyAndOrderFront:self];
		[self performSetReply];
	}
	else
	{
		[_controller onOpen:self];
	}
	LOG(@"[%@ performReplySetReply] done", [self className]);
}

- (void)replySetReply:(NSAttributedString *)reply
{
	if ([[NSThread currentThread] isEqual:_mainThread])
	{
		[self performReplySetReply:reply];
	}
	else
	{
		[self performSelectorOnMainThread:@selector(performReplySetReply:)
							   withObject:reply
							waitUntilDone:YES];
	}
}

- (void)setReply:(NSAttributedString *)reply
	 withService:(NSString *)service
	   inReplyTo:(NSString *)inReplyTo
			text:(NSString *)text
	  tinyURLize:(BOOL)tinyURLize
		  sender:(id)sender
{
	static int ccc = 0;
	[reply retain];
	@try
	{
		LOG(@"[%@ setReply]", [self className]);
		//LOG(@"[%@ setReply]\nreply: %@\nservice: %@\ninReplyTo: %@\ntext: %@\nsender: %@",
		//	[self className], reply, service, inReplyTo, text, sender);
		if ((sender != self) || ![reply isEqualTo:_inReplyReply])
		{
			LOG(@"[%@ setReply] do %d", [self className], ccc);
			ccc++;
			id prepare = [[[_text window] undoManager] prepareWithInvocationTarget:self];
			[prepare setReply:[[[_reply attributedStringValue] copy] autorelease]
				  withService:[[[self inReplyToService] copy] autorelease]
					inReplyTo:[[[self inReplyTo] copy] autorelease]
						 text:@""
				   tinyURLize:_inReplyTinyURLize
					   sender:self];
			if (_inReplyTo)
			{
				[_inReplyToText release];
				[_inReplyToService release];
				[_inReplyTo release];
				[_inReplyReply release];
				_inReplyToText = nil;
				_inReplyToService = nil;
				_inReplyTo = nil;
				_inReplyReply = nil;
			}
			if (inReplyTo)
			{
				_inReplyToText = [text copy];
				_inReplyToService = [service copy];
				_inReplyTo = [inReplyTo copy];
				_inReplyReply = [reply copy];
				_inReplyTinyURLize = tinyURLize;
			}
			[self replySetReply:_inReplyReply];
			ccc--;
		}
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ setReply] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
	@finally
	{
		[reply release];
	}
	LOG(@"[%@ setReply] done", [self className]);
}

- (void)setReply:(NSAttributedString *)reply
	 withService:(NSString *)service
	   inReplyTo:(NSString *)inReplyTo
			text:(NSString *)text
		  sender:(id)sender
{
	[self setReply:reply
	   withService:service
		 inReplyTo:inReplyTo
			  text:text
		tinyURLize:[_checkTinyURLize state]
			sender:sender];
}

- (void)setAdjustPanel
{
	if (_adjustPanel)
	{
		[[_text cell] setScrollable:NO];
	}
	else
	{
		[[_text cell] setScrollable:YES];
	}
}

- (void)performClick:(id)target
		  withSource:(id)source
			   state:(int)state
{
	//LOG(@"[%@ performClick]", [self className]);
	if ([target isEnabled] && ![target isEqual:source] && ([target state] != state))
	{
		[target performClick:nil];
	}
}

- (void)doClickServiceAll:(id)check
{
	//LOG(@"[%@ doClickAll]", [self className]);
	int state = [check state];
	[self performClick:_checkTwitter    withSource:check state:state];
	[self performClick:_checkJaiku      withSource:check state:state];
	[self performClick:_checkTumblr     withSource:check state:state];
//	[self performClick:_checkNowa       withSource:check state:state];
	[self performClick:_checkWassr      withSource:check state:state];
	[self performClick:_checkIdentica   withSource:check state:state];
//	[self performClick:_checkJisko      withSource:check state:state];
//	[self performClick:_checkChuitter   withSource:check state:state];
	[self performClick:_checkFriendFeed withSource:check state:state];
	[self performClick:_checkFaceBook   withSource:check state:state];
}

- (void)doClickServiceOnly:(id)check
{
	//LOG(@"[%@ doClickOnly]", [self className]);
	int state = [check state] ? NSOffState : NSOnState;
	[self performClick:_checkTwitter    withSource:check state:state];
	[self performClick:_checkJaiku      withSource:check state:state];
	[self performClick:_checkTumblr     withSource:check state:state];
//	[self performClick:_checkNowa       withSource:check state:state];
	[self performClick:_checkWassr      withSource:check state:state];
	[self performClick:_checkIdentica   withSource:check state:state];
//	[self performClick:_checkJisko      withSource:check state:state];
//	[self performClick:_checkChuitter   withSource:check state:state];
	[self performClick:_checkFriendFeed withSource:check state:state];
	[self performClick:_checkFaceBook   withSource:check state:state];
}

- (void)doClickService:(id)checkBox
{
	//LOG(@"[%@ doClickService]", [self className]);
	if ([_lockClick tryLock])
	{
		//LOG(@"[%@ doClickService] do", [self className]);
		@try
		{
			//LOG(@"[%@ doClick]", [self className]);
			UInt32 modifiers = [_controller keyModifiers];
			if (modifiers == NSAlternateKeyMask)
			{
				[self doClickServiceAll:checkBox];
			}
			else if (modifiers == NSCommandKeyMask)
			{
				[self doClickServiceOnly:checkBox];
			}
		}
		@catch (NSException *exception)
		{
			EXPLOG(@"[%@ doClick] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
		}
		@finally
		{
			[_lockClick unlock];
		}
	}
	//LOG(@"[%@ doClickService] done", [self className]);
}

- (void)performLabelsetStringValue:(NSString *)text
{
	[_label setStringValue:text];
}

- (void)threadControlTextDidChange:(id)object
{
	//NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
	@try
	{
		for (;;)
		{
			NSAutoreleasePool *internalPool = [[NSAutoreleasePool alloc] init];
			[_lockText lock];
			//LOG(@"[%@ threadControlTextDidChange]", [self className]);
			NSString *text = [@"" copy];
			@try
			{
				@synchronized(_queueText)
				{
					while ([_queueText count] > 0)
					{
						[text release];
						text = [[_queueText objectAtIndex:0] retain];
						[_queueText removeObjectAtIndex:0];
					}
				}
				//LOG(@"[%@ threadControlTextDidChange] isURL", [self className]);
				NSString* opt = @"";
				if (![text isEqualToString:@""] && [_controller isURL:text])
				{
					NSString *tiny = [_controller encodeTinyURL:text withCreateFlag:NO];
					if (tiny)
					{
						opt = [NSString stringWithFormat:@" (%u / %uB)",
							   [tiny length],
							   [tiny lengthOfBytesUsingEncoding:NSUTF8StringEncoding]
							   ];
					}
				}
				//LOG(@"[%@ threadControlTextDidChange] setStringValue", [self className]);
				NSString *label = [NSString stringWithFormat:@"%u / %uB%@",
								   [text length],
								   [text lengthOfBytesUsingEncoding:NSUTF8StringEncoding],
								   opt];
				[self performSelectorOnMainThread:@selector(performLabelsetStringValue:)
									   withObject:label
									waitUntilDone:NO];
				//LOG(@"[%@ threadControlTextDidChange]\n%@", [self className], label);
			}
			@catch (NSException *exception)
			{
				EXPLOG(@"[%@ threadControlTextDidChange] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
			}
			@finally
			{
				[text release];
			}
			//LOG(@"[%@ threadControlTextDidChange] done", [self className]);
			[_lockText unlock];
			[self lockText];
			[internalPool release];
		}
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ threadControlTextDidChange] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
	//[pool release];
	[NSThread exit];
}

- (void)startThread
{
	[self lockText];
	[NSThread detachNewThreadSelector:@selector(threadControlTextDidChange:)
							 toTarget:self
						   withObject:nil];
}

- (void)copyUndoOldText:(id)object
{
	if ([object isKindOfClass:[NSTextView class]])
	{
		if (_undoOldText)
		{
			[_undoOldText release];
		}
		_undoOldText = [[object string] copy];
		_undoOldRange = [object selectedRange];
	}
}

- (void)controlTextDidChange
{
	//LOG(@"[%@ controlTextDidChange]", [self className]);
	NSString* txt = [[_text stringValue] retain];
	//LOG(@"[%@ controlTextDidChange] %@", [self className], txt);
	@try
	{
		@synchronized(_queueText)
		{
			[_queueText addObject:txt];
		}
		[self unlockText];
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ controlTextDidChange] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
	@finally
	{
		[txt release];
	}
	//LOG(@"[%@ controlTextDidChange] done", [self className]);
}

- (void)adjustFrameDisplay:(NSWindow *)window
				  withDiff:(float)diff
{
	NSRect rect = [window frame];
	rect.size.height -= diff;
	rect.origin.y += diff;
	[window setFrame:rect display:YES];
}

- (void)adjustFrame:(NSView *)view
		   withDiff:(float)diff
{
	NSRect rect = [view frame];
	[[view superview] setNeedsDisplayInRect:rect];
	rect.size.height -= diff;
	rect.origin.y += diff;
	[view setFrame:rect];
	[view setNeedsDisplay:YES];
}

- (void)adjustFrameOrigin:(NSView *)view
				 withDiff:(float)diff
{
	NSRect rect = [view frame];
	[[view superview] setNeedsDisplayInRect:rect];
	rect.origin.y += diff;
	[view setFrameOrigin:rect.origin];
	[view setNeedsDisplay:YES];
}

- (void)adjustTextFieldSize
{
	if (_adjustPanel)
	{
		@try
		{
			float usedHeight = [[_textViewHUD layoutManager]
								usedRectForTextContainer:[_textViewHUD textContainer]].size.height;
			float cellHeight = [[_text cell] cellSizeForBounds:[_text bounds]].height;
			//cellHeight = 0;
			if (usedHeight > cellHeight)
			{
				cellHeight = usedHeight;
			}
			if (_cellHeight != cellHeight)
			{
				LOG2(@"[%@ adjustTextFieldSize] cellSize:%f", [self className], cellHeight);
				float diff = (_cellHeight - cellHeight);
				_cellHeight = cellHeight;
				_height -= diff;
				[self adjustFrameDisplay:_panel withDiff:diff];
				[self adjustFrame:_textBox withDiff:diff];
				[self adjustFrameOrigin:_label withDiff:diff];
				[self adjustFrameOrigin:_checkTinyURLize withDiff:diff];
				[self adjustFrameOrigin:_checkTwitter withDiff:diff];
				[self adjustFrameOrigin:_checkJaiku withDiff:diff];
				[self adjustFrameOrigin:_checkIdentica withDiff:diff];
				[self adjustFrameOrigin:_checkTumblr withDiff:diff];
				[self adjustFrameOrigin:_checkWassr withDiff:diff];
//				[self adjustFrameOrigin:_checkJisko withDiff:diff];
//				[self adjustFrameOrigin:_checkChuitter withDiff:diff];
				[self adjustFrameOrigin:_checkFriendFeed withDiff:diff];
				[self adjustFrameOrigin:_checkFaceBook withDiff:diff];
				[self adjustFrameOrigin:_buttonEmoji withDiff:diff];
				[self adjustFrameOrigin:_buttonEmoji2 withDiff:diff];
				[self adjustFrameOrigin:_viewDoCoMo withDiff:diff];
				[self adjustFrameOrigin:_viewEZweb withDiff:diff];
				[self adjustFrameOrigin:_viewSoftBank withDiff:diff];
				//	[self adjustFrameOrigin:_viewJaiku withDiff:diff];
			}
		}
		@catch (NSException *exception)
		{
			EXPLOG(@"[%@ adjustTextFieldSize] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
		}
	}
}

#pragma mark NSObject Delegates

- (void)awakeFromNib
{
	//LOG(@"[%@ awakeFromNib]", [self className]);
	[_panel setCanHide:YES];
	if (![_preferences frameMain])
	{
		NSRect frame = [_panel frame];
		NSRect screen = [[NSScreen mainScreen] frame];
		frame.origin.x = (screen.size.width - frame.size.width) / 2 + screen.origin.x;
		frame.origin.y = (screen.size.height - frame.size.height) / 4 * 3 + screen.origin.y;
		[_panel setFrame:frame display:YES];
	}
	_height = [_panel frame].size.height;
	[_textViewHUD setCompleteItem:[_preferences complete]];
	[_spinner startAnimation:self];
	_emojiViews = [[NSDictionary alloc] initWithObjectsAndKeys:
				   _viewDoCoMo, DOCOMO,
				   _viewEZweb, AU,
				   _viewSoftBank, SOFTBANK,
				   _viewJaiku, JAIKU,
				   nil];
	[_viewJaikuIcon setMouseUpAction:@selector(clearJaikuIcon:) withTarget:self];
	[_viewJaikuIcon setEnabled:NO];
	[_viewJaikuIcon setHidden:YES];
	[_viewJaikuIcon setImageFrameStyle:NSImageFrameNone];
	[self setAdjustPanel];
	_inReplyTinyURLize = [_checkTinyURLize state];
	_saveTinyURLize = [_checkTinyURLize state];
	_cellHeight = [[_text cell] cellSizeForBounds:[_text bounds]].height;
	[NSThread detachNewThreadSelector:@selector(setUpEmojiViews)
							 toTarget:self
						   withObject:nil];
}

#pragma mark TextFieldHUD Delegates

- (void)textDidBeginEditing:(NSNotification *)aNotification
{
	//LOG(@"[%@ textDidBeginEditing]", [self className]);
	if (aNotification)
	{
		id object = [aNotification valueForKey:@"object"];
		if (object)
		{
			//LOG(@"[%@ textDidBeginEditing]\n%@", [self className], object);
			[self copyUndoOldText:object];
		}
	}
}

- (void)textDidEndEditing:(NSNotification *)aNotification
{
	//LOG(@"[%@ controlTextDidEndEditing]", [self className]);
}

- (void)textViewDidChangeSelection:(NSNotification *)aNotification
{
	//LOG(@"[%@ textViewDidChangeSelection]", [self className]);
	id object = [aNotification valueForKey:@"object"];
	NSRange range = [object selectedRange];
	if (!NSEqualRanges(range, _undoOldOldRange))
	{
		//LOG(@"[%@ textViewDidChangeSelection]\n%@: %d, %d / %d, %d", [self className], [object className], range.location, range.length, _undoOldOldRange.location, _undoOldOldRange.length);
		if ((range.location >= 0) && (range.length >= 0))
		{
			_undoOldRange = _undoOldOldRange;
			_undoOldOldRange = range;
		}
	}
}

- (void)textDidChange:(NSNotification *)aNotification
{
	//LOG(@"[%@ textDidChange]", [self className]);
	NSString* txt = [[_text stringValue] retain];
	@try
	{
		//LOG(@"[%@ textDidChange]\n%@", [self className], aNotification);
		//LOG(@"[%@ textDidChange]", [self className]);
		id object = [aNotification valueForKey:@"object"];
		if (object)
		{
			[txt release];
			txt = [[object string] retain];
			if (_undoOldText)
			{
				id prepare = [[[object window] undoManager] prepareWithInvocationTarget:self];
				[prepare performSetStringText:[[_undoOldText copy] autorelease]
									withRange:_undoOldRange];
				[self copyUndoOldText:object];
				if (![txt isEqualToString:@""] && !_completePosting && !_commandHandling && !_stringSetting)
				{
					NSRange range = [object selectedRange];
					//LOG(@"[%@ controlTextDidChange]\n%d, %d", [self className], range.location, range.length);
					BOOL isComplete = NO;
					int location = range.location - 1;
					while (location >= 0)
					{
						unichar character = [txt characterAtIndex:location];
						if ((character == '#') || (character == '!'))
						{
							if (location > 0)
							{
								unichar character2 = [txt characterAtIndex:location - 1];
								if (character != character2)
								{
									isComplete = YES;
									break;
								}
							}
							else
							{
								isComplete = YES;
								break;
							}
						}
						else if((character == ' ') || (character == [@"　" characterAtIndex:0]))
						{
							break;
						}
						location--;
					}
					if (isComplete)
					{
						//LOG(@"[%@ controlTextDidChange]\n%d, %d", [self className], location, range.location - location);
						_completePosting = YES;
						[object setCompleteRange:NSMakeRange(location, range.location - location)];
						[object complete:nil];
						_completePosting = NO;
					}
				}
			}
		}
		@synchronized(_queueText)
		{
			[_queueText addObject:txt];
		}
		[self adjustTextFieldSize];
		[self unlockText];
	}
	@catch (NSException *exception)
	{
		EXPLOG(@"[%@ textDidChange] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
	}
	@finally
	{
		[txt release];
	}
}

- (void)enterKeyDown:(id)object
{
	if ([object isKindOfClass:[NSNumber class]])
	{
		int postTo = [_preferences generalPostTo];
		int postToALT = [_preferences generalPostToALT];
		int	modifiers = [object intValue] &	(NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask| NSCommandKeyMask);
		BOOL	doAfficheur = NO;
		switch (modifiers)
		{
			case 0:
				if (postTo == PostToEnter)
				{
					doAfficheur = YES;
				}
				else if (postToALT == PostToEnter)
				{
					doAfficheur = YES;
				}
				break;
			case NSControlKeyMask:
				if (postTo == PostToControlEnter)
				{
					doAfficheur = YES;
				}
				else if (postToALT == PostToControlEnter)
				{
					doAfficheur = YES;
				}
				break;
			case NSAlternateKeyMask:
				if (postTo == PostToOptionEnter)
				{
					doAfficheur = YES;
				}
				else if (postToALT == PostToOptionEnter)
				{
					doAfficheur = YES;
				}
				break;
			case NSShiftKeyMask:
				if (postTo == PostToShiftEnter)
				{
					doAfficheur = YES;
				}
				else if (postToALT == PostToShiftEnter)
				{
					doAfficheur = YES;
				}
				break;
			case NSControlKeyMask | NSShiftKeyMask:
				if (postTo == PostToControlShiftEnter)
				{
					doAfficheur = YES;
				}
				else if (postToALT == PostToControlShiftEnter)
				{
					doAfficheur = YES;
				}
				break;
			case NSAlternateKeyMask | NSShiftKeyMask:
				if (postTo == PostToOptionShiftEnter)
				{
					doAfficheur = YES;
				}
				else if (postToALT == PostToOptionShiftEnter)
				{
					doAfficheur = YES;
				}
				break;
		}
		if (doAfficheur)
		{
			LOG2(@"[%@ enterKeyUp]", [self className]);
			_doCommand = 1;
		}
	}
}

- (void)enterKeyUp:(id)object
{
	if ([object isKindOfClass:[NSNumber class]])
	{
		int postTo = [_preferences generalPostTo];
		int postToALT = [_preferences generalPostToALT];
		int	modifiers = [object intValue] &	(NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask| NSCommandKeyMask);
		NSString* text = [[_textViewHUD textStorage] string];
		if (_doCommand == 2)
		{
			BOOL	doAfficheur = NO;
			BOOL	altEncodeTinyURL = NO;
			switch (modifiers)
			{
				case 0:
					if (postTo == PostToEnter)
					{
						doAfficheur = YES;
					}
					else if (postToALT == PostToEnter)
					{
						doAfficheur = YES;
						altEncodeTinyURL = YES;
					}
					break;
				case NSControlKeyMask:
					if (postTo == PostToControlEnter)
					{
						doAfficheur = YES;
					}
					else if (postToALT == PostToControlEnter)
					{
						doAfficheur = YES;
						altEncodeTinyURL = YES;
					}
					break;
				case NSAlternateKeyMask:
					if (postTo == PostToOptionEnter)
					{
						doAfficheur = YES;
					}
					else if (postToALT == PostToOptionEnter)
					{
						doAfficheur = YES;
						altEncodeTinyURL = YES;
					}
					break;
				case NSShiftKeyMask:
					if (postTo == PostToShiftEnter)
					{
						doAfficheur = YES;
					}
					else if (postToALT == PostToShiftEnter)
					{
						doAfficheur = YES;
						altEncodeTinyURL = YES;
					}
					break;
				case NSControlKeyMask | NSShiftKeyMask:
					if (postTo == PostToControlShiftEnter)
					{
						doAfficheur = YES;
					}
					else if (postToALT == PostToControlShiftEnter)
					{
						doAfficheur = YES;
						altEncodeTinyURL = YES;
					}
					break;
				case NSAlternateKeyMask | NSShiftKeyMask:
					if (postTo == PostToOptionShiftEnter)
					{
						doAfficheur = YES;
					}
					else if (postToALT == PostToOptionShiftEnter)
					{
						doAfficheur = YES;
						altEncodeTinyURL = YES;
					}
					break;
			}
			if (doAfficheur)
			{
				LOG2(@"[%@ enterKeyDown]", [self className]);
				[_controller doPost:text withAltEncodeTinyURL:altEncodeTinyURL];
			}
		}
	}
	_doCommand = 0;
}

#pragma mark NSTextView Delegates

- (BOOL)control:(NSControl *)control
	   textView:(NSTextView *)textView
doCommandBySelector:(SEL)command
{
	NSString *cmd = NSStringFromSelector(command);
	//LOG(@"[%@ doCommandBySelector] %@", [self className], cmd);
	if (cmd && (textView == _textViewHUD))
	{
		if ([cmd isEqualToString:@"deleteBackward:"])
		{
			if ([[_text stringValue] isEqualToString:@""])
			{
				if ([self iconCode] != 0)
				{
					[self clearJaikuIcon:self];
					return NO;
				}
				else if ([self inReplyToText])
				{
					id prepare = [[[_text window] undoManager] prepareWithInvocationTarget:self];
					[prepare setReply:[[[_reply attributedStringValue] copy] autorelease]
						  withService:[[[self inReplyToService] copy] autorelease]
							inReplyTo:[[[self inReplyTo] copy] autorelease]
								 text:@""
							   sender:self];
					[self setReply:@"" withService:@"" inReplyTo:nil text:nil sender:nil];
					return NO;
				}
			}
		}
		if ([cmd isEqualToString:@"insertNewline:"])
		{
			command = @selector(insertLineBreak:);
			cmd = NSStringFromSelector(command);
		}
		if (_doCommand == 1)
		{
			_doCommand = 2;
		}
		else if ([cmd isEqualToString:@"cancelOperation:"])
		{
			LOG(@"[%@ doCommandBySelector] cancelOperation", [self className]);
			if ([_controller isSnowLeopard])
			{
				if (![[_text stringValue] isEqualToString:@""])
				{
					id prepare = [[[textView window] undoManager] prepareWithInvocationTarget:self];
					[prepare performSetStringText:[[_undoOldText copy] autorelease]
										withRange:_undoOldRange];
					[_text setStringValue:@""];
					[self copyUndoOldText:textView];
					[self adjustTextFieldSize];
					return YES;
				}
			}
			return NO;
		}
		else if ([cmd isEqualToString:@"cancel:"])
		{
			LOG(@"[%@ doCommandBySelector] cancel", [self className]);
			return NO;
		}
		else if ([textView respondsToSelector:command])
		{
			_commandHandling = YES;
			[textView performSelector:command withObject:nil];
			_commandHandling = NO;
			LOG2(@"[%@ doCommandBySelector] perform:%@", [self className], cmd);
		}
		[self adjustTextFieldSize];
	}
	return YES;
}

#pragma mark Window Delegates

- (id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)anObject
{
	if ([anObject isKindOfClass:[TextFieldHUD class]])
	{
		[_textViewHUD setupSelectedTextAttributes];
		return _textViewHUD;
	}
	return nil;
}

- (BOOL)windowShouldClose:(id)window
{
	LOG(@"[%@ windowShouldClose]", [self className]);
	[_controller hideAfficheur];
	return NO;
}

- (void)windowDidBecomeKey:(NSNotification *)notification
{
	//LOG(@"[%@ windowDidBecomeKey]\n%@", [self className], notification);
	_keyView = YES;
}

- (void)windowDidResignKey:(NSNotification *)notification
{
	//LOG(@"[%@ windowDidResignKey]\n%@", [self className], notification);
	_keyView = NO;
}

#pragma mark IBActions

- (IBAction)onClickTwitter:(id)sender
{
	//LOG(@"[%@ onClickTwitter]\n%@", [self className], sender);
	[self doClickService:_checkTwitter];
}

- (IBAction)onClickJaiku:(id)sender
{
	[self doClickService:_checkJaiku];
}

- (IBAction)onClickTumblr:(id)sender
{
	[self doClickService:_checkTumblr];
}

//- (IBAction)onClickNowa:(id)sender
//{
//	[self doClickService:_checkNowa];
//}

- (IBAction)onClickWassr:(id)sender
{
	[self doClickService:_checkWassr];
}

- (IBAction)onClickIdentica:(id)sender
{
	[self doClickService:_checkIdentica];
}

//- (IBAction)onClickJisko:(id)sender
//{
//	[self doClickService:_checkJisko];
//}

//- (IBAction)onClickChuitter:(id)sender
//{
//	[self doClickService:_checkChuitter];
//}

- (IBAction)onClickFriendFeed:(id)sender
{
	[self doClickService:_checkFriendFeed];
}

- (IBAction)onClickFaceBook:(id)sender
{
	[self doClickService:_checkFaceBook];
}

- (IBAction)onButtonEmoji:(id)sender
{
	LOG(@"[%@ onButtonEmoji]\n%@\n%@", [self className], [sender className], [sender titleOfSelectedItem]);
	NSString *title = [sender titleOfSelectedItem];
	if ([_preferences generalUseEmoji] ||
		(![title isEqualToString:docomo] &&
		![title isEqualToString:au] &&
		![title isEqualToString:softbank]))
	{
		LOG(@"[%@ onButtonEmoji]\n%@\n%@", [self className], [sender className], [sender titleOfSelectedItem]);
		if ([self isAnimateEmoji:[sender titleOfSelectedItem]])
		{
			NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[sender titleOfSelectedItem]
														  action:nil
												   keyEquivalent:@""];
			@try
			{
				[_controller onEmoji:item];
			}
			@catch (NSException *exception)
			{
				EXPLOG(@"[%@ onButtonEmoji] EXCEPTION\n%@: %@", [self className], [exception name], [exception reason]);
			}
			@finally
			{
				[item release];
			}
		}
	}
}

@end
