1 /*
2 
3  Copyright (c) 2003-2013 uim Project https://github.com/uim/uim
4 
5  All rights reserved.
6 
7  Redistribution and use in source and binary forms, with or without
8  modification, are permitted provided that the following conditions
9  are met:
10 
11  1. Redistributions of source code must retain the above copyright
12     notice, this list of conditions and the following disclaimer.
13  2. Redistributions in binary form must reproduce the above copyright
14     notice, this list of conditions and the following disclaimer in the
15     documentation and/or other materials provided with the distribution.
16  3. Neither the name of authors nor the names of its contributors
17     may be used to endorse or promote products derived from this software
18     without specific prior written permission.
19 
20  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
21  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
24  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  SUCH DAMAGE.
31 
32 */
33 #include <config.h>
34 
35 #include "qt.h"
36 
37 #include <qapplication.h>
38 #include <qlistview.h>
39 #include <qvbuttongroup.h>
40 #include <qradiobutton.h>
41 #include <qsocketnotifier.h>
42 #include <qpushbutton.h>
43 #include <qtextcodec.h>
44 #include <qevent.h>
45 #include <qlayout.h>
46 #include <qsizepolicy.h>
47 
48 #include <stdlib.h>
49 #include <locale.h>
50 
51 #include <uim/uim-scm.h>
52 #include <uim/uim-custom.h>
53 #include "qtgettext.h"
54 
55 #define NAME_COLUMN 0
56 
57 static int uim_fd;
58 static bool customEnabled;
59 static QSocketNotifier *notifier = NULL;
60 
main(int argc,char ** argv)61 int main( int argc, char **argv )
62 {
63     setlocale(LC_ALL, "");
64     bindtextdomain(PACKAGE, LOCALEDIR);
65     textdomain(PACKAGE);
66     bind_textdomain_codeset(PACKAGE, "UTF-8"); // ensure code encoding is UTF8-
67 
68     setenv("XMODIFIERS", "@im=none", 1);
69 
70     QApplication a( argc, argv );
71 
72     UimImSwitcher switcher;
73     switcher.resize( 550, 400 );
74     switcher.setCaption( _( "uim input method switcher" ) );
75     a.setMainWidget( &switcher );
76     switcher.show();
77 
78     return a.exec();
79 }
80 
81 
UimImSwitcher(QWidget * parent,const char * name)82 UimImSwitcher::UimImSwitcher( QWidget *parent, const char *name )
83         : QDialog( parent, name )
84 {
85     /* connect to uim helper message bus */
86     uim_fd = -1;
87     checkHelperConnection();
88 
89     /* to check if another uim-im-switcher exists */
90     uim_helper_send_message( uim_fd, "im_switcher_start\n" );
91 
92     /* to load input method list */
93     uim_helper_send_message( uim_fd, "im_list_get\n" );
94 
95     uim_init();
96     customEnabled = uim_custom_enable();
97 
98     /* create GUI */
99     createGUI();
100 }
101 
~UimImSwitcher()102 UimImSwitcher::~UimImSwitcher()
103 {
104 }
105 
createGUI()106 void UimImSwitcher::createGUI()
107 {
108     /* im list view */
109     listview = new QListView( this );
110     listview->setSelectionMode( QListView::Single );
111     listview->setAllColumnsShowFocus( true );
112     listview->addColumn( _( "InputMethodName" ) );
113     listview->addColumn( _( "Language" ) );
114     listview->addColumn( _( "Description" ) );
115 
116     /* radio buttons for the switcher coverage */
117     QRadioButton *button;
118     vbGroup = new QVButtonGroup( _( "Effective coverage" ), this );
119     button = new QRadioButton( _( "whole desktop" ), vbGroup );
120     vbGroup->insert( button, ID_CHANGE_WHOLE_DESKTOP );
121     button->setChecked( TRUE ); // default is "whole desktop"
122     button = new QRadioButton( _( "current application only" ), vbGroup );
123     vbGroup->insert( button, ID_CHANGE_THIS_APPLICATION_ONLY );
124     button = new QRadioButton( _( "current text area only" ), vbGroup );
125     vbGroup->insert( button, ID_CHANGE_THIS_TEXT_AREA_ONLY );
126 
127     /* cancel & ok button */
128     okButton = new QPushButton( this );
129     okButton->setText( _( "OK" ) );
130     okButton->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
131     QObject::connect( okButton, SIGNAL( clicked() ),
132                       this, SLOT( slotChangeInputMethod() ) );
133     cancelButton = new QPushButton( this );
134     cancelButton->setText( _( "Cancel" ) );
135     cancelButton->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
136     QObject::connect( cancelButton, SIGNAL( clicked() ),
137                       qApp, SLOT( quit() ) );
138     QHBoxLayout *buttonLayout = new QHBoxLayout;
139     buttonLayout->addStretch( 0 );
140     buttonLayout->addWidget( okButton );
141     buttonLayout->addWidget( cancelButton );
142 
143     // main layout
144     QVBoxLayout *mainLayout = new QVBoxLayout( this );
145     mainLayout->setMargin( 6 );
146     mainLayout->setSpacing( 6 );
147     mainLayout->addWidget( listview );
148     mainLayout->addWidget( vbGroup );
149     mainLayout->addLayout( buttonLayout );
150 }
151 
152 
checkHelperConnection()153 void UimImSwitcher::checkHelperConnection()
154 {
155     if ( uim_fd < 0 )
156     {
157         uim_fd = uim_helper_init_client_fd( helper_disconnect_cb );
158         if ( uim_fd > 0 )
159         {
160             if ( notifier )
161                 delete notifier;
162             notifier = new QSocketNotifier( uim_fd, QSocketNotifier::Read );
163             QObject::connect( notifier, SIGNAL( activated( int ) ),
164                               this, SLOT( slotStdinActivated( int ) ) );
165         }
166     }
167 }
168 
helper_disconnect_cb()169 void UimImSwitcher::helper_disconnect_cb()
170 {
171     uim_fd = -1;
172     QObject::disconnect( notifier, SIGNAL( activated( int ) ), 0, 0 );
173 }
174 
slotChangeInputMethod()175 void UimImSwitcher::slotChangeInputMethod()
176 {
177 
178     switch ( vbGroup->selectedId() )
179     {
180     case ID_CHANGE_WHOLE_DESKTOP:
181         sendMessageImChange( "im_change_whole_desktop\n" );
182         saveDefaultIm();
183         break;
184     case ID_CHANGE_THIS_APPLICATION_ONLY:
185         sendMessageImChange( "im_change_this_application_only\n" );
186         break;
187     case ID_CHANGE_THIS_TEXT_AREA_ONLY:
188         sendMessageImChange( "im_change_this_text_area_only\n" );
189         break;
190     default:
191         break;
192     }
193 
194     qApp->quit();
195 }
196 
sendMessageImChange(const QString & change_type)197 void UimImSwitcher::sendMessageImChange( const QString &change_type )
198 {
199     QString imName = selectedImName();
200     if ( imName.isEmpty() )
201         return ;
202 
203     /* ensuring connected to message bus */
204     checkHelperConnection();
205 
206     QString msg = QString::null;
207     msg.append( change_type );
208     msg.append( imName );
209     msg.append( "\n" );
210 
211     uim_helper_send_message( uim_fd, ( const char* ) msg.utf8() );
212 }
213 
saveDefaultIm()214 void UimImSwitcher::saveDefaultIm()
215 {
216     if ( customEnabled )
217     {
218         QString imName = selectedImName();
219         if ( imName.isEmpty() )
220             return ;
221 
222         uim_scm_callf( "custom-set-value!",
223                        "yy",
224                        "custom-preserved-default-im-name",
225                        ( const char* ) imName.utf8() );
226         uim_custom_save_custom( "custom-preserved-default-im-name" );
227     }
228 }
229 
selectedImName() const230 QString UimImSwitcher::selectedImName() const
231 {
232     QListViewItem * selectedItem = listview->selectedItem();
233     if ( selectedItem )
234     {
235         return selectedItem->text( NAME_COLUMN );
236     }
237 
238     return QString::null;
239 }
240 
slotStdinActivated(int)241 void UimImSwitcher::slotStdinActivated( int /*socket*/ )
242 {
243     uim_helper_read_proc( uim_fd );
244 
245     QString msg = QString::null;
246     char *s;
247     while ( ( s = uim_helper_get_message() ) )
248     {
249         const QStringList lines = QStringList::split( "\n", QString( s ) );
250         if ( !lines.isEmpty() && !lines[ 1 ].isEmpty() && lines[ 1 ].startsWith( "charset" ) )
251         {
252             /* get charset */
253             const QString charset = QStringList::split( "=", lines[ 1 ] ) [ 1 ];
254 
255             /* convert to unicode */
256             QTextCodec *codec = QTextCodec::codecForName( charset );
257             msg = codec->toUnicode( s );
258         }
259         else
260         {
261             /* no charset */
262             msg = s;
263         }
264 
265         if ( msg.startsWith( "focus_in" ) )
266             reloadImList();
267         else if ( msg.startsWith( "im_list" ) )
268             parseHelperStrImList( msg );
269         else if ( msg.startsWith( "im_switcher_start" ) )
270             uim_helper_send_message( uim_fd,  "im_switcher_quit\n" );
271         else if ( msg.startsWith( "im_switcher_quit" ) )
272             qApp->quit();
273     }
274 }
275 
276 
parseHelperStrImList(const QString & message)277 void UimImSwitcher::parseHelperStrImList( const QString &message )
278 {
279     /* delete old items */
280     listview->clear();
281 
282     const QStringList lines = QStringList::split( "\n", message );
283     for ( unsigned int i = 2; i < lines.count(); i++ )
284     {
285         const QStringList iminfoList = QStringList::split( "\t", lines[ i ], true );
286 
287         if ( !iminfoList.isEmpty()
288                 && !iminfoList[ 0 ].isEmpty()
289 		// Language of IM with any locale is set as "".
290                 // && !iminfoList[ 1 ].isEmpty()
291                 && !iminfoList[ 2 ].isEmpty() )
292         {
293 	    QString lang, short_desc;
294 
295 	    if (iminfoList[1].isEmpty())
296 		lang = QString("-");
297 	    else
298 		lang = QString::fromUtf8(gettext(iminfoList[1].utf8()));
299 	    short_desc = QString::fromUtf8(gettext(iminfoList[2].utf8()));
300 
301             // add new item to listview
302             QListViewItem * item = new QListViewItem( listview, iminfoList[ 0 ], lang, short_desc );
303 
304             if ( !iminfoList[ 3 ].isEmpty() && QString::compare( iminfoList[ 3 ], "selected" ) == 0 )
305                 listview->setSelected( item, TRUE );
306         }
307     }
308 }
309 
reloadImList()310 void UimImSwitcher::reloadImList()
311 {
312     checkHelperConnection();
313 
314     /* send request to get im list */
315     uim_helper_send_message( uim_fd, "im_list_get\n" );
316 }
317 
318 #include "qt.moc"
319