IIIMF CommonAux UI description Draft

Target Aux

  • Palette
  • IM Configration dialog.
  • Preedit, Virtual Keyboard etc. will use pre-defined implementation.

Data type and supported renderType

type/tag: renderType:
string label, lineEdit, textArea, passwd, file, font
int spinButton, progressBar, sliderBar, color
boolean toggleButton
(in check menu group, it's checkMenuItem;
while in radio menu group, it's radioMenuItem)
command pushButton, URLLink
(in menu group, it's normal menuitem)
group menu, tab, card, folder, frame
checkGroup checkGroup(default), listBox, menu
(only contains boolean item)
radioGroup radioGroup(default), listBox, comboBox, menu
(only contains boolean item)

Composite pattern

+--------------+
| propertyItem |
+--------------+
| key/id/*     |
| label        |           +---------------+
| value        |<|-------<>| propertyGroup |
| type*        |           +---------------+
| renderType*  |           | layout        | (Horizental, Vertical)
| tooltip      |           | children      |
| icon         |           +---------------+
| state        |
| visible      |
+--------------+

Example 1: A radio group

<!-- The value of this radioGroup is the selected key. -->
<radioGroup id="radioGroup1" renderType="listBox">
  <boolean id="key1" label="key1" value="val1"/>
  <boolean id="key2" label="key2" value="val2"/>
  <boolean id="key3" label="key3" value="val3"/>
</radioGroup>

Example 2: A check group

<!-- checkGroup does not have value of itself -->
<checkGroup id="checkGroup1" renderType="checkGroup">
  <boolean id="key4" label="key4" value="val4"/>
  <boolean id="key5" label="key5" value="val5"/>
  <boolean id="key6" label="key6" value="val6"/>
</checkGroup>

Example 3: IM palette

<group id="palette" layout="Horizental">

  <boolean id="Eng_Ch" label="中英" icon="Chinese.jpg" renderType="toggleButton"/>
  <boolean id="Full_Half" label="全半角" icon="Full.jpg" renderType="toggleButton"/>

  <group id="menu1" renderType="menu>
    <group id="subMenu1" label="subMenu1" renderType="menu">
      <command id="cmd1" label="cmd1"/>
      <command id="cmd2" label="cmd2"/>

      <checkGroup id="subMenu2" label="subMenu2" renderType="menu">
        <boolean id="key7" lable="key7" value="val7"/>
        <boolean id="key8" label="key8" value="val8"/>
      </checkGroup>
    </group>

    <!-- anonymous group (i.e. label is empty) here is a flat menu group, not cascading submenu -->
    <radioGroup id="nolabel1" renderType="menu">
      <boolean id="key9" label="key9" value="val9"/>
      <boolean id="key10" label="key10" value="val10"/>
      <boolean id="key11" label="key11" value="val10"/>
    </radioGroup>

    <command id="cmd3" label="cmd3"/>
  </group>

</group>

+---+---+-------+
|E/C|F/H| Menu1 |
+---+---+-------+------+
          | subMenu1 >+------------+
          +-----------| cmd1       |
          | * key9    | cmd2       |
          | * key10   | subMenu2 >+---------+
          | * key11   +-----------| [] key7 |
          +------------+          | [] key8 |
          | cmd3       |          +---------+
          +------------+

Example 4: IM Configuration Dialog

<group id="im_config" label="IME Configuration" renderType="frame">

  <group id="tabs" renderType="tab">

    <group id="tab1" label="tab1">
      <radioGroup id="input_style" label="Input Style" renderType="comboBox">
        <boolean id="classic_style" label="Classic Style"/>
        <boolean id="converting_on_fly" label="Converting on Fly"/>
      </radioGropu>

      <radioGroup id="charset" label="Charset" renderType="comboBox">
        <boolean id="gb2312" label="GB2312"/>
        <boolean id="gbk" label="GBK"/>
      </radioGroup>
    </group>

    <group id="tab2" label="tab2">
    </group>

  </group>

</group>

How to calculate the size of a group?

void group_size (group, w, h)
{
  w = h = 0;

  for (item in group) {
    int x, y;
    if (item is group)
      group_size (group, x, y) /* recursive call */
    else
      item_size (item, x, y) /* icon size, label string size */

    if (group.layout = Horizental) {
      h = max (h, y);
      w += x;
    } else { /* default is Vertical */
      w = max (w, x);
      h += y;
    }
  }

}

How to locate the icon?

iiimp://iiim_server_hostname_or_IP/le_name/the/relative/path/under/le
e.g., iiimp://localhost/cle/images/full.png

Several thoughts about im list in gimlet.

Today, in iiimf's irc, we discussed about the im list in gimlet.

(12:56:02) federic: Actually the 'Input Method List' of Input method attribute in IIIM spec refers to language available, i am afraid that IIIM protocol should be extended in order to list all IMs for langs, otherwise, all IMs have to be reimplemented as LEs to achive this goal. UNIT LE exports a lot of input method, can we leverage?
(12:59:18) federic: Hideki is right, gimlet should directly use iiimp/iiimcf to get more information, but the question is IIIMCF API should export more useful information in order to either gimlet or other IIIM*CF (client) to leverage and communicate to server.
... ...
(13:15:57) yongx40: While IIIMF server does not manage "Input Method" (the concept in CLE) in IIIM spec. Only if IM is a managed unit in server, IIIMF server could provide the information in IM level, and gimlet could tell how to organize the information. But if the IM interface is defined and officially published, we could find LE interface will be useless. So, I think LE could be consider as a IM, and with the multiple layer API (actually wrappers of LE interface), one LE could
(13:25:12) phill: Yes, if we distinguish LE and IM in the future, then there would be lot of other support thing to do, such as the creation of IM is not same as creation of LE,
(13:25:31) phill: and unload IM would be depend on LE
(13:26:00) phill: and unload LE would cause unload all its IM, etc
(13:27:50) phill: We really should make a LE a IM. Yet one package (LE) conatians multiple IM has it advantege
(13:30:08) phill: , many code could be shared. So it is reasonable to leave the LE's concept, but we could just treat it as a .so file
(13:30:50) phill: and make clear what is IM in the .so file. i.e, we have to distinguish LE and IM.
(13:32:03) phill: Currently, in LEBase class, get_langlist() and get_imdesclist() is seperated. Maybe we could try to make a langlist to every IM in the LE.
(13:35:54) yongsun: In concept, what's a IM has to do to declare itself, it should expose it's IM interface, and declare what language (or locale?) it supports, if it will use common AUX or its own custom AUX ... You could find all the stuffs are just what the LE is currently doing. I totally agree with phill, that one LE <-> one IM.
... ...
(13:39:55) juhp: as to LE vs IM I don't have a strong idea about that - I thought they were the same thing, but I see what you mean

IIIM server code reading note (part 2)


* Singleton -- the most commonly used pattern.
There are some singleton classes in IIIMSF such as IMLog, IMSignal
etc, and basicly follows the scheme below:

class Singleton
{
    static Singleton * _pInstance;

    Singleton ();
    vitual ~Singleton ();

public:
    static Singleton * get_instance ();

    static Singleton * construct ();
    static void * cleanup ();
};

It's not correct! At least, we should declare the copy constructor as
private like that:

private:
    Singleton (const Singleton& other);


* State pattern applied in IIIM
First, let's to see how the classic state pattern looks like.

+------------+              +-----------+
|   Context  |<>------------|   State   |
+------------+              +-----------+
| Request () |              | Handle () |
+------------+              +-----------+
                                  A
                                  |    
                    +-------------+----- ... ...
                    |             |
              +-----------+    +-----------+
              |  StateA   |    |  StateB   |
              +-----------+    +-----------+  (concrete states)
              | Handle () |    | Handle () |
              +-----------+    +-----------+

The member functions of concrete states, take the reference of
"Context" as one argument. Concrete states use this reference to call
it's change_state () function. Every concrete state knows what will be
the next state after a certain operation. And state object is usually
a singleton.

In IIIM, ICState is the "Context", IIIMP_ICState is the parent "State"
class, while IIIMP_ICState_REQUESTED, and IIIMP_ICState_WAITING
classes are the concrete state classes. In case of IMState, it's the
same.

IIIM server code reading note (part 1)

* UML static structure:
+----------+
| <<if>>   |---o request_accept ()
| IMAccept |---o request_connect ()
+----------+                          +--------+
     A                    +---------|>| IMAuth |
     |                    |           +--------+
     |                    |
 +-------+           +-----------+       +--------+
 | IMSvr |<>----+----| IMUserMgr |<>-----| IMUser |
 +-------+      |    +-----------+ 1   * +--------+
    |           |
    .           |    +-------+       +--------+        +--------+
    |           +----| LEMgr |<>-----| LEBase |-.-.-.->| libiml |
    .           |    +-------+ 1   * +--------+        +--------+
    |           |
    .           |    +------------+       +---------+       +---------+
    |           +----| IMProtocol |<>-----| IMState |<>-----| ICState |
    V                +------------+ 1   * +---------+ 1   * +---------+
+-------------+               A                 A
|<<singleton>>|---o start ()  |                 |
| IMScheduler |---o stop ()   |                 +-------------------------+
+-------------+               +--+                                        |
       A                         |                                        |
       |                         |                                        |
+------------------+          +--------------+       +----------------+   |
| IMScheduler_MTPC |-.-.-.-.->| IIIMProtocol |<>-----| IMSocketListen |   |
+------------------+          +--------------+ 1   1 +----------------+   |
        \                                <>                               |
         \                                |                               |
  +-------------------------------+       +----------map {IIMPTrans*, IIIMP_IMState}
 / Make a thread per connection.  |
| Invoke thread func IMScheduler_ |
| MTPC_thread_entry ()            |
+---------------------------------+
                                          +-------+
                               +-.-.-.-.->| LEMgr |
                               |          +-------+
+-----------+                  |
| <<if>>    |       +--------------+      +--------+
| IMHandler |<|-----| IMConnection |----->| IMUser |
+-----------+       +--------------+ 1  1 +--------+
                      |1   |1  |1            | 1
                      |    |   |             |
                      |    |   |             V *
                      |    |   |         +-----------+
                      |    |   +-------->| IMDesktop |
                      |    |           1 +-----------+
                      |    |
                      |    |             +-------+
                      |    +-------------| IMSvr |
    <<create>>        |                1 +-------+
 +--------------------+
 |
 |      +---------+       +-----------------+
 |      | ICState |<>-----| IMLExec_ICState |
 |      +---------+ 1   1 +-----------------+
 |          |
 |          . <>
 |          |
 V *        V
+---------------+
|   <<if>>      |----o send_event ()
|   ICHandler   |----o ...
+---------------+
      A
      |
+----------------+       +--------------+
| IMInputContext |<----->| IMConnection |
+----------------+ *   1 +--------------+
    |    | 1
    .    |               +-----------+        +-----------------------+
    |    +-------------->| LEContext |-------/ One ic may corresponds |
    .                  * +-----------+      | to several LE context   |
    |                                       +-------------------------+
    .          +-------+
    +-.-.-.-.->| LEMgr | Note: LEMgr should be a singleton?
    .          +-------+
    |
    .          +---------+
    |          | <<if>>  |       +-----------------+
    +-.-.-.-.->| IMLExec |<|-----| IMLExec_ICState |
               +---------+       +-----------------+

+------------+       +---------------+       +------------------+
| IIIMPTrans |<>-----| IMSocketTrans |<|-----| IMSocketTransTLS |
+------------+ 1   1 +---------------+       +------------------+ 

* Before connected: 1. IIIMProtocol::accept ()    IIIMProtocol::restart (), initialize the private member    IMSocketListen *pimsl.    use pimsl to accetp a client socket, and return an IMSocketTrans    instance, if passes the authorization, create an IIIMP_IMState    object and return. 2. IMScheduler_MTPC::start ()    create a pthread for this client's connection 3. IIIMProtocol::receive_and_dispatch ()    pthread loop IMScheduler_MTPC_thread_entry call this function to    handle client requests.    IIIMPTrans::receive () to receive an IIIMP_message, then call 4. 4. IIIMP_IMState::dispatch ()    if ic_id is invalid, call IMState::deliver (). Since the connection    has not been established, the IMState::pproc_state should be null,    so it then call IIIMP_IMState::message_proc (). 5. IIIMP_IMState::message_proc()    in case of IM_CONNECT message, get userinfo from request, call    IMSvr::request_connect (). This IMAccetp interface tries to    authorize this user, if successed, create an IMConnection and    returned as IMHandler*. Change imstate to IIIMP_IMState_Identified. 6. IIIMP_IMState_Identified::message_proc ()    If connected clients request an operation for im instead of ic,    (e.g., IM_CREATEIC, IM_DISCONNECT etc), this method will be    invoked, while in this case the ic_id must be invalid.    in case of IM_CREATEIC, call IMConnection::createic () to create an    IMInputContext instance and returned as ICHandler*. call    IIIMP_ICState::create(ic_id, this, pich) to create an icstate    instance, add it to icmap, then send a reply to client. * After connected: 1. IIIMProtocol::receive_and_dispatch () 2. IMState::dispatch ()    if ic_id is valid, from the IMState::IMShared::ICStateMap, look up    the matched ICState, then call ICState::deliver () to handle the    client's request or reply. 3. ICState::deliver ()    get current ICState, and then call its message_proc ().    IIIMP_ICState::start_request() will turn the current state to    IIIMP_ICState_REQUESTED. While IIIMP_ICState_REQUESTED::finish ()    will restore to previous state, and destory itself.    if current state is requested, its message_proc () will return    false, since it can not handle any request at that time.    if current state is waiting, its message_proc () will see whether    the incoming message is what it's waiting for. if not, it calls    IIIMP_ICState::message_proc () to handle this message, so in    waiting state will not block the normal request. or, it retrieves    and restore to the previous back-up request state, and then call its    dealing method. this method in trun calls get_imlexec()->execute(),
   this method execute all pending iml instructions.

   IIIMP_ICState_REQUESTED::wait ()
   IIIMP_ICState_REQUESTED::wait_aux ()
   These two methods can change the current icstate from "requested"
   to "waiting" (for the replies from client).

   IIIMP_ICState::message_proc()
   in case of IM_SETICFOCUS:
     start_request()->toggle_icfocus () [REQUESTED::toggle_focus ()]
       get_ichandler()->toggle_focus () [IMInputContext::toggle_focus ()]
         get_current_lecontext()->toggle_focus () [LEContext::toggle_focus ()]
           if_SetSCFocus () [SunIM.c: if_SetSCFocus ()]
             s->If->ifm->if_SetSCFocus () [specified LE's setfocus function]
       dealing ()
         execute remaining IML instructions (pushed by LEs)
         finish (), restore to previous state

* How does LE invoke server?
LE can not directly call server functions to send reply/messages to
client. LE calls iml methods (defined at iiimsf/lib/iml/SunIMSubr.c)
to create iml instructions, and append them to the operation list
(via iml_execute(), virtually is iml_execute_iml_wrapper() in LE.cpp).

As we mentioned early, IIIMP_ICState_REQUESTED::dealing() calls
IMLExec_ICState::execute() to process the pending instructions.

IMLExec_ICState::execute_opcode(), in turn calls imli_xxx methods,
these methods then call IIIMP_ICState_REQUESTED member functions which
actually send messages to client.

iml_make_commit_inst()/iml_execute()
               |
               | through an async flow
               V
in IMLExec_ICState::execute_opcode()
  case IMM_COMMIT:
    IMLExec_ICState::imli_commit ()
      IIIMP_ICState_REQUESTED::commit_string ()
        new an iiimp message
        send ()
          IIIMP_ICState::send ()
            IIIMP_IMState::send ()
              IIIMPTrans::send ()
                iiimf_stream_send ()
                  IIIMPTrans_write ()
                    IMSocketTrans::send () or
                    IMSocketTransTLS::send ()

* NameSpace I/O functions:
NameSpace I/O functions are passively invoked by LEs.

in each nsio function, there is a nested event dispatching loop.
Take open_ns () as an example:

   send_message = iiimp_open_ns_new (...);
   xims->send (send_message, true);

   for (;;) {
       xims->get_iiimptrans ()->receive ();
       if (opcode == IM_OPEN_NS_REPLY) {
           break;
       } else {
           xims->dispatch (...);
       }
   }

this loop is very simliar with IIIMProtocol::receive_and_dispatch.

* Misc:
ICState::send_avail_p ()
  IIIMP_ICState::state_send_available_p () { return false; }
  IIIMP_ICState_REQUESTED::state_send_available_p () { return true; }
  IIIMP_ICState_WAITING::state_send_available_p () { return false; }

-----------ICState::deliver ()--------------
if (!p->message_proc(message)) return false;
if (send_avail_p() && get_imlexec()) {
    result = get_imlexec()->execute();
}
--------------------------------------------

only when the icstate in requested, ICState::deliver () will try to
execute the buffered iml instructions. Because only in this state, the
server side (LEs) need to respond to client, and then LE may push iml
instructions in the queue.

Basicly, every branch in IIIMP_ICState::message_proc () will call
IIIMP_ICState_REQUESTED::dealing (), which actually calls
get_imlexec()->execute(). Is it a dup invoke in ICState::deliver ()?