1/*
2 * Copyright (c) 2023-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16export namespace TreeView {
17  const IMAGE_NODE_HEIGHT: number = 24
18  const IMAGE_NODE_WIDTH: number = 24
19  const ITEM_WIDTH: number = 0
20  const ITEM_HEIGHT: number = 44
21  const ITEM_HEIGHT_INPUT: number = 32
22  const BORDER_WIDTH_HAS: number = 2
23  const BORDER_WIDTH_NONE: number = 0
24  const NODE_HEIGHT: number = 44
25  const LIST_ITEM_HEIGHT_NONE: number = 0
26  const LIST_ITEM_HEIGHT: number = 48
27  const SHADOW_OFFSETY: number = 10
28  const FLAG_NUMBER: number = 2
29  const DRAG_OPACITY: number = 0.4
30  const DRAG_OPACITY_NONE: number = 1
31  const FLAG_LINE_HEIGHT: string = '1.5vp'
32  const X_OFF_SET: string = '0vp'
33  const Y_OFF_SET: string = '2.75vp'
34  const Y_BOTTOM_OFF_SET: string = '-1.25vp'
35  const Y_BASE_PLATE_OFF_SET: string = '1.5vp'
36  const COLOR_SELECT: string = '#1A0A59F7'
37  const COLOR_IMAGE_ROW: string = '#00000000'
38  const COLOR_IMAGE_EDIT: string = '#FFFFFF'
39  const SHADOW_COLOR: string = '#00001E'
40  const GRAG_POP_UP_HEIGHT: string = '48'
41  const LEFT_PADDING: string = '8vp'
42  const RIGHT_PADDING: string = '8vp'
43  const FLOOR_MIN_WIDTH: string = '128vp'
44  const FLOOR_MAX_WIDTH: string = '208vp'
45  const TEXT_MIN_WIDTH: string = '80vp'
46  const TEXT_MAX_WIDTH: string = '160vp'
47  const MIN_WIDTH: string = '112vp'
48  const MAX_WIDTH: string = '192vp'
49
50  export class TreeListener {
51    _events = []
52
53    constructor() {
54    }
55
56    /*
57   * Event registration and processing.
58   *
59   * The event will not be destroyed after being processed.
60   *
61   * @param type Registered Events.
62   * @param callback Event callback.
63   * @since 10
64   */
65    public on(type: TreeListenType, callback: (callbackParam: CallbackParam) => void) {
66      if (Array.isArray(type)) {
67        for (let i = 0, l = type.length; i < l; i++) {
68          this.on(type[i], callback)
69        }
70      } else {
71        (this._events[type] || (this._events[type] = [])).push(callback)
72      }
73    }
74
75    /*
76   * Event registration and processing.
77   *
78   * After the event is processed once, it will be destroyed.
79   *
80   * @param type Registered Events.
81   * @param callback Event callback.
82   * @since 10
83   */
84    public once(type: TreeListenType, callback: (callbackParam: CallbackParam) => void) {
85      let _self = this;
86      function handler() {
87        _self.off(type, handler);
88        callback.apply(null, [type, callback]);
89      }
90
91      handler.callback = callback;
92      this.on(type, handler);
93    }
94
95    /*
96   * Destroy event.
97   *
98   * @param type Registered Events.
99   * @param callback Event callback.
100   * @since 10
101   */
102    public off(type: TreeListenType, callback: (callbackParam: CallbackParam) => void) {
103      if (type == null) {
104        this._events = [];
105      }
106      if (Array.isArray(type)) {
107        for (let i = 0, l = type.length; i < l; i++) {
108          this.off(type[i], callback)
109        }
110      }
111      const cbs = this._events[type];
112      if (!cbs) {
113        return;
114      }
115      if (callback == null) {
116        this._events[type] = null
117      }
118      let cb, i = cbs.length
119      while (i--) {
120        cb = cbs[i]
121        if (cb === callback || cb.callback === callback) {
122          cbs.splice(i, 1)
123          break
124        }
125      }
126    }
127
128    /*
129   * Triggers all callbacks of an event with parameters.
130   *
131   * @param event Registered Events.
132   * @param argument Parameters returned by the callback event.
133   * @since 10
134   */
135    public emit(event, argument: any[]) {
136      let _self = this
137      if (!this._events[event]) {
138        return
139      }
140      let cbs = [...this._events[event]];
141      if (cbs) {
142        for (let i = 0, l = cbs.length; i < l; i++) {
143          try {
144            cbs[i].apply(_self,argument)
145          } catch (e) {
146            new Error(e)
147          }
148        }
149      }
150    }
151  }
152
153  /*
154 * TreeListenType listen type.
155 *
156 * @since 10
157 */
158  export enum TreeListenType {
159    NODE_ADD = "NodeAdd",
160    NODE_DELETE = "NodeDelete",
161    NODE_MODIFY = "NodeModify",
162    NODE_MOVE = "NodeMove",
163    NODE_CLICK = 'NodeClick',
164  }
165
166  /*
167 * TreeListenerManager.
168 *
169 * @since 10
170 */
171  export class TreeListenerManager {
172    static readonly APP_KEY_EVENT_BUS = "app_key_event_bus";
173    private appEventBus: TreeListener;
174    private constructor() {
175      this.appEventBus = new TreeListener();
176    }
177
178    /*
179   * Obtains the EventBusManager object.
180   *
181   * @since 10
182   */
183    public static getInstance(): TreeListenerManager {
184      if (AppStorage.Get(this.APP_KEY_EVENT_BUS) == null) {
185        AppStorage.SetOrCreate(this.APP_KEY_EVENT_BUS, new TreeListenerManager())
186      }
187      return AppStorage.Get(this.APP_KEY_EVENT_BUS);
188    }
189
190    /*
191   * Obtains the EventBus object.
192   *
193   * @since 10
194   */
195    public getTreeListener(): TreeListener {
196      return this.appEventBus;
197    }
198  }
199
200  class BasicDataSource implements IDataSource {
201    private listeners: DataChangeListener[] = []
202
203    public totalCount(): number {
204      return 0
205    }
206    public getData(index: number): any {
207      return undefined
208    }
209
210    registerDataChangeListener(listener: DataChangeListener): void {
211      if (this.listeners.indexOf(listener) < 0) {
212        this.listeners.push(listener)
213      }
214    }
215    unregisterDataChangeListener(listener: DataChangeListener): void {
216      const pos = this.listeners.indexOf(listener);
217      if (pos >= 0) {
218        this.listeners.splice(pos, 1)
219      }
220    }
221
222    notifyDataReload(): void {
223      this.listeners.forEach(listener => {
224        listener.onDataReloaded()
225      })
226    }
227    notifyDataAdd(index: number): void {
228      this.listeners.forEach(listener => {
229        listener.onDataAdd(index)
230      })
231    }
232    notifyDataChange(index: number): void {
233      this.listeners.forEach(listener => {
234        listener.onDataChange(index)
235      })
236    }
237    notifyDataDelete(index: number): void {
238      this.listeners.forEach(listener => {
239        listener.onDataDelete(index)
240      })
241    }
242    notifyDataMove(from: number, to: number): void {
243      this.listeners.forEach(listener => {
244        listener.onDataMove(from, to)
245      })
246    }
247  }
248
249  export enum Event {
250    TOUCH_DOWN = 0,
251    TOUCH_UP = 1,
252    HOVER = 3,
253    HOVER_OVER = 4,
254    FOCUS = 5,
255    BLUR = 6,
256    MOUSE_BUTTON_RIGHT = 7,
257    DRAG = 8
258  }
259
260  export enum MenuOperation {
261    ADD_NODE = 0,
262    REMOVE_NODE = 1,
263    MODIFY_NODE = 2,
264    COMMIT_NODE = 3
265  }
266
267  export enum PopUpType {
268    HINTS = 0,
269    WARNINGS = 1
270  }
271
272  export enum InputError {
273    INVALID_ERROR = 0,
274    LENGTH_ERROR = 1,
275    NONE = 2
276  }
277
278  export enum Flag {
279    DOWN_FLAG = 0,
280    UP_FLAG = 1,
281    NONE = 2
282  }
283
284  export class NodeItem {
285    private nodeItem: { imageNode?: ImageNode,
286    mainTitleNode?: MainTitleNode,
287    imageCollapse?: ImageNode};
288    private childNodeInfo: { isHasChildNode: boolean, childNum: number, allChildNum: number };
289    menu: () => void;
290    nodeLevel: number;
291    parentNodeId: number;
292    currentNodeId: number;
293    children: Array<NodeItem>;
294    data: { isFolder?: boolean,
295    icon?: Resource,
296    selectedIcon?: Resource,
297    editIcon?: Resource,
298    primaryTitle?: string,
299    menu?: () => void,
300    objectCount?: number | string }
301
302    constructor(data: { isFolder?: boolean,
303    icon?: Resource,
304    selectedIcon?: Resource,
305    editIcon?: Resource,
306    primaryTitle?: string,
307    menu?: () => void,
308    objectCount?: number | string }) {
309      this.data = data;
310      this.nodeLevel = -1;
311      this.parentNodeId = -1;
312      this.nodeItem = { imageNode: null, mainTitleNode: null, imageCollapse: null };
313      this.childNodeInfo = { isHasChildNode: false, childNum: 0, allChildNum: 0 };
314      this.menu = data.menu;
315      if (data.icon) {
316        this.nodeItem.imageNode = new ImageNode(data.icon, data.selectedIcon, data.editIcon,
317          $r('sys.float.ohos_id_alpha_content_fourth'),
318          IMAGE_NODE_HEIGHT,
319          IMAGE_NODE_WIDTH);
320      }
321      if (data.primaryTitle) {
322        this.nodeItem.mainTitleNode = new MainTitleNode(data.primaryTitle);
323      }
324      this.children = [];
325    }
326
327    addImageCollapse(isHasChildNode: boolean) {
328      if (isHasChildNode) {
329        this.nodeItem.imageCollapse = new ImageNode($r('sys.media.ohos_ic_public_arrow_right'), null, null,
330          $r('sys.float.ohos_id_alpha_content_tertiary'),
331          IMAGE_NODE_HEIGHT,
332          IMAGE_NODE_WIDTH);
333        this.nodeItem.imageCollapse.itemRightMargin = ($r('sys.float.ohos_id_text_paragraph_margin_xs'));
334      } else {
335        this.nodeItem.imageCollapse = null;
336      }
337    }
338
339    getNodeItem() {
340      return this.nodeItem;
341    }
342
343    getChildNodeInfo() {
344      return this.childNodeInfo;
345    }
346
347    getMenu(): () => void {
348      return this.menu;
349    }
350
351    getCurrentNodeId() {
352      return this.currentNodeId;
353    }
354
355    getIsFolder() {
356      return this.data.isFolder;
357    }
358  }
359
360  class NodeBaseInfo {
361    public rightMargin: Resource | number;
362    private width: number;
363    private height: number;
364    constructor() {
365    }
366
367    set itemWidth(width: number) {
368      this.width = width;
369    }
370
371    get itemWidth(): number {
372      return this.width;
373    }
374
375    set itemHeight(height: number) {
376      this.height = height;
377    }
378
379    get itemHeight(): number {
380      return this.height;
381    }
382
383    set itemRightMargin(rightMargin: Resource | number) {
384      this.rightMargin = rightMargin;
385    }
386
387    get itemRightMargin() {
388      return this.rightMargin;
389    }
390  }
391
392  export enum NodeStatus {
393    Expand = 0,
394    Collapse
395  }
396
397  export enum InteractionStatus {
398    Normal = 0,
399    Selected,
400    Edit,
401    FinishEdit,
402    DragInsert,
403    FinishDragInsert
404  }
405
406  export class ImageNode extends NodeBaseInfo {
407    private imageSource: Resource;
408    private imageNormalSource: Resource;
409    private imageSelectedSource: Resource;
410    private imageEditSource: Resource;
411    private imageOpacity: Resource;
412    private currentInteractionStatus: InteractionStatus;
413    private imageCollapseSource: Resource;
414    private imageCollapseDownSource: Resource;
415    private isImageCollapse: boolean;
416    private imageCollapseRightSource: Resource;
417    constructor(imageSource: Resource, itemSelectedIcon: Resource, itemEditIcon: Resource,
418                imageOpacity: Resource, itemWidth: number, itemHeight: number) {
419      super();
420      this.rightMargin = $r('sys.float.ohos_id_elements_margin_horizontal_m');
421      this.imageSource = imageSource;
422      this.imageNormalSource = imageSource;
423      if (itemSelectedIcon != null) {
424        this.imageSelectedSource = itemSelectedIcon;
425      } else {
426        this.imageSelectedSource = this.imageNormalSource;
427      }
428      if (itemEditIcon != null) {
429        this.imageEditSource = itemEditIcon;
430      } else {
431        this.imageEditSource = this.imageNormalSource;
432      }
433      this.imageOpacity = imageOpacity;
434      this.itemWidth = itemWidth;
435      this.itemHeight = itemHeight;
436      this.imageCollapseSource = imageSource;
437      this.imageCollapseDownSource = $r('sys.media.ohos_ic_public_arrow_down');
438      this.imageCollapseRightSource = $r('sys.media.ohos_ic_public_arrow_right');
439      this.isImageCollapse = true;
440    }
441
442    get source() {
443      return this.imageSource;
444    }
445
446    get normalSource() {
447      return this.imageNormalSource;
448    }
449
450    get selectedSource() {
451      return this.imageSelectedSource;
452    }
453
454    get editSource() {
455      return this.imageEditSource;
456    }
457
458    get opacity() {
459      return this.imageOpacity;
460    }
461
462    get noOpacity() {
463      return 1;
464    }
465
466    get collapseSource() {
467      return this.imageCollapseSource;
468    }
469
470    get isCollapse() {
471      return this.isImageCollapse;
472    }
473
474    changeImageCollapseSource(nodeStatus: NodeStatus) {
475      if (nodeStatus == NodeStatus.Expand) {
476        this.imageCollapseSource = this.imageCollapseDownSource;
477      } else if (nodeStatus == NodeStatus.Collapse) {
478        this.imageCollapseSource = this.imageCollapseRightSource;
479      }
480    }
481
482    setImageCollapseSource(interactionStatus: InteractionStatus, nodeStatus: NodeStatus) {
483      if (interactionStatus === InteractionStatus.Edit || interactionStatus === InteractionStatus.DragInsert) {
484        this.imageCollapseDownSource = $r('sys.media.ohos_ic_public_arrow_down');
485        this.imageCollapseRightSource = $r('sys.media.ohos_ic_public_arrow_right');
486        this.isImageCollapse = false;
487      } else if (interactionStatus === InteractionStatus.FinishEdit ||
488        interactionStatus === InteractionStatus.FinishDragInsert) {
489        this.imageCollapseDownSource = $r('sys.media.ohos_ic_public_arrow_down');
490        this.imageCollapseRightSource = $r('sys.media.ohos_ic_public_arrow_right');
491        this.isImageCollapse = true;
492      }
493      this.imageCollapseSource = (nodeStatus == NodeStatus.Collapse) ?
494      this.imageCollapseRightSource : this.imageCollapseDownSource;
495    }
496
497    setImageSource(interactionStatus: InteractionStatus) {
498      switch (interactionStatus) {
499        case InteractionStatus.Normal:
500          this.imageSource = this.imageNormalSource;
501          this.currentInteractionStatus = interactionStatus;
502          break;
503        case InteractionStatus.Selected:
504          if (this.currentInteractionStatus !== InteractionStatus.Edit) {
505            this.imageSource = this.imageSelectedSource;
506            this.currentInteractionStatus = interactionStatus;
507          }
508          break;
509        case InteractionStatus.Edit:
510          this.imageSource = this.imageEditSource;
511          this.currentInteractionStatus = interactionStatus;
512          break;
513        case InteractionStatus.FinishEdit:
514          this.imageSource = this.imageSelectedSource;
515          this.currentInteractionStatus = interactionStatus;
516          break;
517        case InteractionStatus.DragInsert:
518          this.imageSource = this.imageEditSource;
519          this.currentInteractionStatus = interactionStatus;
520          break;
521        case InteractionStatus.FinishDragInsert:
522          this.imageSource = this.imageNormalSource;
523          this.currentInteractionStatus = interactionStatus;
524          break;
525        default:
526          break;
527      }
528    }
529  }
530
531  export class MainTitleNode extends NodeBaseInfo {
532    private mainTitleName: string;
533    private mainTitleSetting: { fontColor: Resource, fontSize: Resource, fontWeight: FontWeight }
534    private showPopUpTimeout: number;
535    constructor(mainTitleName: string) {
536      super();
537      this.mainTitleName = mainTitleName;
538      this.itemWidth = ITEM_WIDTH;
539      this.itemHeight = ITEM_HEIGHT;
540      this.rightMargin = $r('sys.float.ohos_id_text_paragraph_margin_xs');
541      this.mainTitleSetting = { fontColor: $r('sys.color.ohos_id_color_primary'),
542        fontSize: $r('sys.float.ohos_id_text_size_body1'),
543        fontWeight: FontWeight.Normal };
544      this.showPopUpTimeout = 0;
545    }
546    setMainTitleSelected(isSelected: boolean): void {
547      if (isSelected) {
548        this.mainTitleSetting = { fontColor: $r('sys.color.ohos_id_color_text_primary_activated'),
549          fontSize: $r('sys.float.ohos_id_text_size_body1'),
550          fontWeight: FontWeight.Regular };
551      } else {
552        this.mainTitleSetting = { fontColor: $r('sys.color.ohos_id_color_primary'),
553          fontSize: $r('sys.float.ohos_id_text_size_body1'),
554          fontWeight: FontWeight.Normal };
555      }
556    }
557    set title(text: string) {
558      this.mainTitleName = text;
559    }
560    get title(): string {
561      return this.mainTitleName;
562    }
563
564    set popUpTimeout(showPopUpTimeout: number) {
565      this.showPopUpTimeout = showPopUpTimeout;
566    }
567
568    get popUpTimeout() {
569      return this.showPopUpTimeout;
570    }
571
572    get color(): Resource {
573      return this.mainTitleSetting.fontColor;
574    }
575
576
577    get size(): Resource {
578      return this.mainTitleSetting.fontSize;
579    }
580
581    get weight(): FontWeight {
582      return this.mainTitleSetting.fontWeight;
583    }
584
585    setMainTitleHighLight(isHighLight: boolean): void {
586      if (isHighLight) {
587        this.mainTitleSetting = { fontColor: $r('sys.color.ohos_id_color_primary_contrary'),
588          fontSize: $r('sys.float.ohos_id_text_size_body1'),
589          fontWeight: FontWeight.Regular };
590      } else {
591        this.mainTitleSetting = { fontColor: $r('sys.color.ohos_id_color_primary'),
592          fontSize: $r('sys.float.ohos_id_text_size_body1'),
593          fontWeight: FontWeight.Normal };
594      }
595    }
596
597  }
598
599  export class InputText extends NodeBaseInfo {
600    private inputTextSetting: { fontColor: Resource, fontSize: Resource, fontWeight: FontWeight }
601    private status: { normal: Resource, hover: Resource, press: Resource };
602    private statusColor: Resource = $r('sys.color.ohos_id_color_background');
603    private editItemColor: Resource = $r('sys.color.ohos_id_color_emphasize');
604    private radius: Resource = $r('sys.float.ohos_id_corner_radius_default_xs')
605    constructor() {
606      super();
607      this.itemWidth = ITEM_WIDTH;
608      this.itemHeight = ITEM_HEIGHT_INPUT;
609      this.rightMargin = $r('sys.float.ohos_id_text_paragraph_margin_xs');
610      this.inputTextSetting = {
611        fontColor: $r('sys.color.ohos_id_color_text_primary'),
612        fontSize: $r('sys.float.ohos_id_text_size_body1'),
613        fontWeight: FontWeight.Normal };
614    }
615
616    get color(): Resource {
617      return this.inputTextSetting.fontColor;
618    }
619
620    get size(): Resource {
621      return this.inputTextSetting.fontSize;
622    }
623
624    get weight(): FontWeight {
625      return this.inputTextSetting.fontWeight;
626    }
627
628    get borderRadius(): Resource {
629      return this.radius;
630    }
631
632    get backgroundColor() {
633      return this.statusColor;
634    }
635
636    get editColor() {
637      return this.editItemColor;
638    }
639
640    get textInputStatusColor() {
641      return this.status;
642    }
643  }
644
645  export class NodeInfo {
646    private childNodeInfo: { isHasChildNode: boolean, childNum: number, allChildNum: number };
647    private parentNodeId: number;
648    private currentNodeId: number;
649    private nodeHeight: Resource | number;
650    private nodeLevel: number;
651    private nodeItem: { imageNode?: ImageNode,
652    inputText: InputText,
653    mainTitleNode?: MainTitleNode,
654    imageCollapse?: ImageNode };
655    private nodeLeftPadding: number;
656    private nodeColor: Resource | string;
657    private nodeIsShow: boolean;
658    private status: { normal: Resource, hover: Resource, press: Resource, selected: string, highLight: Resource };
659    private nodeBorder: { borderWidth: Resource | number, borderColor: Resource, borderRadius: Resource };
660    private popUpInfo: { popUpIsShow: boolean,
661    popUpEnableArrow: boolean,
662    popUpColor: Resource,
663    popUpText: string | Resource,
664    popUpTextColor: Resource};
665    private listItemHeight: number;
666    private menu: () => void;
667    private isShowTitle: boolean;
668    private isShowInputText: boolean;
669    private isSelected: boolean;
670    readonly borderWidth: {has: Resource | number, none: Resource | number } =
671      {has: BORDER_WIDTH_HAS/* 2vp */, none: BORDER_WIDTH_NONE/* 0vp */}
672
673    /* parameter of the drag event.*/
674    private nodeParam: {
675      isFolder?: boolean,
676      icon?: Resource,
677      selectedIcon?: Resource,
678      editIcon?: Resource,
679      primaryTitle?: string,
680      menu?: () => void,
681      secondaryTitle?: number | string
682    };
683    private node: NodeItem;
684    private canShowFlagLine: boolean = false;
685    private isOverBorder: boolean = false;
686    private canShowBottomFlagLine: boolean = false;
687    private isHighLight: boolean = false;
688    private flagLineLeftMargin: number;
689    private isModify: boolean = false;
690
691    constructor(node: NodeItem) {
692      this.childNodeInfo = node.getChildNodeInfo();
693      this.nodeItem = { imageNode: null, inputText: null, mainTitleNode: null, imageCollapse: null };
694      this.popUpInfo = { popUpIsShow: false,
695        popUpEnableArrow: false,
696        popUpColor: null,
697        popUpText: '',
698        popUpTextColor: null };
699      this.nodeItem.imageNode = node.getNodeItem().imageNode;
700      this.nodeItem.inputText = new InputText();
701      this.nodeItem.mainTitleNode = node.getNodeItem().mainTitleNode;
702      this.nodeItem.imageCollapse = node.getNodeItem().imageCollapse;
703      this.menu = node.menu;
704      this.parentNodeId = node.parentNodeId;
705      this.currentNodeId = node.currentNodeId;
706      this.nodeHeight = NODE_HEIGHT;
707      this.nodeLevel = node.nodeLevel;
708      this.nodeLeftPadding = node.nodeLevel * 12 + 8; // calculate left padding
709      this.nodeColor = $r('sys.color.ohos_id_color_background');
710      this.nodeIsShow = (this.nodeLevel > 0) ? false : true;
711      this.listItemHeight =  (this.nodeLevel > 0) ? LIST_ITEM_HEIGHT_NONE : LIST_ITEM_HEIGHT;
712      this.isShowTitle = true;
713      this.isShowInputText = false;
714      this.isSelected = false;
715      this.status = { normal: $r('sys.color.ohos_id_color_background_transparent'),
716        hover: $r('sys.color.ohos_id_color_hover'),
717        press: $r('sys.color.ohos_id_color_click_effect'),
718        selected: COLOR_SELECT,
719        highLight: $r('sys.color.ohos_id_color_activated')
720      };
721      this.nodeBorder = { borderWidth: BORDER_WIDTH_NONE,
722        borderColor: $r('sys.color.ohos_id_color_focused_outline'),
723        borderRadius: $r('sys.float.ohos_id_corner_radius_clicked')
724      };
725      this.flagLineLeftMargin = node.nodeLevel * 12 + 8;
726      this.node = node;
727      this.nodeParam = node.data;
728    }
729
730    getPopUpInfo() {
731      return this.popUpInfo;
732    }
733
734    setPopUpIsShow(isShow: boolean) {
735      this.popUpInfo.popUpIsShow = isShow;
736    }
737
738    setPopUpEnableArrow(popUpEnableArrow: boolean) {
739      this.popUpInfo.popUpEnableArrow = popUpEnableArrow;
740    }
741
742    setPopUpColor(color: Resource) {
743      this.popUpInfo.popUpColor = color;
744    }
745
746    setPopUpText(text: string | Resource) {
747      this.popUpInfo.popUpText = text;
748    }
749
750    setPopUpTextColor(popUpTextColor: Resource) {
751      this.popUpInfo.popUpTextColor = popUpTextColor;
752    }
753
754    getIsShowTitle() {
755      return this.isShowTitle;
756    }
757
758    getIsShowInputText() {
759      return this.isShowInputText;
760    }
761
762    setTitleAndInputTextStatus(isModify: boolean) {
763      if (isModify) {
764        this.isShowTitle = false;
765        this.isShowInputText = true;
766      } else {
767        this.isShowTitle = true;
768        this.isShowInputText = false;
769      }
770    }
771
772    handleImageCollapseAfterAddNode(isAddImageCollapse: boolean) {
773      // listTree this node already has ImageCollapse.
774      if (isAddImageCollapse) {
775        this.nodeItem.imageCollapse = new ImageNode($r('sys.media.ohos_ic_public_arrow_down'), null, null,
776          $r('sys.float.ohos_id_alpha_content_tertiary'),
777          IMAGE_NODE_HEIGHT,
778          IMAGE_NODE_WIDTH);
779        this.nodeItem.imageCollapse.itemRightMargin = ($r('sys.float.ohos_id_text_paragraph_margin_xs'));
780      } else {
781        this.nodeItem.imageCollapse = null;
782      }
783    }
784
785    setNodeColor(nodeColor: Resource | string): void {
786      this.nodeColor = nodeColor;
787    }
788
789    getNodeColor(): Resource | string {
790      return this.nodeColor;
791    }
792
793    setListItemHeight(listItemHeight: number): void {
794      this.listItemHeight = listItemHeight;
795    }
796
797    getListItemHeight(): number {
798      return this.listItemHeight;
799    }
800
801    getNodeCurrentNodeId(): number {
802      return this.currentNodeId;
803    }
804
805    getNodeParentNodeId(): number {
806      return this.parentNodeId;
807    }
808
809    getNodeLeftPadding(): number {
810      return this.nodeLeftPadding;
811    }
812
813    getNodeHeight(): Resource | number {
814      return this.nodeHeight;
815    }
816
817    setNodeIsShow(nodeIsShow: boolean): void {
818      this.nodeIsShow = nodeIsShow;
819    }
820
821    getNodeIsShow(): boolean {
822      return this.nodeIsShow;
823    }
824
825    getNodeItem()  {
826      return this.nodeItem;
827    }
828
829    getNodeStatus() {
830      return this.status;
831    }
832
833    getNodeBorder() {
834      return this.nodeBorder;
835    }
836
837    setNodeBorder(isClearFocusStatus: boolean): void {
838      this.nodeBorder.borderWidth = isClearFocusStatus ?  this.borderWidth.has : this.borderWidth.none;
839    }
840
841    getChildNodeInfo() {
842      return this.childNodeInfo;
843    }
844
845    getCurrentNodeId() {
846      return this.currentNodeId;
847    }
848
849    getMenu() {
850      return this.menu;
851    }
852
853    setIsSelected(isSelected: boolean) {
854      this.isSelected = isSelected;
855    }
856
857    getIsSelected() {
858      return this.isSelected;
859    }
860
861    /* To gain the information while to alter node. */
862    getNodeInfoData() {
863      return this.nodeParam;
864    }
865
866    /* To gain the tree Node(NodeItem) while to alter node. */
867    public getNodeInfoNode() {
868      return this.node;
869    }
870
871    public getIsFolder() {
872      return this.nodeParam.isFolder;
873    }
874
875    public setCanShowFlagLine(canShowFlagLine: boolean) {
876      this.canShowFlagLine = canShowFlagLine;
877    }
878
879    public getCanShowFlagLine(): boolean {
880      return this.canShowFlagLine;
881    }
882
883    public setFlagLineLeftMargin(currentNodeLevel: number) {
884      this.flagLineLeftMargin = currentNodeLevel * 12 + 8;  // calculate
885    }
886
887    public getFlagLineLeftMargin(): number {
888      return this.flagLineLeftMargin;
889    }
890
891    public getNodeLevel(): number {
892      return this.nodeLevel;
893    }
894
895    public setIsOverBorder(isOverBorder: boolean) {
896      this.isOverBorder = isOverBorder;
897    }
898
899    public getIsOverBorder() {
900      return this.isOverBorder;
901    }
902
903    public setCanShowBottomFlagLine(canShowBottomFlagLine: boolean) {
904      this.canShowBottomFlagLine = canShowBottomFlagLine;
905    }
906
907    public getCanShowBottomFlagLine() {
908      return this.canShowBottomFlagLine;
909    }
910
911    public setIsHighLight(isHighLight: boolean) {
912      this.isHighLight = isHighLight;
913    }
914
915    public getIsHighLight(): boolean {
916      return this.isHighLight;
917    }
918
919    public setIsModify(isModify: boolean) {
920      this.isModify = isModify;
921    }
922
923    public getIsModify(): boolean {
924      return this.isModify;
925    }
926
927  }
928
929
930  export class ListNodeUtils {
931    private _root: NodeItem;
932    public addNewNodeId: number;
933    private readonly MaxNodeLevel = 50;
934    private readonly MAX_CN_LENGTH: number = 254;
935    private readonly MAX_EN_LENGTH: number = 255;
936    private readonly INITIAL_INVALID_VALUE = -1;
937    constructor() {
938      this._root = new NodeItem({});
939      this._root.nodeLevel = -1;
940      this._root.parentNodeId = -1;
941      this._root.currentNodeId = -1;
942    }
943
944    getNewNodeId() {
945      return this.addNewNodeId;
946    }
947
948    traverseNodeDF(callback, root: NodeItem = this._root) {
949      let stack = [], found = false;
950      stack.unshift(root);
951      let currentNode = stack.shift();
952      while(!found && currentNode) {
953        found = callback(currentNode) === true;
954        if (!found) {
955          stack.unshift(...currentNode.children);
956          currentNode = stack.shift();
957        }
958      }
959    }
960
961    traverseNodeBF(callback) {
962      let queue = [];
963      let found: boolean = false;
964      queue.push(this._root);
965      let currentNode: NodeItem = queue.shift();
966      while(!found && currentNode) {
967        try {
968          found = callback(currentNode);
969        } catch(err) {
970          var e = err.name + " == " + err.message;
971        }
972        if (!found) {
973          queue.push(...currentNode.children)
974          currentNode = queue.shift();
975        }
976      }
977    }
978
979    private contains(callback, traversal) {
980      traversal.call(this, callback, true);
981    }
982
983    private updateParentChildNum(parentNode: NodeItem, isAdd: boolean, count: number) {
984      let parentNodeId: number = parentNode.parentNodeId;
985      while(parentNodeId >= 0) {
986        this.traverseNodeDF((node: NodeItem): boolean => {
987          if (node.currentNodeId == parentNodeId) {
988            node.getChildNodeInfo().allChildNum =
989            isAdd ? node.getChildNodeInfo().allChildNum + count : node.getChildNodeInfo().allChildNum - count;
990            parentNodeId = node.parentNodeId;
991            return false;
992          }
993          return false;
994        })
995      }
996    }
997
998    findParentNodeId(currentNodeId: number): number {
999      let current = null,
1000        callback = function(node): boolean {
1001          if (node.currentNodeId == currentNodeId ) {
1002            current = node;
1003            return true;
1004          }
1005          return false;
1006        };
1007      this.contains(callback, this.traverseNodeBF);
1008      return current.parentNodeId;
1009    }
1010
1011    addNode(parentNodeId: number,
1012            currentNodeId: number,
1013            data: { isFolder?: boolean,
1014            icon?: Resource,
1015            selectedIcon?: Resource,
1016            editIcon?: Resource,
1017            primaryTitle?: string,
1018            secondaryTitle?: number | string,
1019            menu?: () => void,
1020            }): ListNodeUtils {
1021      if (this._root === null) {
1022        this._root = new NodeItem({});
1023        this._root.nodeLevel = -1;
1024        this._root.parentNodeId = -1;
1025        this._root.currentNodeId = -1;
1026      }
1027
1028      let parent = null,
1029        callback = function(node): boolean {
1030          if (node.currentNodeId == parentNodeId ) {
1031            parent = node;
1032            return true;
1033          }
1034          return false;
1035        };
1036      this.contains(callback, this.traverseNodeBF);
1037      if (parent) {
1038        let currentNode: NodeItem = new NodeItem(data);
1039        if (parent.nodeLevel > this.MaxNodeLevel) {
1040          throw new Error('ListNodeUtils[addNode]: The level of the tree view cannot exceed 50.');
1041        }
1042        currentNode.nodeLevel = parent.nodeLevel + 1; // nodeLevel
1043        currentNode.parentNodeId = parentNodeId;
1044        currentNode.currentNodeId = currentNodeId;
1045        parent.children.push(currentNode);
1046        parent.getChildNodeInfo().isHasChildNode = true;
1047        parent.getChildNodeInfo().childNum = parent.children.length;
1048        parent.getChildNodeInfo().allChildNum += 1; // childNum
1049        parent.addImageCollapse(parent.getChildNodeInfo().isHasChildNode);
1050        this.updateParentChildNum(parent, true, 1);
1051        return this;
1052      } else {
1053        throw new Error('ListNodeUtils[addNode]: Parent node not found.');
1054      }
1055    }
1056
1057    findNodeIndex(children, currentNodeId: number) {
1058      let index = this.INITIAL_INVALID_VALUE;
1059      for (let i = 0, len = children.length; i < len; i++) {
1060        if (children[i].currentNodeId === currentNodeId) {
1061          index = i;
1062          break;
1063        }
1064      }
1065      return index;
1066    }
1067
1068    private freeNodeMemory(rootNode: NodeItem, removeNodeIdList: number[]) {
1069      let deleteNode: NodeItem[] = [];
1070      let callback = function(node): boolean {
1071        deleteNode.push(node);
1072        return false;
1073      };
1074      this.traverseNodeDF(callback, rootNode);
1075      deleteNode.forEach((value)=>{
1076        removeNodeIdList.push(value.currentNodeId);
1077        value = null;
1078      })
1079    }
1080
1081    removeNode(currentNodeId: number, parentNodeId: number, traversal: any) {
1082      let parent = null,
1083        callback = function(node): boolean {
1084          if (node.currentNodeId == parentNodeId) {
1085            parent = node;
1086            return true;
1087          }
1088          return false;
1089        };
1090      this.contains(callback, traversal);
1091
1092      if (parent) {
1093        let removeNodeIdList: number[] = [];
1094        let index = this.findNodeIndex(parent.children, currentNodeId);
1095        if (index < 0) {
1096          throw new Error('Node does not exist.');
1097        } else {
1098          var deleteNodeAllChildNum = parent.children[index].getChildNodeInfo().allChildNum + 1;
1099          this.freeNodeMemory(parent.children[index], removeNodeIdList);
1100          let node = parent.children.splice(index, 1);
1101          node = null;
1102          if (parent.children.length == 0) {
1103            parent.addImageCollapse(false);
1104          }
1105        }
1106        parent.getChildNodeInfo().childNum = parent.children.length;
1107        parent.getChildNodeInfo().allChildNum -= (deleteNodeAllChildNum);
1108        this.updateParentChildNum(parent, false, deleteNodeAllChildNum);
1109        return removeNodeIdList;
1110      } else {
1111        throw new Error('Parent does not exist.');
1112      }
1113    }
1114
1115    getNewNodeInfo(nodeId: number) {
1116      let parent = null,
1117        callback = function(node): boolean {
1118          if (node.currentNodeId == nodeId) {
1119            parent = node;
1120            return true;
1121          }
1122          return false;
1123        };
1124      this.contains(callback, this.traverseNodeBF);
1125      let newNodeInfo: { isFolder: boolean, icon: Resource, selectedIcon: Resource, editIcon: Resource, menu: () => any, secondaryTitle: number | string } =
1126        { isFolder: true, icon: null, selectedIcon: null, editIcon: null, menu: null, secondaryTitle: '' };
1127      if (parent) {
1128        if (parent.children.length === 0) {
1129          if (parent.getNodeItem().imageNode != null) {
1130            newNodeInfo.icon = parent.getNodeItem().imageNode.normalSource;
1131            newNodeInfo.selectedIcon = parent.getNodeItem().imageNode.selectedSource;
1132            newNodeInfo.editIcon = parent.getNodeItem().imageNode.editSource;
1133            newNodeInfo.menu = parent.getMenu();
1134          } else {
1135            newNodeInfo.icon = null;
1136            newNodeInfo.selectedIcon = null;
1137            newNodeInfo.editIcon = null;
1138            newNodeInfo.menu = parent.getMenu();
1139          }
1140        } else if (parent.children.length > 0) {
1141          if (parent.getNodeItem().imageNode != null) {
1142            newNodeInfo.icon = (parent.children[0].getNodeItem().imageNode != null) ?
1143            parent.children[0].getNodeItem().imageNode.normalSource : null;
1144            newNodeInfo.selectedIcon = (parent.children[0].getNodeItem().imageNode != null) ?
1145            parent.children[0].getNodeItem().imageNode.selectedSource : null;
1146            newNodeInfo.editIcon = (parent.children[0].getNodeItem().imageNode != null) ?
1147            parent.children[0].getNodeItem().imageNode.editSource : null;
1148            newNodeInfo.menu = parent.children[0].getMenu();
1149          } else {
1150            newNodeInfo.icon = null;
1151            newNodeInfo.selectedIcon = null;
1152            newNodeInfo.editIcon = null;
1153            newNodeInfo.menu = parent.children[0].getMenu();
1154          }
1155        }
1156      }
1157      return newNodeInfo;
1158    }
1159
1160    getClickChildId(nodeId: number) {
1161      let parent = null,
1162        callback = function(node): boolean {
1163          if (node.currentNodeId == nodeId) {
1164            parent = node;
1165            return true;
1166          }
1167          return false;
1168        };
1169      this.contains(callback, this.traverseNodeBF);
1170      if (parent) {
1171        if (parent.children.length === 0) {
1172          return [];
1173        } else if (parent.children.length > 0) {
1174          var nodeInfo: { itemId: number, itemIcon: Resource, itemTitle: string } =
1175            { itemId: null, itemIcon: null, itemTitle: null }
1176          var childrenNodeInfo: Array<number> = new Array(parent.children.length);
1177          for (let i = 0; i < childrenNodeInfo.length; i++) {
1178            childrenNodeInfo[i] = 0;
1179          }
1180          for (let i = 0; i < parent.children.length && i < childrenNodeInfo.length; i++) {
1181            childrenNodeInfo[i] = parent.children[i].currentNodeId;
1182          }
1183          return childrenNodeInfo;
1184        }
1185      }
1186      return [];
1187    }
1188
1189    getClickNodeChildrenInfo(nodeId: number) {
1190      let parent = null,
1191        callback = function(node): boolean {
1192          if (node.currentNodeId == nodeId) {
1193            parent = node;
1194            return true;
1195          }
1196          return false;
1197        };
1198      this.contains(callback, this.traverseNodeBF);
1199      if (parent) {
1200        if (parent.children.length === 0) {
1201          return [];
1202        } else if (parent.children.length > 0) {
1203          var nodeInfo: { itemId: number, itemIcon: Resource, itemTitle: string } =
1204            { itemId: null, itemIcon: null, itemTitle: null }
1205          var childrenNodeInfo: Array<{ itemId: number, itemIcon: Resource, itemTitle: string, isFolder: boolean }> = new Array(parent.children.length);
1206          for (let i = 0; i < childrenNodeInfo.length; i++) {
1207            childrenNodeInfo[i] = { itemId: null, itemIcon: null, itemTitle: null, isFolder: null };
1208          }
1209          for (let i = 0; i < parent.children.length && i < childrenNodeInfo.length; i++) {
1210            childrenNodeInfo[i].itemId = parent.children[i].currentNodeId;
1211            if (parent.children[i].getNodeItem().imageNode) {
1212              childrenNodeInfo[i].itemIcon = parent.children[i].getNodeItem().imageNode.source;
1213            }
1214            if (parent.children[i].getNodeItem().mainTitleNode) {
1215              childrenNodeInfo[i].itemTitle = parent.children[i].getNodeItem().mainTitleNode.title;
1216            }
1217            childrenNodeInfo[i].isFolder =  parent.children[i].getIsFolder();
1218          }
1219          return childrenNodeInfo;
1220        }
1221      }
1222      return [];
1223    }
1224
1225    public checkMainTitleIsValid(title: string) : boolean {
1226      let invalid = /[\\\/:*?"<>|]/;
1227      let invalidLength = /^[\u4e00-\u9fa5]+$/;
1228      if (invalid.test(title)) {
1229        return false;
1230      }
1231      if ((invalidLength.test(title) && title.length > this.MAX_CN_LENGTH) ||
1232        (!invalidLength.test(title) && title.length > this.MAX_EN_LENGTH)) {
1233        return false;
1234      }
1235      return true;
1236    }
1237
1238    /*
1239   * DFS: Depth first traversal in drag event.
1240   * @param callback
1241   */
1242    dragTraverseNodeDF(callback, root: NodeItem = this._root, listNode) {
1243      let stack = [], found = false;
1244      stack.unshift(root);
1245      let currentNode = stack.shift();
1246      while(!found && currentNode) {
1247        found = callback(currentNode, listNode) === true;
1248        if (!found) {
1249          stack.unshift(...currentNode.children);
1250          currentNode = stack.shift();
1251        }
1252      }
1253    }
1254
1255    /*
1256   * Add the first dragging node in dragging nodes
1257   * 1.the first dragging node needs to distinguish the position to insert
1258   */
1259    addDragNode(parentNodeId: number,
1260                currentNodeId: number,
1261                insertCurrentNodeId: number,
1262                isAfter: boolean,
1263                data: { isFolder?: boolean,
1264                icon?: Resource,
1265                selectedIcon?: Resource,
1266                editIcon?: Resource,
1267                primaryTitle?: string,
1268                menu?: () => any,
1269                objectCount?: number }): ListNodeUtils {
1270
1271      if (this._root === null) {
1272        this._root = new NodeItem({});
1273        this._root.nodeLevel = this.INITIAL_INVALID_VALUE;
1274        this._root.parentNodeId = this.INITIAL_INVALID_VALUE;
1275        this._root.currentNodeId = this.INITIAL_INVALID_VALUE;
1276      }
1277
1278      let parent = null,
1279        callback = function(node): boolean {
1280          if (node.currentNodeId == parentNodeId ) {
1281            parent = node;
1282            return true;
1283          }
1284          return false;
1285        };
1286      this.contains(callback, this.traverseNodeBF);
1287      if (parent) {
1288        let currentNode: NodeItem = new NodeItem(data);
1289        if (parent.nodeLevel > this.MaxNodeLevel) {
1290          throw new Error('ListNodeUtils[addNode]: The level of the tree view cannot exceed 50.');
1291        }
1292        currentNode.nodeLevel = parent.nodeLevel + 1;
1293        currentNode.parentNodeId = parentNodeId;
1294        currentNode.currentNodeId = currentNodeId;
1295        let insertIndex: number = this.INITIAL_INVALID_VALUE;
1296        if (parent.children.length) {
1297          for (let i = 0; i < parent.children.length; i++) {
1298            if ( parent.children[i].getCurrentNodeId() == insertCurrentNodeId) {
1299              insertIndex = i;
1300              break;
1301            }
1302          }
1303          if (isAfter) {
1304            parent.children.splice(insertIndex + 1, 0, currentNode);
1305          } else {
1306            parent.children.splice(insertIndex, 0, currentNode);
1307          }
1308        } else {
1309          parent.children.push(currentNode);
1310        }
1311        parent.getChildNodeInfo().isHasChildNode = true;
1312        parent.getChildNodeInfo().childNum = parent.children.length;
1313        parent.getChildNodeInfo().allChildNum += 1;
1314        parent.addImageCollapse(parent.getChildNodeInfo().isHasChildNode);
1315        this.updateParentChildNum(parent, true, 1);
1316        return this;
1317      } else {
1318        throw new Error('ListNodeUtils[addNode]: Parent node not found.');
1319      }
1320    }
1321
1322  }
1323
1324
1325  export class ListNodeDataSource extends BasicDataSource {
1326    readonly ROOT_NODE_ID = -1;
1327    private listNodeUtils: ListNodeUtils = new ListNodeUtils();
1328    private listNode: NodeInfo[] = [];
1329    private readonly INITIAL_INVALID_VALUE = -1;
1330    private lastIndex: number = -1; // record the last focused node.
1331    thisIndex: number = -1; // records clicked nodes in the current period.
1332    private modifyNodeIndex: number = -1; // records the nodes edited in the current period.
1333    modifyNodeId: number = -1
1334    private currentOperation: MenuOperation;
1335    private expandAndCollapseInfo = new Map();
1336    private loadedNodeIdAndIndexMap = new Map(); // [currentNodeId, index]
1337    private isTouchDown: boolean = false;
1338    private appEventBus: TreeListener = TreeListenerManager.getInstance().getTreeListener();
1339
1340    /* parameter of the drag event. */
1341    private isInnerDrag: boolean = false; // Judge whether it is an internal drag event.
1342    private isDrag: boolean = false; // It is used to handle events(For example, prevent press events) during global drag.
1343    private draggingCurrentNodeId: number = this.INITIAL_INVALID_VALUE; // Record the current ID of the dragged node.
1344    private draggingParentNodeId: number = this.INITIAL_INVALID_VALUE; // Record the parent ID of the dragged node.
1345    private currentNodeInfo: NodeInfo = null; // To solve the problem of currentIndex missed in onDrop event.
1346    private listItemOpacity : number = 1; // It is used to set the opacity of the node when dragged.
1347    private lastPassIndex: number = this.INITIAL_INVALID_VALUE; // record the last passing node index in drag.
1348    private lastPassId: number = this.INITIAL_INVALID_VALUE; // record the last passing node Id in drag.
1349    private thisPassIndex: number = this.INITIAL_INVALID_VALUE; // record the current passing node in drag.
1350    private lastDelayExpandIndex: number = this.INITIAL_INVALID_VALUE; // record last passing node in delay expand event.
1351    private timeoutExpandId: number = this.INITIAL_INVALID_VALUE;
1352    private lastTimeoutExpandId: number = this.INITIAL_INVALID_VALUE;
1353    private clearTimeoutExpandId: number = this.INITIAL_INVALID_VALUE;
1354    private timeoutHighLightId: number = this.INITIAL_INVALID_VALUE;
1355    private lastTimeoutHighLightId: number = this.INITIAL_INVALID_VALUE;
1356    private clearTimeoutHighLightId: number = this.INITIAL_INVALID_VALUE;
1357    private lastDelayHighLightIndex: number = this.INITIAL_INVALID_VALUE; // record last passing node in HighLight event.
1358    private lastDelayHighLightId: number = this.INITIAL_INVALID_VALUE; //record last passing node Id in HighLight event.
1359    private nodeIdAndSubtitleMap = new Map(); // [currentNodeId, subtitle]
1360    private flag: Flag = Flag.NONE;
1361    private selectedParentNodeId: number = this.INITIAL_INVALID_VALUE;
1362    private selectedParentNodeSubtitle: any = '';
1363    private insertNodeSubtitle: any = '';
1364    private currentFocusNodeId: number = this.INITIAL_INVALID_VALUE;
1365    private lastFocusNodeId: number = this.INITIAL_INVALID_VALUE;
1366    private addFocusNodeId: number = this.INITIAL_INVALID_VALUE;
1367
1368    readonly FLAG_LINE: { flagLineHeight: string,
1369    flagLineColor: Resource,
1370    xOffset: string,
1371    yTopOffset: string,
1372    yBottomOffset: string,
1373    yBasePlateOffset: string } = {
1374      flagLineHeight: FLAG_LINE_HEIGHT,
1375      flagLineColor: $r('sys.color.ohos_id_color_activated'),
1376      xOffset: X_OFF_SET,
1377      yTopOffset: Y_OFF_SET,
1378      yBottomOffset: Y_BOTTOM_OFF_SET,
1379      yBasePlateOffset: Y_BASE_PLATE_OFF_SET
1380    }
1381
1382    private readonly DRAG_POPUP: { floorConstraintSize: { minWidth: string, maxWidth: string },
1383    textConstraintSize: { minWidth1: string, maxWidth1: string,
1384    minWidth2: string, maxWidth2: string },
1385    padding: { left: string, right: string },
1386    backgroundColor: ResourceColor,
1387    height: string,
1388    shadow: { radius: Resource, color: ResourceColor, offsetX?: number, offsetY?: number },
1389    borderRadius : Resource,
1390    fontColor: Resource,
1391    fontSize: Resource,
1392    fontWeight: FontWeight
1393    imageOpacity: Resource } = {
1394      floorConstraintSize: { minWidth: FLOOR_MIN_WIDTH, maxWidth: FLOOR_MAX_WIDTH },
1395      textConstraintSize: { minWidth1: TEXT_MIN_WIDTH, maxWidth1: TEXT_MAX_WIDTH, minWidth2: MIN_WIDTH, maxWidth2: MAX_WIDTH },
1396      padding: { left: LEFT_PADDING, right: RIGHT_PADDING },
1397      backgroundColor: COLOR_IMAGE_EDIT,
1398      height: GRAG_POP_UP_HEIGHT,
1399      shadow: { radius: $r('sys.float.ohos_id_corner_radius_default_m'), color: SHADOW_COLOR, offsetX: 0, offsetY: SHADOW_OFFSETY },
1400      borderRadius: $r('sys.float.ohos_id_corner_radius_clicked'),
1401      fontColor: $r('sys.color.ohos_id_color_primary'),
1402      fontSize: $r('sys.float.ohos_id_text_size_body1'),
1403      fontWeight: FontWeight.Regular,
1404      imageOpacity: $r('sys.float.ohos_id_alpha_content_fourth')
1405    }
1406
1407    private readonly subTitle: { normalFontColor: Resource,
1408    highLightFontColor: Resource,
1409    fontSize: Resource,
1410    fontWeight: FontWeight,
1411    margin: { left: string, right: string } } = {
1412      normalFontColor: $r('sys.color.ohos_id_color_secondary'),
1413      highLightFontColor: $r('sys.color.ohos_id_color_primary_contrary'),
1414      fontSize: $r('sys.float.ohos_id_text_size_body2'),
1415      fontWeight: FontWeight.Regular,
1416      margin: { left: '4vp', right: '24' }
1417    }
1418
1419    private changeNodeColor(index: number, color: Resource | string): void {
1420      this.listNode[index].setNodeColor(color);
1421    }
1422
1423    private getNodeColor(index) {
1424      return this.listNode[index].getNodeColor();
1425    }
1426
1427    private handleFocusEffect(index: number, isClearFocusStatus: boolean) {
1428      if (this.listNode[index].getNodeIsShow()) {
1429        this.listNode[index].setNodeBorder(isClearFocusStatus);
1430      }
1431    }
1432
1433    private setImageSource(index: number, interactionStatus: InteractionStatus) {
1434      let nodeInfo: NodeInfo = this.listNode[index];
1435      nodeInfo.setIsSelected(interactionStatus === InteractionStatus.Selected ||
1436        interactionStatus === InteractionStatus.Edit || interactionStatus === InteractionStatus.FinishEdit);
1437      if (nodeInfo.getNodeItem().mainTitleNode != null && interactionStatus != InteractionStatus.DragInsert &&
1438        interactionStatus != InteractionStatus.FinishDragInsert) {
1439        nodeInfo.getNodeItem().mainTitleNode.setMainTitleSelected(interactionStatus === InteractionStatus.Selected ||
1440          interactionStatus === InteractionStatus.FinishEdit);
1441      }
1442      if (nodeInfo.getNodeItem().imageNode != null) {
1443        nodeInfo.getNodeItem().imageNode.setImageSource(interactionStatus);
1444      }
1445    }
1446
1447    private setImageCollapseSource(index: number, interactionStatus: InteractionStatus) {
1448      let nodeInfo: NodeInfo = this.listNode[index];
1449      if (nodeInfo.getNodeItem().imageCollapse != null) {
1450        nodeInfo.getNodeItem().imageCollapse.setImageCollapseSource(interactionStatus,
1451          this.expandAndCollapseInfo.get(nodeInfo.getCurrentNodeId()));
1452      }
1453    }
1454
1455    public clearLastIndexStatus() {
1456      if (this.lastIndex == -1 || this.lastIndex >= this.listNode.length) {
1457        return;
1458      }
1459      this.setImageSource(this.lastIndex, InteractionStatus.Normal);
1460      this.changeNodeColor(this.lastIndex, this.listNode[this.lastIndex].getNodeStatus().normal);
1461      this.handleFocusEffect(this.lastIndex, false);
1462      this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(this.listNode[this.lastIndex].getCurrentNodeId()));
1463    }
1464
1465    private changeNodeStatus(clickIndex: number): void {
1466      let thisIndex: number = clickIndex;
1467      let tmp: NodeInfo[] = this.ListNode;
1468      let nodeId =  tmp[clickIndex].getCurrentNodeId();
1469      if (this.expandAndCollapseInfo.get(nodeId) == NodeStatus.Expand) {
1470        this.expandAndCollapseInfo.set(nodeId, NodeStatus.Collapse);
1471        tmp[thisIndex].getNodeItem().imageCollapse.changeImageCollapseSource(NodeStatus.Collapse);
1472      } else if (this.expandAndCollapseInfo.get(nodeId) == NodeStatus.Collapse) {
1473        this.expandAndCollapseInfo.set(nodeId, NodeStatus.Expand);
1474        tmp[thisIndex].getNodeItem().imageCollapse.changeImageCollapseSource(NodeStatus.Expand);
1475      }
1476    }
1477
1478    private handleExpandAndCollapse(clickIndex: number) {
1479      let thisIndex: number = clickIndex;
1480      let tmp: NodeInfo[] = this.ListNode;
1481      let nodeId = tmp[thisIndex].getCurrentNodeId();
1482      if (!this.expandAndCollapseInfo.has(nodeId)) {
1483        return;
1484      }
1485
1486      let rootNodeStatus: NodeStatus = this.expandAndCollapseInfo.get(nodeId);
1487      if (tmp[thisIndex].getChildNodeInfo().isHasChildNode && rootNodeStatus == NodeStatus.Collapse) {
1488        for(var i = 0; i < tmp[thisIndex].getChildNodeInfo().allChildNum; i++) {
1489          tmp[thisIndex + 1 + i].setNodeIsShow(false);
1490          tmp[thisIndex + 1 + i].setListItemHeight(LIST_ITEM_HEIGHT_NONE);
1491        }
1492        this.notifyDataReload();
1493        return;
1494      }
1495
1496      let childNum: number[] = new Array(tmp[thisIndex].getChildNodeInfo().childNum);
1497      childNum[0] = thisIndex + 1;
1498      let index = 1;
1499      while(index < tmp[thisIndex].getChildNodeInfo().childNum) {
1500        childNum[index] = childNum[index -1] + tmp[childNum[index - 1]].getChildNodeInfo().allChildNum + 1;
1501        index++;
1502      }
1503      if (rootNodeStatus == NodeStatus.Expand) {
1504        for(var i = 0; i < childNum.length; i++) {
1505          tmp[childNum[i]].setNodeIsShow(true);
1506          tmp[childNum[i]].setListItemHeight(LIST_ITEM_HEIGHT);
1507          let nodeId =  tmp[childNum[i]].getCurrentNodeId();
1508          if(this.expandAndCollapseInfo.get(nodeId) == NodeStatus.Expand) {
1509            this.handleExpandAndCollapse(childNum[i]);
1510          }
1511        }
1512      }
1513      childNum = null;
1514      this.notifyDataReload();
1515    }
1516
1517    public init(listNodeUtils: ListNodeUtils) {
1518      let index = 0;
1519      this.listNode = [];
1520      this.listNodeUtils = listNodeUtils;
1521      this.loadedNodeIdAndIndexMap.clear();
1522      this.listNodeUtils.traverseNodeDF((node: NodeItem): boolean => {
1523        if (node.currentNodeId >= 0) {
1524          var nodeInfo: NodeInfo = new NodeInfo(node);
1525          this.listNode.push(nodeInfo);
1526          if (nodeInfo.getChildNodeInfo().isHasChildNode) {
1527            this.expandAndCollapseInfo.set(nodeInfo.getCurrentNodeId(), NodeStatus.Collapse);
1528          }
1529          if (nodeInfo.getNodeIsShow()) {
1530            this.loadedNodeIdAndIndexMap.set(nodeInfo.getCurrentNodeId(), index++);
1531          }
1532          if (nodeInfo.getIsFolder()) {
1533            this.nodeIdAndSubtitleMap.set(nodeInfo.getCurrentNodeId(),
1534              nodeInfo.getNodeInfoData().secondaryTitle || nodeInfo.getNodeInfoData().secondaryTitle == 0 ?
1535              nodeInfo.getNodeInfoData().secondaryTitle : '');
1536          }
1537        }
1538        return false;
1539      });
1540    }
1541
1542    private refreshRemoveNodeData(removeNodeIdList: number[], parentNodeInfo: NodeInfo) {
1543      let deleteIndexList: number[] = [];
1544      for (let i = 0; i < removeNodeIdList.length; i++) {
1545        for (let j = 0; j < this.listNode.length; j++) {
1546          if (this.listNode[j].getNodeCurrentNodeId() == removeNodeIdList[i]) {
1547            let currentNodeId = this.listNode[j].getNodeCurrentNodeId();
1548            if (this.loadedNodeIdAndIndexMap.has(currentNodeId)) {
1549              // this.listNode index to lazyForEach index.
1550              deleteIndexList.push(this.loadedNodeIdAndIndexMap.get(currentNodeId));
1551            }
1552            let deleteNode = this.listNode.splice(j, 1);
1553            deleteNode = null; // free memory
1554            if (this.expandAndCollapseInfo.has(removeNodeIdList[i])) {
1555              this.expandAndCollapseInfo.delete(removeNodeIdList[i]); // delete deleteNode expandAndCollapseInfo.
1556            }
1557            break;
1558          }
1559        }
1560      }
1561      deleteIndexList.forEach((value)=>{
1562        this.notifyDataDelete(value); // notifyDataDelete do not  update data.
1563        this.notifyDataChange(value); // call notifyDataChange to update data.
1564      })
1565      let index: number = 0;
1566      for (let i = 0; i < this.listNode.length; i++) {
1567        if (this.listNode[i].getNodeCurrentNodeId() == parentNodeInfo.getNodeCurrentNodeId()) {
1568          if (parentNodeInfo.getNodeItem().imageCollapse == null) {
1569            this.listNode[i].handleImageCollapseAfterAddNode(false);
1570            // delete deleteNode parentNode expandAndCollapseInfo.
1571            this.expandAndCollapseInfo.delete(parentNodeInfo.getNodeCurrentNodeId());
1572            this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(this.listNode[i].getNodeCurrentNodeId()));
1573          }
1574          break;
1575        }
1576      }
1577      let callbackParam: CallbackParam = {currentNodeId: parentNodeInfo.getNodeCurrentNodeId(), parentNodeId: parentNodeInfo.getNodeParentNodeId()};
1578      this.appEventBus.emit(TreeListenType.NODE_DELETE, [callbackParam]);
1579    }
1580
1581    private refreshAddNodeData(addNodeIdList: number[]) {
1582      var addNodeInfo: NodeInfo;
1583      this.listNodeUtils.traverseNodeDF((node: NodeItem): boolean => {
1584        if (node.currentNodeId === addNodeIdList[0]) {
1585          addNodeInfo = new NodeInfo(node);
1586          return true;
1587        }
1588        return false;
1589      });
1590      addNodeInfo.setIsModify(true);
1591
1592      let index: number = 0;
1593      for (let i = 0; i < this.listNode.length; i++) {
1594        if (this.listNode[i].getNodeCurrentNodeId() == addNodeInfo.getNodeParentNodeId()) {
1595          index = i;
1596          if (this.listNode[i].getNodeItem().imageCollapse == null) {
1597            this.listNode[i].handleImageCollapseAfterAddNode(true);
1598            this.notifyDataChange(index);
1599          } else if (this.expandAndCollapseInfo.get(this.listNode[i].getNodeCurrentNodeId()) == NodeStatus.Collapse) {
1600            this.changeNodeStatus(index);
1601          }
1602          this.listNode.splice(i + 1, 0, addNodeInfo);
1603          this.listNode[i + 1].setTitleAndInputTextStatus(true); // false->true: realize inner Interaction.
1604          this.listNode[i + 1].setNodeIsShow(true);
1605          this.listNode[i + 1].setListItemHeight(LIST_ITEM_HEIGHT);
1606          this.setImageSource(i + 1, InteractionStatus.Edit); // Normal->Edit : realize inner Interaction.
1607          this.currentOperation = MenuOperation.ADD_NODE;
1608          this.notifyDataAdd(i + 1);
1609          this.notificationNodeInfo(i+1, this.currentOperation);
1610          break;
1611        }
1612      }
1613      this.modifyNodeIndex = index + 1;
1614      this.expandAndCollapseInfo.set(addNodeInfo.getNodeParentNodeId(), NodeStatus.Expand);
1615      this.handleExpandAndCollapse(index);
1616    }
1617
1618    public refreshData(listNodeUtils: ListNodeUtils, operation: MenuOperation,
1619                       parentNodeId: number, changeNodeIdList: number[]) {
1620      let parentNodeInfo: NodeInfo;
1621      this.listNodeUtils = listNodeUtils;
1622      this.listNodeUtils.traverseNodeDF((node: NodeItem): boolean => {
1623        if (node.currentNodeId == parentNodeId) {
1624          parentNodeInfo = new NodeInfo(node);
1625          return true;
1626        }
1627        return false;
1628      });
1629
1630      if (operation === MenuOperation.REMOVE_NODE) {
1631        this.nodeIdAndSubtitleMap.set(parentNodeId, this.selectedParentNodeSubtitle);
1632        this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(parentNodeId));
1633        this.refreshRemoveNodeData(changeNodeIdList, parentNodeInfo);
1634      }
1635
1636      if (operation === MenuOperation.ADD_NODE) {
1637        this.addFocusNodeId = changeNodeIdList[0];
1638        this.nodeIdAndSubtitleMap.set(this.getClickNodeId(), this.selectedParentNodeSubtitle);
1639        this.nodeIdAndSubtitleMap.set(changeNodeIdList[0], this.insertNodeSubtitle);
1640        this.refreshAddNodeData(changeNodeIdList);
1641      }
1642    }
1643
1644    public setClickIndex(index: number) {
1645      this.thisIndex = index;
1646    }
1647
1648    public getClickNodeId(): number {
1649      if (this.thisIndex < 0 || this.thisIndex >= this.ListNode.length) {
1650        return  -1;
1651      }
1652      return this.ListNode[this.thisIndex].getCurrentNodeId();
1653    }
1654
1655    public expandAndCollapseNode(clickIndex: number) {
1656      this.changeNodeStatus(clickIndex);
1657      this.handleExpandAndCollapse(clickIndex)
1658    }
1659
1660    public getIsTouchDown(): boolean {
1661      return this.isTouchDown;
1662    }
1663
1664    public getLastIndex(): number {
1665      return this.lastIndex;
1666    }
1667
1668    public handleEvent(event: Event, index: number) {
1669      /* Return while the event is dragging event. */
1670      if (this.isDrag) {
1671        return;
1672      }
1673
1674      if (event === Event.TOUCH_DOWN || event === Event.TOUCH_UP || event === Event.MOUSE_BUTTON_RIGHT) {
1675        if (index != this.lastIndex) {
1676          this.clearLastIndexStatus();
1677        }
1678      }
1679      let lazyForEachIndex = this.loadedNodeIdAndIndexMap.get(this.listNode[index].getCurrentNodeId());
1680      switch(event) {
1681        case Event.TOUCH_DOWN:
1682          this.isTouchDown = true;
1683          this.changeNodeColor(index, this.listNode[index].getNodeStatus().press);
1684          break;
1685        case Event.TOUCH_UP: {
1686          if (this.isInnerDrag) {
1687            this.isInnerDrag = false;
1688          }
1689          this.isTouchDown = false;
1690          let nodeInfo: NodeInfo = this.listNode[index];
1691          this.setImageSource(index, InteractionStatus.Selected);
1692          this.lastIndex = index;
1693          this.changeNodeColor(index, nodeInfo.getNodeStatus().selected);
1694          this.notifyDataChange(lazyForEachIndex);
1695          break;
1696        }
1697        case Event.HOVER:
1698          if (this.getNodeColor(index) != this.listNode[index].getNodeStatus().selected) {
1699            this.changeNodeColor(index, this.listNode[index].getNodeStatus().hover);
1700            this.notifyDataChange(lazyForEachIndex);
1701          }
1702          break;
1703        case Event.HOVER_OVER:
1704          if (this.getNodeColor(index) != this.listNode[index].getNodeStatus().selected) {
1705            this.changeNodeColor(index, this.listNode[index].getNodeStatus().normal);
1706            this.notifyDataChange(lazyForEachIndex);
1707          }
1708          break;
1709        case Event.FOCUS:
1710          this.handleFocusEffect(index, true);
1711          this.notifyDataChange(lazyForEachIndex);
1712          break;
1713        case Event.BLUR:
1714          this.handleFocusEffect(index, false);
1715          this.notifyDataChange(lazyForEachIndex);
1716          break;
1717        case Event.MOUSE_BUTTON_RIGHT:
1718          this.lastIndex = index;
1719          this.finishEditing();
1720          break;
1721        case Event.DRAG:
1722          this.isTouchDown = false;
1723          let nodeInfo: NodeInfo = this.listNode[index];
1724          this.setImageSource(index, InteractionStatus.Selected);
1725          this.lastIndex = index;
1726          this.changeNodeColor(index, nodeInfo.getNodeStatus().selected);
1727          this.notifyDataChange(lazyForEachIndex);
1728          break;
1729        default:
1730          break;
1731      }
1732    }
1733
1734    private notificationNodeInfo(addNodeId: number, operation: MenuOperation) {
1735      if (operation === MenuOperation.MODIFY_NODE) {
1736        let modifyNodeInfo: NodeInfo = this.listNode[this.modifyNodeIndex];
1737        let backParamModify: CallbackParam = { currentNodeId: modifyNodeInfo.getNodeCurrentNodeId(),
1738          parentNodeId: modifyNodeInfo.getNodeParentNodeId() }
1739        this.appEventBus.emit(TreeListenType.NODE_MODIFY,
1740          [backParamModify]);
1741      } else if (operation === MenuOperation.ADD_NODE)  {
1742        let addNodeInfo: NodeInfo = this.listNode[addNodeId];
1743        let icon: Resource = (addNodeInfo.getNodeItem().imageNode != null) ?
1744        addNodeInfo.getNodeItem().imageNode.source : null;
1745        let selectedIcon: Resource = (addNodeInfo.getNodeItem().imageNode != null) ?
1746        addNodeInfo.getNodeItem().imageNode.selectedSource : null;
1747        let editIcon: Resource = (addNodeInfo.getNodeItem().imageNode != null) ?
1748        addNodeInfo.getNodeItem().imageNode.editSource : null;
1749        let callbackParam: CallbackParam = { currentNodeId: addNodeInfo.getNodeCurrentNodeId(),
1750          parentNodeId: addNodeInfo.getNodeParentNodeId() }
1751        this.appEventBus.emit(TreeListenType.NODE_ADD,
1752          [callbackParam]);
1753      }
1754    }
1755
1756    public finishEditing() {
1757      if (this.modifyNodeIndex!= -1) {
1758        this.setImageSource(this.modifyNodeIndex, InteractionStatus.FinishEdit);
1759        this.setImageCollapseSource(this.modifyNodeIndex, InteractionStatus.FinishEdit);
1760        this.listNode[this.modifyNodeIndex].setIsModify(false);
1761        this.listNode[this.modifyNodeIndex].setTitleAndInputTextStatus(false);
1762        this.notificationNodeInfo(this.modifyNodeIndex, this.currentOperation);
1763        this.notifyDataChange(this.modifyNodeIndex);
1764      }
1765    }
1766
1767    public setItemVisibilityOnEdit(nodeId: number, operation: MenuOperation) {
1768      let index: number = -1;
1769      if (nodeId == -1) {
1770        return;
1771      }
1772      if (operation === MenuOperation.MODIFY_NODE) {
1773        for (let i = 0; i < this.listNode.length; i++) { // nodeId to find index
1774          if (this.listNode[i].getCurrentNodeId() == nodeId) {
1775            index = i;
1776            break;
1777          }
1778        }
1779        let nodeInfo: NodeInfo = this.listNode[index];
1780        nodeInfo.setIsModify(true);
1781        if (nodeInfo.getNodeItem().mainTitleNode === null) {
1782          return; // no title
1783        }
1784
1785        this.currentOperation = MenuOperation.MODIFY_NODE;
1786        nodeInfo.setTitleAndInputTextStatus(true);
1787        this.setImageSource(index, InteractionStatus.Edit);
1788        this.setImageCollapseSource(index, InteractionStatus.Edit);
1789        this.modifyNodeIndex = index;
1790        if (nodeInfo.getNodeItem().inputText) {
1791          if (nodeInfo.getNodeItem().imageCollapse != null) {
1792            nodeInfo.getNodeItem().inputText.rightMargin =
1793            $r('sys.float.ohos_id_text_paragraph_margin_xs')
1794          } else {
1795            nodeInfo.getNodeItem().inputText.rightMargin =
1796            $r('sys.float.ohos_id_elements_margin_horizontal_m')
1797          }
1798        }
1799        this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(nodeId));
1800      }
1801      index = nodeId;
1802      if (operation === MenuOperation.COMMIT_NODE) {
1803        let nodeInfo: NodeInfo = this.listNode[index];
1804        nodeInfo.setTitleAndInputTextStatus(false);
1805        nodeInfo.setIsModify(false);
1806        this.setImageSource(index, InteractionStatus.FinishEdit);
1807        this.setImageCollapseSource(index, InteractionStatus.FinishEdit);
1808        this.notificationNodeInfo(this.modifyNodeIndex, this.currentOperation);
1809        this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(nodeInfo.getCurrentNodeId()));
1810      }
1811    }
1812
1813    public setPopUpInfo(popUpType: PopUpType, inputError: InputError, isShow: boolean, index: number) {
1814      let nodeInfo: NodeInfo = this.listNode[index];
1815      nodeInfo.setPopUpIsShow(isShow);
1816      // this.listNode index to lazyForEach index.
1817      let lazyForEachIndex = this.loadedNodeIdAndIndexMap.get(nodeInfo.getCurrentNodeId());
1818      if (!isShow) {
1819        this.notifyDataChange(lazyForEachIndex);
1820        return;
1821      }
1822      if (popUpType === PopUpType.HINTS) {
1823        if (nodeInfo.getNodeItem().mainTitleNode != null) {
1824          nodeInfo.setPopUpText(nodeInfo.getNodeItem().mainTitleNode.title);
1825        } else {
1826          nodeInfo.setPopUpText('');
1827          nodeInfo.setPopUpIsShow(false);
1828        }
1829        nodeInfo.setPopUpEnableArrow(false);
1830        nodeInfo.setPopUpColor($r('sys.color.ohos_id_color_background'));
1831        nodeInfo.setPopUpTextColor($r('sys.color.ohos_id_color_text_secondary'));
1832      } else if (popUpType === PopUpType.WARNINGS) {
1833        if (nodeInfo.getNodeItem().inputText != null) {
1834          if (inputError === InputError.INVALID_ERROR) {
1835            nodeInfo.setPopUpText("invalid error");
1836          } else if (inputError === InputError.LENGTH_ERROR) {
1837            nodeInfo.setPopUpText("length error");
1838          }
1839          nodeInfo.setPopUpEnableArrow(true);
1840          nodeInfo.setPopUpColor($r('sys.color.ohos_id_color_help_tip_bg'));
1841          nodeInfo.setPopUpTextColor($r('sys.color.ohos_id_color_text_hint_contrary'));
1842        }
1843      }
1844      this.notifyDataChange(lazyForEachIndex);
1845    }
1846
1847    public setShowPopUpTimeout(timeout: number, index: number) {
1848      if (this.listNode[index].getNodeItem().mainTitleNode != null) {
1849        this.listNode[index].getNodeItem().mainTitleNode.popUpTimeout = timeout;
1850      }
1851      // this.notifyDataChange(index)  lazyForEachIndex;
1852      let lazyForEachIndex = this.loadedNodeIdAndIndexMap.get(this.listNode[index].getCurrentNodeId());
1853      this.notifyDataChange(lazyForEachIndex);
1854    }
1855
1856    public setMainTitleNameOnEdit(index: number, text: string) {
1857      this.modifyNodeIndex = index;
1858      if (this.listNode[index].getNodeItem().mainTitleNode != null) {
1859        this.listNode[index].getNodeItem().mainTitleNode.title = text;
1860        // this.listNode index to lazyForEach index.
1861        let lazyForEachIndex = this.loadedNodeIdAndIndexMap.get(this.listNode[index].getCurrentNodeId());
1862        this.notifyDataChange(lazyForEachIndex);
1863      }
1864    }
1865
1866    public get ListNode(): NodeInfo[] {
1867      return this.listNode;
1868    }
1869
1870    public totalCount(): number {
1871      let count: number = 0;
1872      let index: number = 0;
1873      this.loadedNodeIdAndIndexMap.clear();
1874      for (let i =0 ; i < this.listNode.length; i++) {
1875        if (this.listNode[i].getNodeIsShow()) {
1876          this.loadedNodeIdAndIndexMap.set(this.listNode[i].getCurrentNodeId(), index++);
1877          count++;
1878        }
1879      }
1880      return count;
1881    }
1882
1883    public getData(index: number): any {
1884      let count = 0;
1885      for (let i = 0; i < this.listNode.length; i++) {
1886        if (this.listNode[i].getNodeIsShow()) {
1887          if (index == count) {
1888            return this.listNode[i];
1889          }
1890          count++;
1891        }
1892      }
1893      return null;
1894    }
1895
1896    public addData(index: number, data: NodeInfo): void {
1897      this.listNode.splice(index, 0, data)
1898      this.notifyDataAdd(index)
1899    }
1900
1901    public pushData(data: NodeInfo): void {
1902      this.listNode.push(data)
1903      this.notifyDataAdd(this.listNode.length - 1)
1904    }
1905
1906    public setIsInnerDrag(isInnerDrag: boolean) {
1907      this.isInnerDrag = isInnerDrag;
1908    }
1909
1910    public getIsInnerDrag(): boolean {
1911      return this.isInnerDrag;
1912    }
1913
1914    public setIsDrag(isDrag: boolean) {
1915      this.isDrag = isDrag;
1916    }
1917
1918    public getIsDrag(): boolean {
1919      return this.isDrag;
1920    }
1921
1922    public setCurrentNodeInfo(currentNodeInfo: NodeInfo) {
1923      this.currentNodeInfo = currentNodeInfo;
1924    }
1925
1926    public getCurrentNodeInfo() {
1927      return this.currentNodeInfo;
1928    }
1929
1930    public setDraggingParentNodeId(draggingParentNodeId: number) {
1931      this.draggingParentNodeId = draggingParentNodeId;
1932    }
1933
1934    public getDraggingParentNodeId() {
1935      return this.draggingParentNodeId;
1936    }
1937
1938    public getDraggingCurrentNodeId() {
1939      return this.draggingCurrentNodeId;
1940    }
1941
1942    public setDraggingCurrentNodeId(draggingCurrentNodeId: number) {
1943      this.draggingCurrentNodeId = draggingCurrentNodeId;
1944    }
1945
1946    public setListItemOpacity(listItemOpacity: number) {
1947      this.listItemOpacity = listItemOpacity;
1948    }
1949
1950    public getListItemOpacity(item: NodeInfo) {
1951      return item.getCurrentNodeId() == this.getDraggingCurrentNodeId() ? this.listItemOpacity : 1;
1952    }
1953
1954    public getDragPopupPara() {
1955      return this.DRAG_POPUP;
1956    }
1957
1958    public setLastPassIndex(lastPassIndex: number) {
1959      this.lastPassIndex = lastPassIndex;
1960    }
1961
1962    public getLastPassIndex(): number {
1963      return this.lastPassIndex;
1964    }
1965
1966    public getIsParentOfInsertNode(insertNodeId: number): boolean {
1967      let selectedNodeItem: NodeItem = this.currentNodeInfo.getNodeInfoNode();
1968      let isParentNodeOfInsertNode = false,
1969        callback = function(node): boolean {
1970          if (node.currentNodeId == insertNodeId ) {
1971            isParentNodeOfInsertNode = true;
1972            return true;
1973          }
1974          return false;
1975        };
1976      this.listNodeUtils.traverseNodeDF(callback, selectedNodeItem);
1977      return isParentNodeOfInsertNode;
1978    }
1979
1980    public setPassIndex(thisPassIndex: number) {
1981      this.thisPassIndex = thisPassIndex;
1982    }
1983
1984    public getPassIndex(): number {
1985      return this.thisPassIndex;
1986    }
1987
1988    public clearTimeOutAboutDelayHighLightAndExpand(currentIndex: number) {
1989      if (this.lastPassId != this.INITIAL_INVALID_VALUE && this.loadedNodeIdAndIndexMap.has(this.lastPassId)) {
1990        let index: number = this.loadedNodeIdAndIndexMap.get(this.lastPassId);
1991        let that =this;
1992        this.ListNode.forEach(function(value) {
1993          if (value.getNodeCurrentNodeId() == that.lastPassId) {
1994            value.setCanShowFlagLine(false);
1995          }
1996        })
1997        this.notifyDataChange(index);
1998      }
1999
2000      if ((this.lastTimeoutHighLightId != this.INITIAL_INVALID_VALUE &&
2001        this.clearTimeoutHighLightId != this.lastTimeoutHighLightId)) {
2002        clearTimeout(this.lastTimeoutHighLightId);
2003        if (this.lastDelayHighLightIndex != this.INITIAL_INVALID_VALUE) {
2004          this.clearHighLight(this.lastDelayHighLightIndex);
2005          let index: number = this.loadedNodeIdAndIndexMap
2006            .get(this.listNode[this.lastDelayHighLightIndex].getCurrentNodeId());
2007          this.notifyDataChange(index);
2008        }
2009        this.clearTimeoutHighLightId = this.lastTimeoutHighLightId;
2010      }
2011      this.lastTimeoutHighLightId = this.timeoutHighLightId;
2012      this.lastDelayHighLightIndex = currentIndex;
2013
2014      if ((this.lastTimeoutExpandId != this.INITIAL_INVALID_VALUE &&
2015        this.clearTimeoutExpandId != this.lastTimeoutExpandId)) {
2016        clearTimeout(this.lastTimeoutExpandId);
2017        this.clearTimeoutExpandId = this.lastTimeoutExpandId;
2018      }
2019      this.lastTimeoutExpandId = this.timeoutExpandId;
2020      this.lastDelayExpandIndex = this.INITIAL_INVALID_VALUE;
2021    }
2022
2023    public clearHighLight(currentIndex: number) {
2024      this.changeNodeColor(currentIndex, this.listNode[currentIndex].getNodeStatus().normal);
2025      this.changeNodeHighLightColor(currentIndex, false);
2026      this.setImageSource(currentIndex, InteractionStatus.FinishDragInsert);
2027      this.setImageCollapseSource(currentIndex, InteractionStatus.FinishDragInsert);
2028      this.listNode[currentIndex].setIsHighLight(false);
2029    }
2030
2031    private changeNodeHighLightColor(index: number, isHighLight: boolean): void {
2032      if (this.listNode[index].getNodeItem().mainTitleNode && this.listNode[index].getIsShowTitle()) {
2033        this.listNode[index].getNodeItem().mainTitleNode.setMainTitleHighLight(isHighLight);
2034      }
2035    }
2036
2037    public setVisibility(flag: Flag, index: number, isOverBorder: boolean) {
2038      let isChanged: boolean = (this.thisPassIndex != index || this.flag != flag) ? true : false;
2039      this.thisPassIndex = index;
2040      if ((isChanged || isOverBorder) && this.isInnerDrag) {
2041        this.flag = flag;
2042        let currentNodeId: number = this.getData(index).getCurrentNodeId();
2043        let currentNodeLevel: number = this.expandAndCollapseInfo.get(currentNodeId) == NodeStatus.Expand &&
2044                                         this.flag == Flag.DOWN_FLAG ? this.getData(index).getNodeLevel() + 1 : this.getData(index).getNodeLevel();
2045        if (this.lastPassId != this.INITIAL_INVALID_VALUE && this.loadedNodeIdAndIndexMap.has(this.lastPassId)) {
2046          let lastIndex: number = this.loadedNodeIdAndIndexMap.get(this.lastPassId);
2047          let that = this;
2048          this.ListNode.forEach(function (value) {
2049            if (value.getNodeCurrentNodeId() == that.lastPassId) {
2050              value.setCanShowFlagLine(false);
2051            }
2052          })
2053          this.notifyDataChange(lastIndex);
2054        }
2055        if (this.flag == Flag.DOWN_FLAG && index < this.totalCount() - 1) {
2056          this.getData(index).setCanShowFlagLine(false);
2057          this.getData(index+1).setCanShowFlagLine(true);
2058          this.getData(index).setCanShowBottomFlagLine(false);
2059          this.getData(index+1).setFlagLineLeftMargin(currentNodeLevel);
2060          this.notifyDataChange(index);
2061          this.notifyDataChange(index + 1);
2062          this.lastPassId = this.getData(index + 1).getNodeCurrentNodeId();
2063        } else if (this.flag == Flag.UP_FLAG && index < this.totalCount() - 1) {
2064          this.getData(index).setCanShowFlagLine(true);
2065          this.getData(index+1).setCanShowFlagLine(false);
2066          this.getData(index).setCanShowBottomFlagLine(false);
2067          this.getData(index).setFlagLineLeftMargin(currentNodeLevel);
2068          this.notifyDataChange(index);
2069          this.notifyDataChange(index + 1);
2070          this.lastPassId = this.getData(index).getNodeCurrentNodeId();
2071        } else if (index >= this.totalCount() - 1) {
2072          if (this.flag == Flag.DOWN_FLAG) {
2073            this.getData(index).setCanShowFlagLine(false);
2074            this.getData(index).setCanShowBottomFlagLine(true);
2075          } else {
2076            this.getData(index).setCanShowFlagLine(true);
2077            this.getData(index).setCanShowBottomFlagLine(false);
2078          }
2079          this.getData(index).setFlagLineLeftMargin(currentNodeLevel);
2080          this.notifyDataChange(index);
2081          this.lastPassId = this.getData(index).getNodeCurrentNodeId();
2082        }
2083      }
2084    }
2085
2086    public delayHighLightAndExpandNode(currentIndex: number, currentNodeId: number, showIndex: number) {
2087      let isChangIndex: boolean = currentIndex != this.lastDelayExpandIndex ? true : false;
2088      let isOverBorder: boolean = this.getData(showIndex).getIsOverBorder();
2089      if (isOverBorder) {
2090        this.lastDelayExpandIndex = this.INITIAL_INVALID_VALUE;
2091      } else {
2092        this.lastDelayExpandIndex = currentIndex;
2093      }
2094      if (isOverBorder || isChangIndex) {
2095        let that = this;
2096
2097        /* highLight node time-out. */
2098        let canDelayHighLight: boolean = !isOverBorder && (!this.isInnerDrag ||
2099          (this.expandAndCollapseInfo.get(currentNodeId) == NodeStatus.Collapse && this.isInnerDrag) ||
2100          (!this.expandAndCollapseInfo.has(currentNodeId) && this.listNode[currentIndex].getIsFolder()));
2101        if (canDelayHighLight) {
2102          /* set hoverState color before highLight. */
2103          this.changeNodeColor(currentIndex, this.listNode[currentIndex].getNodeStatus().hover);
2104          this.notifyDataChange(showIndex);
2105
2106          let delayHighLightTime: number = this.isInnerDrag ? 1000 : 0; // ms
2107          this.timeoutHighLightId = setTimeout(function() {
2108            that.delayHighLight(currentIndex);
2109          }, delayHighLightTime)
2110        }
2111        if (isOverBorder || (this.lastTimeoutHighLightId != this.INITIAL_INVALID_VALUE &&
2112          this.clearTimeoutHighLightId != this.lastTimeoutHighLightId)) {
2113          clearTimeout(this.lastTimeoutHighLightId);
2114          if (this.lastDelayHighLightIndex != this.INITIAL_INVALID_VALUE) {
2115            this.clearHighLight(this.lastDelayHighLightIndex);
2116            this.notifyDataReload();
2117          }
2118          this.clearTimeoutHighLightId = this.lastTimeoutHighLightId;
2119        }
2120        this.lastTimeoutHighLightId = this.timeoutHighLightId;
2121        this.lastDelayHighLightIndex = currentIndex;
2122
2123        /* alter flagLine and expand node time-out. */
2124        if (!isOverBorder && this.expandAndCollapseInfo.get(currentNodeId) == NodeStatus.Collapse) {
2125          let firstChildNodeId: number = this.getData(showIndex).getNodeInfoNode().children[0].currentNodeId;
2126          let delayAlterFlagLineAndExpandNodeTime: number = 2000; // ms
2127          this.timeoutExpandId = setTimeout(function() {
2128            that.clearHighLight(that.lastDelayHighLightIndex);
2129            that.alterFlagLineAndExpandNode(currentIndex, firstChildNodeId);
2130          }, delayAlterFlagLineAndExpandNodeTime)
2131        }
2132        if (isOverBorder || (this.lastTimeoutExpandId != this.INITIAL_INVALID_VALUE &&
2133          this.clearTimeoutExpandId != this.lastTimeoutExpandId)) {
2134          clearTimeout(this.lastTimeoutExpandId);
2135          this.clearTimeoutExpandId = this.lastTimeoutExpandId;
2136        }
2137        this.lastTimeoutExpandId = this.timeoutExpandId;
2138      }
2139    }
2140
2141    public delayHighLight(currentIndex: number) {
2142      let that =this;
2143      this.ListNode.forEach(function (value) {
2144        if (value.getNodeCurrentNodeId() == that.lastPassId) {
2145          value.setCanShowFlagLine(false);
2146          value.setCanShowBottomFlagLine(false);
2147        }
2148      })
2149      this.changeNodeColor(currentIndex, this.listNode[currentIndex].getNodeStatus().highLight);
2150      this.listNode[currentIndex].setIsHighLight(true);
2151      this.changeNodeHighLightColor(currentIndex, true);
2152      this.setImageSource(currentIndex, InteractionStatus.DragInsert);
2153      this.setImageCollapseSource(currentIndex, InteractionStatus.DragInsert);
2154      this.notifyDataReload();
2155    }
2156
2157    public alterFlagLineAndExpandNode(currentIndex: number, firstChildNodeId: number) {
2158      let that =this;
2159      this.ListNode.forEach(function (value) {
2160        if (value.getNodeCurrentNodeId() == that.lastPassId) {
2161          value.setCanShowFlagLine(false);
2162          value.setCanShowBottomFlagLine(false);
2163        }
2164      })
2165      this.ListNode.forEach(function (value) {
2166        if (that.isInnerDrag && value.getNodeCurrentNodeId() == firstChildNodeId) {
2167          value.setCanShowFlagLine(true);
2168        }
2169      })
2170      this.changeNodeStatus(currentIndex);
2171      this.handleExpandAndCollapse(currentIndex);
2172      this.lastPassId = firstChildNodeId;
2173    }
2174
2175    public hideLastLine() {
2176      if (this.lastPassId != this.INITIAL_INVALID_VALUE && this.loadedNodeIdAndIndexMap.has(this.lastPassId)) {
2177        let that = this;
2178        this.ListNode.forEach(function (value) {
2179          if (value.getNodeCurrentNodeId() == that.lastPassId) {
2180            value.setCanShowFlagLine(false);
2181            value.setCanShowBottomFlagLine(false);
2182          }
2183        })
2184        let index: number = this.loadedNodeIdAndIndexMap.get(this.lastPassId);
2185        this.notifyDataChange(index);
2186      }
2187    }
2188
2189    public clearLastTimeoutHighLight() {
2190      if (this.lastTimeoutHighLightId != this.INITIAL_INVALID_VALUE &&
2191        this.clearTimeoutHighLightId != this.lastTimeoutHighLightId) {
2192        clearTimeout(this.lastTimeoutHighLightId);
2193        if (this.lastDelayHighLightIndex != this.INITIAL_INVALID_VALUE) {
2194          this.clearHighLight(this.lastDelayHighLightIndex);
2195        }
2196      }
2197    }
2198
2199    public clearLastTimeoutExpand() {
2200      if (this.lastTimeoutExpandId != this.INITIAL_INVALID_VALUE &&
2201        this.clearTimeoutExpandId != this.lastTimeoutExpandId) {
2202        clearTimeout(this.lastTimeoutExpandId);
2203      }
2204    }
2205
2206    public getSubtitle(currentNodeId: number): string {
2207      if (this.nodeIdAndSubtitleMap.has(currentNodeId)) {
2208        if (typeof this.nodeIdAndSubtitleMap.get(currentNodeId) == "number") {
2209          return this.nodeIdAndSubtitleMap.get(currentNodeId).toString();
2210        } else {
2211          return this.nodeIdAndSubtitleMap.get(currentNodeId)
2212        }
2213      } else {
2214        return '';
2215      }
2216    }
2217
2218    public hasSubtitle(currentNodeId: number) {
2219      return this.nodeIdAndSubtitleMap.has(currentNodeId);
2220    }
2221
2222    public initialParameterAboutDelayHighLightAndExpandIndex() {
2223      this.lastDelayHighLightIndex = this.INITIAL_INVALID_VALUE;
2224      this.lastDelayExpandIndex = this.INITIAL_INVALID_VALUE;
2225      this.lastPassIndex = this.INITIAL_INVALID_VALUE;
2226      this.draggingCurrentNodeId = this.INITIAL_INVALID_VALUE;
2227      this.flag = Flag.NONE;
2228    }
2229
2230    public refreshSubtitle(insertNodeCurrentNodeId: number) {
2231      this.nodeIdAndSubtitleMap.set(this.selectedParentNodeId, this.selectedParentNodeSubtitle);
2232      this.nodeIdAndSubtitleMap.set(insertNodeCurrentNodeId, this.insertNodeSubtitle);
2233      this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(this.selectedParentNodeId));
2234      this.notifyDataChange(this.loadedNodeIdAndIndexMap.get(insertNodeCurrentNodeId));
2235    }
2236
2237    public setNodeSubtitlePara(
2238      selectedParentNodeId: number,
2239      selectedParentNodeSubtitle: ResourceStr,
2240      insertNodeSubtitle: ResourceStr
2241    ) {
2242      this.selectedParentNodeId = selectedParentNodeId;
2243      this.selectedParentNodeSubtitle = selectedParentNodeSubtitle;
2244      this.insertNodeSubtitle = insertNodeSubtitle;
2245    }
2246
2247    public getInsertNodeSubtitle() {
2248      return this.insertNodeSubtitle;
2249    }
2250
2251    public getExpandAndCollapseInfo(currentNodeId: number) {
2252      return this.expandAndCollapseInfo.get(currentNodeId);
2253    }
2254
2255    public getLastDelayHighLightId() {
2256      return this.lastDelayHighLightId;
2257    }
2258
2259    public setLastDelayHighLightId() {
2260      this.ListNode.forEach((value, index) => {
2261        if (index == this.lastDelayHighLightIndex) {
2262          this.lastDelayHighLightId = value.getCurrentNodeId();
2263        }
2264      })
2265    }
2266
2267    public setLastPassId(lastPassId: number) {
2268      this.lastPassId = lastPassId;
2269    }
2270
2271    public setLastDelayHighLightIndex(lastDelayHighLightIndex) {
2272      this.lastDelayHighLightIndex = lastDelayHighLightIndex;
2273    }
2274
2275    /*
2276   * Alter the current node location to a needful position.
2277   * 1.Create an array named 'dragNodeParam' to store dragging node information.
2278   * 2.Delete the dragging node from the tree.
2279   * 3.Add the dragging node to the tree.
2280   */
2281    public alterDragNode(rearParentNodeId: number, rearCurrentNodeId: number, insertNodeInfo: NodeInfo,
2282                         dragParentNodeId: number, dragCurrentNodeId: number, frontNodeInfoItem: NodeInfo) {
2283      let dragNodeParam: { parentId: number, currentId: number, data: any }[] = [];
2284      let parentNodeId: number = rearParentNodeId;
2285      let currentNodeId: number = dragCurrentNodeId;
2286      let nodeParam = frontNodeInfoItem.getNodeInfoData();
2287      let nodeInfo: NodeInfo = null;
2288      let nodeInfoNode: NodeItem = frontNodeInfoItem.getNodeInfoNode();
2289      let isHighLight : boolean = false;
2290      let insertChildIndex: number = this.INITIAL_INVALID_VALUE;
2291      let currentChildIndex: number = this.INITIAL_INVALID_VALUE;
2292      let isDownFlag: boolean = this.flag == Flag.DOWN_FLAG ? true : false;
2293
2294      currentChildIndex = this.getChildIndex(dragParentNodeId, dragCurrentNodeId);
2295      insertChildIndex = this.getChildIndex(rearParentNodeId, rearCurrentNodeId);
2296
2297      if (rearParentNodeId != dragParentNodeId) {
2298        insertChildIndex = isDownFlag ? insertChildIndex + 1 : insertChildIndex;
2299      } else {
2300        if (insertChildIndex > currentChildIndex) {
2301          insertChildIndex = isDownFlag ? insertChildIndex : insertChildIndex - 1;
2302        } else {
2303          insertChildIndex = isDownFlag ? insertChildIndex + 1 : insertChildIndex;
2304        }
2305      }
2306
2307      for (let i = 0; i < this.listNode.length; i++) {
2308        if (this.listNode[i].getCurrentNodeId() == rearCurrentNodeId) {
2309          isHighLight = this.listNode[i].getIsHighLight();
2310          if (this.flag == Flag.DOWN_FLAG && this.expandAndCollapseInfo.get(rearCurrentNodeId) == NodeStatus.Expand) {
2311            parentNodeId = rearCurrentNodeId;
2312            insertChildIndex = 0;
2313          } else if (this.flag == Flag.UP_FLAG && this.expandAndCollapseInfo.get(rearCurrentNodeId) == NodeStatus.Expand
2314            && this.listNode[i].getCanShowFlagLine() == false) {
2315            parentNodeId = rearCurrentNodeId;
2316            insertChildIndex = 0;
2317          } else if (isHighLight) {
2318            parentNodeId = rearCurrentNodeId;
2319            insertChildIndex = 0;
2320          }
2321          break;
2322        }
2323      }
2324
2325      let callbackParam: CallbackParam = { currentNodeId: currentNodeId, parentNodeId: parentNodeId, childIndex: insertChildIndex }
2326
2327      /* export inner drag node Id. */
2328      this.appEventBus.emit(TreeListenType.NODE_MOVE, [callbackParam]);
2329
2330      /* To store dragging node information by the array named 'dragNodeParam'. */
2331      dragNodeParam.push({parentId: parentNodeId, currentId: currentNodeId, data: nodeParam});
2332
2333      let oneself = null,
2334        callback = function(node, listNode): boolean {
2335          if (node) {
2336            oneself = node;
2337            parentNodeId = oneself.parentNodeId;
2338            currentNodeId = oneself.currentNodeId;
2339            for (let i = 0; i < listNode.length; i++) {
2340              if (listNode[i].getNodeCurrentNodeId() == currentNodeId) {
2341                nodeInfo = listNode[i];
2342                break;
2343              }
2344            }
2345            nodeParam = nodeInfo.getNodeInfoData();
2346            if (parentNodeId != dragParentNodeId) {
2347              dragNodeParam.push({parentId: parentNodeId, currentId: currentNodeId, data: nodeParam});
2348            }
2349            return false;
2350          }
2351          return false;
2352        }
2353      this.listNodeUtils.dragTraverseNodeDF(callback, nodeInfoNode, this.listNode);
2354
2355      /* Delete the dragging node from the tree. */
2356      this.listNodeUtils.removeNode(dragCurrentNodeId, dragParentNodeId, this.listNodeUtils.traverseNodeBF);
2357
2358      /*
2359     * Add the dragging node to the tree
2360     * 1.The first dragging node is added singly, because it needs to distinguish the position to insert
2361     *
2362     * Add first node.
2363     */
2364      let insertCurrentNodeId: number = rearCurrentNodeId;
2365      let isAfter: boolean = isDownFlag;
2366      if (this.expandAndCollapseInfo.get(rearCurrentNodeId) == NodeStatus.Expand) {
2367        isAfter = false;
2368        this.listNode.forEach((value) => {
2369          if (value.getCurrentNodeId() == rearCurrentNodeId && value.getCanShowFlagLine() == false) {
2370            if (value.getNodeInfoNode().children.length) {
2371              insertCurrentNodeId = value.getNodeInfoNode().children[0].currentNodeId;
2372            } else {
2373              insertCurrentNodeId = this.INITIAL_INVALID_VALUE;
2374            }
2375          }
2376        })
2377      } else if (!this.expandAndCollapseInfo.get(rearCurrentNodeId) && isHighLight) {
2378        this.expandAndCollapseInfo.set(rearCurrentNodeId, NodeStatus.Expand);
2379      }
2380
2381      this.listNodeUtils.addDragNode(dragNodeParam[0].parentId, dragNodeParam[0].currentId, insertCurrentNodeId,
2382        isAfter, dragNodeParam[0].data);
2383
2384      /* Add remaining node. */
2385      for (let j = 1; j < dragNodeParam.length; j++) {
2386        this.listNodeUtils.addNode(dragNodeParam[j].parentId, dragNodeParam[j].currentId, dragNodeParam[j].data);
2387      }
2388
2389      /* Update node data and reload the array named 'listNode'. */
2390      for (let i = 0; i < this.listNode.length; i++) {
2391        if (this.listNode[i].getCurrentNodeId() == dragParentNodeId) {
2392          if (this.listNode[i].getNodeInfoNode().getNodeItem().imageCollapse == null) {
2393            this.listNode[i].handleImageCollapseAfterAddNode(false);
2394            this.expandAndCollapseInfo.delete(dragParentNodeId);
2395            break;
2396          }
2397        }
2398      }
2399      let tmp: NodeInfo[] = [...this.listNode];
2400      this.reloadListNode(this.listNodeUtils, tmp);
2401
2402    }
2403
2404    /*
2405   * Reload the array named 'listNode'
2406   * @param listNodeUtils
2407   * @param tmp
2408   */
2409    public reloadListNode(listNodeUtils: ListNodeUtils, tmp: NodeInfo[]) {
2410      let index = 0;
2411      this.listNode = [];
2412      this.listNodeUtils = listNodeUtils;
2413      this.loadedNodeIdAndIndexMap.clear();
2414      this.listNodeUtils.traverseNodeDF((node: NodeItem): boolean => {
2415        if (node.currentNodeId >= 0) {
2416          var nodeInfo: NodeInfo = new NodeInfo(node);
2417          this.listNode.push(nodeInfo);
2418
2419          if (this.expandAndCollapseInfo.get(node.currentNodeId) == NodeStatus.Expand) {
2420            nodeInfo.getNodeItem().imageCollapse.changeImageCollapseSource(NodeStatus.Expand);
2421          } else if (this.expandAndCollapseInfo.get(node.currentNodeId) == NodeStatus.Collapse) {
2422            nodeInfo.getNodeItem().imageCollapse.changeImageCollapseSource(NodeStatus.Collapse);
2423          }
2424
2425          for (let i = 0; i < tmp.length; i++){
2426            if (tmp[i].getCurrentNodeId() == nodeInfo.getCurrentNodeId()) {
2427              nodeInfo.setNodeIsShow(tmp[i].getNodeIsShow());
2428              nodeInfo.setListItemHeight(tmp[i].getListItemHeight());
2429              if (nodeInfo.getNodeItem().mainTitleNode && nodeInfo.getIsShowTitle()) {
2430                nodeInfo.getNodeItem().mainTitleNode.title = tmp[i].getNodeItem().mainTitleNode.title;
2431              }
2432              break;
2433            }
2434          }
2435          if (nodeInfo.getNodeIsShow()) {
2436            this.loadedNodeIdAndIndexMap.set(nodeInfo.getCurrentNodeId(), index++);
2437          }
2438        }
2439        return false;
2440      });
2441    }
2442
2443    public getFlagLine() {
2444      return this.FLAG_LINE;
2445    }
2446
2447    public getVisibility(nodeInfo: NodeInfo): any {
2448      let lastShowIndex: number = this.loadedNodeIdAndIndexMap.get(nodeInfo.getCurrentNodeId()) - 1;
2449      if (lastShowIndex > this.INITIAL_INVALID_VALUE) {
2450        let lastNodeInfo: NodeInfo = this.getData(lastShowIndex);
2451        return (nodeInfo.getCanShowFlagLine() == true && !nodeInfo.getIsHighLight() && !lastNodeInfo.getIsHighLight()) ?
2452        Visibility.Visible : Visibility.Hidden;
2453      } else {
2454        return (nodeInfo.getCanShowFlagLine() == true && !nodeInfo.getIsHighLight()) ?
2455        Visibility.Visible : Visibility.Hidden;
2456      }
2457    }
2458
2459    public getSubTitlePara() {
2460      return this.subTitle;
2461    }
2462
2463    public getIsFolder(nodeId: number) {
2464      if (this.loadedNodeIdAndIndexMap.has(nodeId)) {
2465        return this.getData(this.loadedNodeIdAndIndexMap.get(nodeId)).getIsFolder();
2466      }
2467      return false;
2468    }
2469
2470    public getSubTitleFontColor(isHighLight: boolean) {
2471      return isHighLight ? this.subTitle.highLightFontColor : this.subTitle.normalFontColor;
2472    }
2473
2474    private getChildIndex(rearParentNodeId: number, rearCurrentNodeId: number) {
2475      let insertChildIndex: number = this.INITIAL_INVALID_VALUE;
2476      this.listNodeUtils.traverseNodeBF(function(node): boolean {
2477        if (node.getCurrentNodeId() == rearParentNodeId) {
2478          node.children.forEach((value, index) => {
2479            if (value.getCurrentNodeId() == rearCurrentNodeId) {
2480              insertChildIndex = index;
2481            }
2482          })
2483          return true;
2484        }
2485        return false;
2486      });
2487      return insertChildIndex;
2488    }
2489
2490    public setCurrentFocusNodeId(focusNodeId: number) {
2491      this.currentFocusNodeId = focusNodeId;
2492    }
2493
2494    public getCurrentFocusNodeId(): number {
2495      return this.currentFocusNodeId;
2496    }
2497
2498    public setLastFocusNodeId(focusNodeId: number) {
2499      this.lastFocusNodeId = focusNodeId;
2500    }
2501
2502    public getLastFocusNodeId(): number {
2503      return this.lastFocusNodeId;
2504    }
2505
2506    public getAddFocusNodeId(): number {
2507      return this.addFocusNodeId;
2508    }
2509
2510    public setFlag(flag: Flag) {
2511      this.flag = flag;
2512    }
2513  }
2514
2515  /* nodeId to find index */
2516  function findCurrentNodeIndex(this, currentNodeId: number): number {
2517    let thisIndex: number = 0;
2518    this.listNodeDataSource.ListNode.forEach(function (value, index) {
2519      if (value.getNodeCurrentNodeId() == currentNodeId) {
2520        thisIndex = index;
2521      }
2522    })
2523    return thisIndex;
2524  }
2525
2526  /**
2527   * Tree view control, which is created by using the TreeController class.
2528   *
2529   * When you create this component, you must initialize the listNodeDataSource.
2530   * You can run the listTreeViewWidth command to set the width of the component.
2531   * The default width is 200vp.
2532   *
2533   * @since 10
2534   */
2535  @Component
2536  export struct TreeView {
2537    listNodeDataSource: ListNodeDataSource;
2538    treeController: TreeController;
2539    @State dropSelectedIndex: number = 0;
2540    @BuilderParam private listTreeViewMenu: () => void  = null;
2541    @Prop listTreeViewWidth: string | number;
2542    @Prop listTreeViewHeight: number | string;
2543    private readonly MAX_CN_LENGTH: number = 254;
2544    private readonly MAX_EN_LENGTH: number = 255;
2545    private readonly INITIAL_INVALID_VALUE = -1;
2546    private readonly MAX_TOUCH_DOWN_COUNT = 0;
2547    private isMultiPress: boolean = false;
2548    private touchDownCount: number = this.INITIAL_INVALID_VALUE;
2549    private appEventBus: TreeListener = TreeListenerManager.getInstance().getTreeListener();
2550    private readonly itemPadding: { left: Resource, right: Resource, top: Resource, bottom: Resource } =
2551      { left: $r('sys.float.ohos_id_card_margin_middle'),
2552        right: $r('sys.float.ohos_id_card_margin_middle'),
2553        top: $r('sys.float.ohos_id_text_margin_vertical'),
2554        bottom: $r('sys.float.ohos_id_text_margin_vertical')};
2555    /* { left: '12vp', right: '12vp', top: '2vp', bottom: '2vp' } */
2556    private readonly textInputPadding:  { left : string, right: string, top: string, bottom: string } =
2557      { left : '0vp', right: '0vp', top: '0vp', bottom: '0vp'}
2558    aboutToAppear(): void  {
2559      this.listTreeViewWidth = (this.listTreeViewWidth === undefined) ? 200 : this.listTreeViewWidth;
2560      this.listNodeDataSource = this.treeController.getListNodeDataSource();
2561    }
2562
2563    private checkInvalidPattern(title: string): boolean {
2564      let pattern = /[\\\/:*?"<>|]/;
2565      return pattern.test(title)
2566    }
2567
2568    private checkIsAllCN(title: string): boolean {
2569      let pattern = /^[\u4e00-\u9fa5]+$/;
2570      return pattern.test(title)
2571    }
2572
2573    @Builder popupForShowTitle(text: string | Resource, backgroundColor: Resource, fontColor: Resource ) {
2574      Row() {
2575        Text(text).fontSize($r('sys.float.ohos_id_text_size_body2')).fontWeight('regular').fontColor(fontColor)
2576      }.backgroundColor(backgroundColor)
2577      .border({radius: $r('sys.float.ohos_id_elements_margin_horizontal_l')})
2578      .padding({left: $r('sys.float.ohos_id_elements_margin_horizontal_l'),
2579        right: $r('sys.float.ohos_id_elements_margin_horizontal_l'),
2580        top: $r('sys.float.ohos_id_card_margin_middle'),
2581        bottom: $r('sys.float.ohos_id_card_margin_middle')})
2582    }
2583
2584    @Builder builder() {
2585      this.listTreeViewMenu()
2586    }
2587
2588    /* Set the popup of dragging node. */
2589    @Builder draggingPopup(item: NodeInfo) {
2590      Row() {
2591        if (item.getNodeItem().imageNode) {
2592          Row() {
2593            Image(item.getNodeItem().imageNode.normalSource)
2594              .objectFit(ImageFit.Contain)
2595              .height(item.getNodeItem().imageNode.itemHeight)
2596              .width(item.getNodeItem().imageNode.itemWidth)
2597              .opacity(this.listNodeDataSource.getDragPopupPara().imageOpacity)
2598          }
2599          .backgroundColor(COLOR_IMAGE_ROW)
2600          .margin({ right: item.getNodeItem().imageNode.itemRightMargin })
2601          .height(item.getNodeItem().imageNode.itemHeight)
2602          .width(item.getNodeItem().imageNode.itemWidth)
2603        }
2604        Row() {
2605          if (item.getNodeItem().mainTitleNode && item.getIsShowTitle()) {
2606            Text(item.getNodeItem().mainTitleNode.title)
2607              .maxLines(1)
2608              .fontSize(item.getNodeItem().mainTitleNode.size)
2609              .fontColor(this.listNodeDataSource.getDragPopupPara().fontColor)
2610              .fontWeight(this.listNodeDataSource.getDragPopupPara().fontWeight)
2611              .textOverflow({ overflow: TextOverflow.Ellipsis })
2612          }
2613        }
2614        .constraintSize({
2615          minWidth: item.getNodeItem().imageNode ?
2616          this.listNodeDataSource.getDragPopupPara().textConstraintSize.minWidth1 :
2617          this.listNodeDataSource.getDragPopupPara().textConstraintSize.minWidth2,
2618          maxWidth: item.getNodeItem().imageNode ?
2619          this.listNodeDataSource.getDragPopupPara().textConstraintSize.maxWidth1 :
2620          this.listNodeDataSource.getDragPopupPara().textConstraintSize.maxWidth2 })
2621      }
2622      .constraintSize({ minWidth: this.listNodeDataSource.getDragPopupPara().floorConstraintSize.minWidth,
2623        maxWidth: this.listNodeDataSource.getDragPopupPara().floorConstraintSize.maxWidth })
2624      .height(this.listNodeDataSource.getDragPopupPara().height)
2625      .backgroundColor(this.listNodeDataSource.getDragPopupPara().backgroundColor)
2626      .padding({ left: this.listNodeDataSource.getDragPopupPara().padding.left,
2627        right: this.listNodeDataSource.getDragPopupPara().padding.right })
2628      .shadow({ radius: this.listNodeDataSource.getDragPopupPara().shadow.radius,
2629        color: this.listNodeDataSource.getDragPopupPara().shadow.color,
2630        offsetY: this.listNodeDataSource.getDragPopupPara().shadow.offsetY })
2631      .borderRadius(this.listNodeDataSource.getDragPopupPara().borderRadius) // need to doubleCheck.
2632    }
2633
2634    build() {
2635      List({ }) {
2636        LazyForEach(this.listNodeDataSource, (item: NodeInfo) => {
2637          ListItem() {
2638            if (item.getNodeIsShow()) {
2639              Column() {
2640                Divider()
2641                  .height(this.listNodeDataSource.getFlagLine().flagLineHeight)
2642                  .color(this.listNodeDataSource.getFlagLine().flagLineColor)
2643                  .visibility(this.listNodeDataSource.getVisibility(item))
2644                  .lineCap(LineCapStyle.Round)
2645                  .margin({ left: item.getFlagLineLeftMargin() })
2646                  .markAnchor({ x: this.listNodeDataSource.getFlagLine().xOffset,
2647                    y: this.listNodeDataSource.getFlagLine().yTopOffset })
2648                Row({}) {
2649                  Row({}) {
2650                    if (item.getNodeItem().imageNode) {
2651                      Row() {
2652                        Image(item.getNodeItem().imageNode.source)
2653                          .objectFit(ImageFit.Contain)
2654                          .height(item.getNodeItem().imageNode.itemHeight)
2655                          .width(item.getNodeItem().imageNode.itemWidth)
2656                          .opacity(!item.getIsSelected() && !item.getIsHighLight() ?
2657                          item.getNodeItem().imageNode.opacity : item.getNodeItem().imageNode.noOpacity)
2658                          .focusable(item.getNodeItem().mainTitleNode != null ? false : true)
2659                          .onFocus(() => {
2660                            let that = this;
2661                            that.listNodeDataSource.handleEvent(Event.FOCUS,
2662                              findCurrentNodeIndex.call(that, item.getNodeCurrentNodeId()));
2663                          })
2664                          .onBlur(() => {
2665                            let that = this;
2666                            that.listNodeDataSource.handleEvent(Event.BLUR,
2667                              findCurrentNodeIndex.call(that, item.getNodeCurrentNodeId()));
2668                          })
2669                      }
2670                      .backgroundColor(COLOR_IMAGE_ROW)
2671                      .margin({ right: item.getNodeItem().imageNode.itemRightMargin })
2672                      .height(item.getNodeItem().imageNode.itemHeight)
2673                      .width(item.getNodeItem().imageNode.itemWidth)
2674                    }
2675                    Row() {
2676                      if (item.getNodeItem().mainTitleNode && item.getIsShowTitle()) {
2677                        Text(item.getNodeItem().mainTitleNode.title)
2678                          .maxLines(1) // max line
2679                          .fontSize(item.getNodeItem().mainTitleNode.size)
2680                          .fontColor(item.getNodeItem().mainTitleNode.color)
2681                          .margin({ right: item.getNodeItem().mainTitleNode.itemRightMargin })
2682                          .textOverflow({ overflow: TextOverflow.Ellipsis })
2683                          .fontWeight(item.getNodeItem().mainTitleNode.weight)
2684                          .focusable(true)
2685                          .onFocus(() => {
2686                            let that = this;
2687                            this.listNodeDataSource.handleEvent(Event.FOCUS,
2688                              findCurrentNodeIndex.call(that, item.getNodeCurrentNodeId()));
2689                          })
2690                          .onBlur(() => {
2691                            let that = this;
2692                            that.listNodeDataSource.handleEvent(Event.BLUR,
2693                              findCurrentNodeIndex.call(that, item.getNodeCurrentNodeId()));
2694                          })
2695                      }
2696                      if (item.getNodeItem().mainTitleNode &&
2697                      item.getNodeItem().inputText &&
2698                      item.getIsShowInputText()) {
2699                        Row() {
2700                          TextInput({ text: item.getNodeItem().mainTitleNode.title })
2701                            .height(item.getNodeItem().inputText.itemHeight)
2702                            .fontSize(item.getNodeItem().inputText.size)
2703                            .fontColor(item.getNodeItem().inputText.color)
2704                            .borderRadius(item.getNodeItem().inputText.borderRadius)
2705                            .backgroundColor(item.getNodeItem().inputText.backgroundColor)
2706                            .enterKeyType(EnterKeyType.Done)
2707                            .padding({ left: this.textInputPadding.left, right: this.textInputPadding.right,
2708                              top: this.textInputPadding.top, bottom: this.textInputPadding.bottom })
2709                            .onChange((value: string) => {
2710                              let that = this;
2711                              var thisIndex = findCurrentNodeIndex.call(that, item.getNodeCurrentNodeId());
2712                              let res: string = '';
2713                              let isInvalidError: boolean = false;
2714                              let isLengthError: boolean = false;
2715                              if (that.checkInvalidPattern(value)) {
2716                                for (let i = 0; i < value.length; i++) {
2717                                  if (!that.checkInvalidPattern(value[i])) {
2718                                    res += value[i];
2719                                  }
2720                                }
2721                                isInvalidError = true;
2722                                that.listNodeDataSource.setPopUpInfo(PopUpType.WARNINGS,
2723                                  InputError.INVALID_ERROR, true, thisIndex);
2724                              } else {
2725                                res = value;
2726                                isInvalidError = false;
2727                                that.listNodeDataSource.setPopUpInfo(PopUpType.WARNINGS,
2728                                  InputError.INVALID_ERROR, false, thisIndex);
2729                              }
2730                              if ((that.checkIsAllCN(res) && res.length > this.MAX_CN_LENGTH) ||
2731                                (!that.checkIsAllCN(res) && res.length > this.MAX_EN_LENGTH)) {
2732                                res = that.checkIsAllCN(res) ?
2733                                res.substr(0, this.MAX_CN_LENGTH) : res.substr(0, this.MAX_EN_LENGTH);
2734                                isLengthError = true;
2735                                that.listNodeDataSource.setPopUpInfo(PopUpType.WARNINGS,
2736                                  InputError.LENGTH_ERROR, true, thisIndex);
2737                              } else {
2738                                isLengthError = false;
2739                              }
2740                              if (!isLengthError && !isInvalidError) {
2741                                that.listNodeDataSource.setPopUpInfo(PopUpType.WARNINGS,
2742                                  InputError.LENGTH_ERROR, false, thisIndex);
2743                                that.listNodeDataSource.setMainTitleNameOnEdit(thisIndex, res);
2744                              }
2745
2746                            })
2747                            .onSubmit((enterKey: EnterKeyType) => {
2748                              let that = this;
2749                              var thisIndex = findCurrentNodeIndex.call(that, item.getNodeCurrentNodeId());
2750                              that.listNodeDataSource.setPopUpInfo(PopUpType.WARNINGS, InputError.NONE, false, thisIndex);
2751                              that.listNodeDataSource.setItemVisibilityOnEdit(thisIndex, MenuOperation.COMMIT_NODE);
2752                            })
2753                        }.backgroundColor(item.getNodeItem().inputText.backgroundColor)
2754                        .borderRadius(item.getNodeItem().inputText.borderRadius)
2755                        .margin({ right: item.getNodeItem().inputText.itemRightMargin })
2756                      }
2757                      Blank()
2758                    }.layoutWeight(1)
2759
2760                    if (this.listNodeDataSource.hasSubtitle(item.getCurrentNodeId())) {
2761                      Row() {
2762                        Text(this.listNodeDataSource.getSubtitle(item.getCurrentNodeId()))
2763                          .fontSize(this.listNodeDataSource.getSubTitlePara().fontSize)
2764                          .fontColor(this.listNodeDataSource.getSubTitleFontColor(item.getIsHighLight() || item.getIsModify()))
2765                          .fontWeight(this.listNodeDataSource.getSubTitlePara().fontWeight)
2766                      }
2767                      .margin({ left: this.listNodeDataSource.getSubTitlePara().margin.left,
2768                        right: item.getNodeItem().imageCollapse ?
2769                          0 : this.listNodeDataSource.getSubTitlePara().margin.right })
2770                    }
2771
2772                    if (item.getNodeItem().imageCollapse) {
2773                      Row() {
2774                        Image(item.getNodeItem().imageCollapse.collapseSource)
2775                          .fillColor(item.getNodeItem().imageCollapse.isCollapse ? COLOR_IMAGE_ROW : COLOR_IMAGE_EDIT)
2776                          .align(Alignment.End)
2777                          .objectFit(ImageFit.Contain)
2778                          .height(item.getNodeItem().imageCollapse.itemHeight)
2779                          .width(item.getNodeItem().imageCollapse.itemWidth)
2780                          .opacity(!item.getIsHighLight() ?
2781                          item.getNodeItem().imageCollapse.opacity : item.getNodeItem().imageCollapse.noOpacity)
2782                          .onTouch((event: TouchEvent) => {
2783                            if (event.type === TouchType.Down) {
2784                              let that = this;
2785                              that.listNodeDataSource.expandAndCollapseNode(
2786                                findCurrentNodeIndex.call(that, item.getNodeCurrentNodeId()));
2787                              this.listNodeDataSource.setCurrentFocusNodeId(item.getCurrentNodeId());
2788                            }
2789                            event.stopPropagation();
2790                          })
2791                      }
2792                      .backgroundColor(COLOR_IMAGE_ROW)
2793                      .height(item.getNodeItem().imageCollapse.itemHeight)
2794                      .width(item.getNodeItem().imageCollapse.itemWidth)
2795                    }
2796                  }
2797                  .width('100%')
2798                  .onTouch((event: TouchEvent) => {
2799                    let that = this;
2800                    let index = findCurrentNodeIndex.call(that, item.getNodeCurrentNodeId());
2801                    let currentId = item.getNodeCurrentNodeId();
2802                    that.listNodeDataSource.setClickIndex(index);
2803                    if (event.type === TouchType.Down) {
2804                      this.touchDownCount ++;
2805                      this.isMultiPress = this.touchDownCount > this.MAX_TOUCH_DOWN_COUNT ? true : false;
2806                      if (!this.listNodeDataSource.getIsTouchDown()) {
2807                        that.listNodeDataSource.handleEvent(Event.TOUCH_DOWN, index);
2808                      }
2809                    }
2810                    if (event.type === TouchType.Up) {
2811                      this.touchDownCount--;
2812                      if (this.touchDownCount < this.MAX_TOUCH_DOWN_COUNT) {
2813                        this.isMultiPress = false;
2814                      }
2815                      let callbackParam = { currentNodeId: currentId }
2816                      this.appEventBus.emit(TreeListenType.NODE_CLICK, [callbackParam]);
2817                      that.listNodeDataSource.handleEvent(Event.TOUCH_UP, index);
2818                    }
2819                  })
2820                  .onHover((isHover: boolean) => {
2821                    let that = this;
2822                    let index = findCurrentNodeIndex.call(that, item.getNodeCurrentNodeId())
2823                    if (isHover) {
2824                      that.listNodeDataSource.handleEvent(Event.HOVER, index);
2825                    } else {
2826                      if (!that.listNodeDataSource.getIsTouchDown()) {
2827                        that.listNodeDataSource.handleEvent(Event.HOVER_OVER, index);
2828                      }
2829                    }
2830                  })
2831                  .gesture(
2832                    TapGesture({ count: 2 }) // doubleClick
2833                      .onAction((event: GestureEvent) => {
2834                        let that = this;
2835                        that.listNodeDataSource.expandAndCollapseNode(
2836                          findCurrentNodeIndex.call(that, item.getNodeCurrentNodeId()));
2837                      })
2838                  )
2839                  .height(item.getNodeHeight())
2840                  .padding({ left: item.getNodeLeftPadding() })
2841                  /* backgroundColor when editing and in other states. */
2842                  .backgroundColor((item.getNodeItem().mainTitleNode && item.getNodeItem().inputText &&
2843                  item.getIsShowInputText()) ? item.getNodeItem().inputText.editColor : item.getNodeColor())
2844                  .border({
2845                    width: item.getNodeBorder().borderWidth,
2846                    color: item.getNodeBorder().borderColor,
2847                    radius: item.getNodeBorder().borderRadius
2848                  })
2849                  .bindContextMenu(this.builder, ResponseType.RightClick)
2850                }
2851                .opacity(this.listNodeDataSource.getListItemOpacity(item))
2852                .markAnchor({ x: this.listNodeDataSource.getFlagLine().xOffset,
2853                  y: this.listNodeDataSource.getFlagLine().yBasePlateOffset })
2854                if (item.getCanShowBottomFlagLine()) {
2855                  if (this.listNodeDataSource.getPassIndex() == this.listNodeDataSource.totalCount() - 1 &&
2856                    !item.getIsHighLight()) {
2857                    Divider()
2858                      .height(this.listNodeDataSource.getFlagLine().flagLineHeight)
2859                      .color(this.listNodeDataSource.getFlagLine().flagLineColor)
2860                      .visibility(Visibility.Visible)
2861                      .lineCap(LineCapStyle.Round)
2862                      .margin({ left: item.getFlagLineLeftMargin() })
2863                      .markAnchor({ x: this.listNodeDataSource.getFlagLine().xOffset,
2864                        y: this.listNodeDataSource.getFlagLine().yBottomOffset })
2865                  }
2866                }
2867              }
2868              .focusable(true)
2869              .onMouse((event: MouseEvent) => {
2870                let that = this;
2871                let thisIndex = findCurrentNodeIndex.call(that, item.getNodeCurrentNodeId());
2872                if (event.button == MouseButton.Right) {
2873                  that.listNodeDataSource.handleEvent(Event.MOUSE_BUTTON_RIGHT,
2874                    findCurrentNodeIndex.call(that, item.getNodeCurrentNodeId()));
2875                  this.listTreeViewMenu = item.getMenu();
2876                  that.listNodeDataSource.setClickIndex(thisIndex);
2877                  that.listNodeDataSource.setPopUpInfo(PopUpType.HINTS, InputError.NONE, false, thisIndex);
2878                  clearTimeout(item.getNodeItem().mainTitleNode.popUpTimeout);
2879                }
2880                event.stopPropagation();
2881              })
2882              .padding({ top: this.itemPadding.top, bottom: this.itemPadding.bottom })
2883              .bindPopup(item.getPopUpInfo().popUpIsShow, {
2884                builder: this.popupForShowTitle(item.getPopUpInfo().popUpText, item.getPopUpInfo().popUpColor,
2885                  item.getPopUpInfo().popUpTextColor),
2886                placement: Placement.BottomLeft,
2887                placementOnTop: false,
2888                popupColor: item.getPopUpInfo().popUpColor,
2889                autoCancel: true,
2890                enableArrow: item.getPopUpInfo().popUpEnableArrow
2891              })
2892            }
2893
2894          }
2895          .width('100%').height(item.getListItemHeight())
2896          .padding({ left: this.itemPadding.left, right: this.itemPadding.right})
2897          .align(Alignment.Start)
2898          .onDragStart((event: DragEvent, extraParams: string) => {
2899            if (this.listNodeDataSource.getIsDrag() || this.listNodeDataSource.getIsInnerDrag() || this.isMultiPress) {
2900              console.error('drag error, a item has been dragged');
2901              return;
2902            }
2903            this.dropSelectedIndex = JSON.parse(extraParams).selectedIndex;
2904            let currentNodeIndex = JSON.parse(extraParams).selectedIndex;
2905            let currentNodeInfo = this.listNodeDataSource.getData(currentNodeIndex);
2906            let currentItemNodeId = item.getNodeCurrentNodeId();
2907
2908            /* handle the situation of drag error, currentNodeIndex is not found in onDragStart. */
2909            if (currentNodeIndex >= this.listNodeDataSource.totalCount() || currentNodeIndex == undefined) {
2910              console.error('drag error, currentNodeIndex is not found in onDragStart');
2911              return;
2912            }
2913
2914            this.listNodeDataSource.setIsInnerDrag(true);
2915            this.listNodeDataSource.setIsDrag(true);
2916            this.listNodeDataSource.setCurrentNodeInfo(currentNodeInfo);
2917            this.listNodeDataSource.setDraggingCurrentNodeId(currentNodeInfo.getNodeCurrentNodeId());
2918            this.listNodeDataSource.setDraggingParentNodeId(currentNodeInfo.getNodeParentNodeId());
2919
2920            /* set the opacity of the dragging node. */
2921            let draggingNodeOpacity: number = DRAG_OPACITY;
2922            this.listNodeDataSource.setListItemOpacity(draggingNodeOpacity);
2923            this.listNodeDataSource.notifyDataChange(currentNodeIndex);
2924
2925            /*
2926           * handle the situation of drag is too fast,it attribute a fault to OH.
2927           * OH has Solved on real machine.
2928           */
2929            if (currentItemNodeId != currentNodeInfo.getNodeCurrentNodeId()) {
2930              console.error('drag is too fast,it attribute a fault to OH');
2931              this.listNodeDataSource.setIsDrag(false);
2932              return;
2933            }
2934            return this.draggingPopup(currentNodeInfo);
2935          })
2936        }, item => JSON.stringify(item))
2937      }.width(this.listTreeViewWidth).height(this.listTreeViewHeight)
2938
2939      /* Move the dragged node. */
2940      .onDragMove((event: DragEvent) => {
2941        if (this.isMultiPress) {
2942          console.error('drag error, a item has been dragged');
2943          return;
2944        }
2945        let nodeHeight: number = LIST_ITEM_HEIGHT;
2946
2947        /* flag the position of the focus on the node. */
2948        let flag: Flag = Math.floor(event.getY() / (nodeHeight / FLAG_NUMBER)) % FLAG_NUMBER ? Flag.DOWN_FLAG : Flag.UP_FLAG;
2949
2950        /* Record the node position to which the dragged node moves. */
2951        let index: number = Math.floor(event.getY() / nodeHeight);
2952
2953        /* Handle the situation where the focus(index) exceeds the list area. */
2954        let isOverBorder: boolean = false;
2955        if (index >= this.listNodeDataSource.totalCount()) {
2956          flag = Flag.DOWN_FLAG;
2957          index = this.listNodeDataSource.totalCount() - 1;
2958          this.listNodeDataSource.getData(index).setIsOverBorder(true);
2959          isOverBorder = true;
2960        } else {
2961          this.listNodeDataSource.getData(index).setIsOverBorder(false);
2962        }
2963
2964        let currentNodeInfo: NodeInfo = this.listNodeDataSource.getData(index);
2965        let currentNodeId: number = currentNodeInfo.getCurrentNodeId();
2966
2967        /*
2968       * handle a situation that "draggingCurrentNodeId" is parent of "insertNodeCurrentNodeId";
2969       * do not perform some functions.
2970       */
2971        if (index != this.listNodeDataSource.getLastPassIndex() && this.listNodeDataSource.getIsInnerDrag()) {
2972          let isParentNodeOfInsertNode: boolean = this.listNodeDataSource.getIsParentOfInsertNode(currentNodeId);
2973          if (isParentNodeOfInsertNode) {
2974            this.listNodeDataSource.setPassIndex(index);
2975            let that = this;
2976            this.listNodeDataSource.clearTimeOutAboutDelayHighLightAndExpand(findCurrentNodeIndex.call(that,
2977              currentNodeId));
2978            this.listNodeDataSource.setFlag(Flag.NONE);
2979            return;
2980          }
2981        }
2982        this.listNodeDataSource.setLastPassIndex(index);
2983
2984        /* Set the visibility of the flag line. */
2985        this.listNodeDataSource.setVisibility(flag, index, isOverBorder);
2986
2987        /* Automatically HighLight one second delay and expand after two second delay. */
2988        if (currentNodeId != this.listNodeDataSource.getDraggingCurrentNodeId()) {
2989          let that = this;
2990          this.listNodeDataSource.delayHighLightAndExpandNode(findCurrentNodeIndex.call(that, currentNodeId),
2991            currentNodeId, index);
2992        }
2993      })
2994
2995      /* DragEvent Enter. */
2996      .onDragEnter((event: DragEvent, extraParams: string) => {
2997        if (this.listNodeDataSource.getIsInnerDrag()) {
2998          this.listNodeDataSource.setIsDrag(true);
2999
3000          /* set the opacity of the dragging node. */
3001          let draggingNodeOpacity: number = DRAG_OPACITY;
3002          this.listNodeDataSource.setListItemOpacity(draggingNodeOpacity);
3003        }
3004      })
3005
3006      /* DragEvent Leave. */
3007      .onDragLeave((event: DragEvent, extraParams: string) => {
3008        this.listNodeDataSource.hideLastLine();
3009        this.listNodeDataSource.clearLastTimeoutHighLight();
3010        this.listNodeDataSource.clearLastTimeoutExpand();
3011        let draggingNodeOpacity: number = DRAG_OPACITY_NONE;
3012        this.listNodeDataSource.setListItemOpacity(draggingNodeOpacity);
3013        this.listNodeDataSource.setIsDrag(false);
3014        this.listNodeDataSource.notifyDataReload();
3015      })
3016
3017      /* DragEvent Drop. */
3018      .onDrop((event: DragEvent, extraParams: string) => {
3019        this.listNodeDataSource.clearLastTimeoutExpand();
3020        let draggingNodeOpacity: number = DRAG_OPACITY_NONE;
3021        this.listNodeDataSource.setListItemOpacity(draggingNodeOpacity);
3022        let insertNodeIndex: number = this.listNodeDataSource.getPassIndex();
3023        let currentNodeIndex: number = this.dropSelectedIndex;
3024
3025        if (currentNodeIndex - 1 > this.listNodeDataSource.totalCount() || currentNodeIndex == undefined) {
3026          console.error('drag error, currentNodeIndex is not found');
3027          this.listNodeDataSource.setIsDrag(false);
3028          return;
3029        }
3030
3031        if (insertNodeIndex == this.listNodeDataSource.totalCount()) {
3032          console.log('need to insert into the position of the last line, now insertNodeIndex = insertNodeIndex - 1');
3033          insertNodeIndex -= 1;
3034        }
3035
3036        let insertNodeInfo: NodeInfo = this.listNodeDataSource.getData(insertNodeIndex);
3037        let insertNodeCurrentNodeId: number = insertNodeInfo.getNodeCurrentNodeId();
3038
3039        /* outer node is move in. */
3040        if (!this.listNodeDataSource.getIsDrag() || !this.listNodeDataSource.getIsInnerDrag()) {
3041          this.listNodeDataSource.clearLastTimeoutHighLight();
3042          this.listNodeDataSource.setIsInnerDrag(false);
3043          this.listNodeDataSource.hideLastLine();
3044          this.listNodeDataSource.initialParameterAboutDelayHighLightAndExpandIndex();
3045          this.listNodeDataSource.refreshSubtitle(insertNodeCurrentNodeId);
3046          this.listNodeDataSource.notifyDataReload();
3047          return;
3048        }
3049
3050        let currentNodeInfo: NodeInfo = this.listNodeDataSource.getCurrentNodeInfo();
3051        let insertNodeParentNodeId: number = insertNodeInfo.getNodeParentNodeId();
3052        let draggingCurrentNodeId: number = this.listNodeDataSource.getDraggingCurrentNodeId();
3053        let draggingParentNodeId: number = this.listNodeDataSource.getDraggingParentNodeId();
3054
3055        /*
3056       * handle a situation that "draggingCurrentNodeId" is parent of "insertNodeCurrentNodeId".
3057       * drag is fail.
3058       */
3059        let isParentNodeOfInsertNode: boolean = this.listNodeDataSource.getIsParentOfInsertNode(insertNodeCurrentNodeId);
3060        if (isParentNodeOfInsertNode) {
3061          this.listNodeDataSource.clearLastTimeoutHighLight();
3062          this.listNodeDataSource.setIsInnerDrag(false);
3063          this.listNodeDataSource.hideLastLine();
3064          this.listNodeDataSource.notifyDataChange(insertNodeIndex);
3065          this.listNodeDataSource.initialParameterAboutDelayHighLightAndExpandIndex();
3066          this.listNodeDataSource.setIsDrag(false);
3067
3068          /* set the position of focus. */
3069          let that = this;
3070          let currentFocusIndex: number = findCurrentNodeIndex.call(that, draggingCurrentNodeId);
3071          this.listNodeDataSource.setClickIndex(currentFocusIndex);
3072          this.listNodeDataSource.handleEvent(Event.DRAG, currentFocusIndex);
3073          return;
3074        }
3075
3076        /* Collapse drag node. */
3077        if (this.listNodeDataSource.getExpandAndCollapseInfo(draggingCurrentNodeId) == NodeStatus.Expand) {
3078          let that = this;
3079          this.listNodeDataSource.expandAndCollapseNode(
3080            findCurrentNodeIndex.call(that, draggingCurrentNodeId));
3081        }
3082
3083        /* Expand insert node. */
3084        if (this.listNodeDataSource.getExpandAndCollapseInfo(insertNodeCurrentNodeId) == NodeStatus.Collapse) {
3085          let that = this;
3086          let currentIndex: number = findCurrentNodeIndex.call(that, insertNodeCurrentNodeId);
3087          if (this.listNodeDataSource.ListNode[currentIndex].getIsHighLight()) {
3088            this.listNodeDataSource.expandAndCollapseNode(currentIndex);
3089          }
3090        }
3091
3092        /* alter dragNode. */
3093        this.listNodeDataSource.setLastDelayHighLightId();
3094        if (draggingCurrentNodeId != insertNodeCurrentNodeId) {
3095          this.listNodeDataSource.alterDragNode(insertNodeParentNodeId, insertNodeCurrentNodeId, insertNodeInfo,
3096            draggingParentNodeId, draggingCurrentNodeId, currentNodeInfo);
3097          this.listNodeDataSource.hideLastLine();
3098        } else {
3099          /*the position of dragNode is equal with the position of insertNode. */
3100          this.listNodeDataSource.hideLastLine();
3101          this.listNodeDataSource.setLastPassId(draggingCurrentNodeId);
3102          this.listNodeDataSource.hideLastLine();
3103        }
3104
3105        let that = this;
3106        let lastDelayHighLightIndex: number = findCurrentNodeIndex.call(that,
3107          this.listNodeDataSource.getLastDelayHighLightId());
3108        this.listNodeDataSource.setLastDelayHighLightIndex(lastDelayHighLightIndex);
3109        this.listNodeDataSource.clearLastTimeoutHighLight();
3110        this.listNodeDataSource.initialParameterAboutDelayHighLightAndExpandIndex();
3111        this.listNodeDataSource.setIsDrag(false);
3112
3113        /* set the position of focus. */
3114        let currentFocusIndex: number = findCurrentNodeIndex.call(that, draggingCurrentNodeId);
3115        this.listNodeDataSource.setClickIndex(currentFocusIndex);
3116        this.listNodeDataSource.handleEvent(Event.DRAG, currentFocusIndex);
3117
3118        /* innerDrag is over. */
3119        this.listNodeDataSource.setIsInnerDrag(false);
3120        this.listNodeDataSource.notifyDataReload();
3121      })
3122    }
3123  }
3124
3125  // Declare NodeParam
3126  export interface NodeParam {
3127    parentNodeId?: number,
3128    currentNodeId?: number,
3129    isFolder?: boolean,
3130    icon?: Resource,
3131    selectedIcon?: Resource,
3132    editIcon? : Resource,
3133    primaryTitle?: string,
3134    secondaryTitle?: number | string,
3135    menu?: () => void,
3136  }
3137
3138
3139  // Declare CallbackParam
3140  export interface CallbackParam {
3141    currentNodeId: number,
3142    parentNodeId?: number,
3143    childIndex?: number,
3144  }
3145
3146  /**
3147   * Create a tree view control proxy class to generate a tree view.
3148   *
3149   * @since 10
3150   */
3151  export class TreeController {
3152    readonly ROOT_NODE_ID: number = -1;
3153    private nodeIdList: number[] = [];
3154    private listNodeUtils : ListNodeUtils = new ListNodeUtils();
3155    private listNodeDataSource : ListNodeDataSource = new ListNodeDataSource();
3156
3157    /**
3158     * After the addNode interface is invoked,
3159     * this interface is used to obtain the initialization data of
3160     * the tree view component for creating a tree view component.
3161     *
3162     * @return ListNodeDataSource Obtains the initialization data of the tree view component.
3163     *
3164     * @since 10
3165     */
3166    public getListNodeDataSource(): ListNodeDataSource {
3167      return this.listNodeDataSource;
3168    }
3169
3170    /**
3171     * Obtains the subNode information of the currently clicked node.
3172     *
3173     * @return Array Returns an array that stores the configuration information of each node.
3174     * If there is no child node, an empty array is returned.
3175     *
3176     * @since 10
3177     */
3178    public getClickNodeChildrenInfo(): Array<{ itemId: number, itemIcon: Resource, itemTitle: string,
3179    isFolder: boolean }> {
3180      let clickNodeId = this.listNodeDataSource.getClickNodeId();
3181      return this.listNodeUtils.getClickNodeChildrenInfo(clickNodeId);
3182    }
3183
3184    public getChildrenId(): Array<number> {
3185      let clickNodeId = this.listNodeDataSource.getClickNodeId();
3186      return this.listNodeUtils.getClickChildId(clickNodeId);
3187    }
3188
3189    /**
3190     * Delete a node.
3191     *
3192     * Register an ON_ITEM_DELETE callback through the EventBus mechanism to obtain the IDs of all deleted nodes.
3193     *
3194     * @since 10
3195     */
3196    public removeNode(): void {
3197      let clickNodeId = this.listNodeDataSource.getClickNodeId();
3198      let parentNodeId = this.listNodeUtils.findParentNodeId(clickNodeId);
3199      let removeNodeIdList: number[] = this.listNodeUtils.removeNode(clickNodeId,
3200        parentNodeId, this.listNodeUtils.traverseNodeBF);
3201      this.listNodeDataSource.refreshData(this.listNodeUtils, MenuOperation.REMOVE_NODE, parentNodeId, removeNodeIdList);
3202      this.nodeIdList.splice(this.nodeIdList.indexOf(clickNodeId), 1);
3203    }
3204
3205    /**
3206     * Modify the node name.
3207     *
3208     * Register an ON_ITEM_MODIFY callback to obtain the ID, parent node ID, and node name of the modified node.
3209     *
3210     * @since 10
3211     */
3212    public modifyNode(): void {
3213      let clickNodeId = this.listNodeDataSource.getClickNodeId();
3214      this.listNodeDataSource.setItemVisibilityOnEdit(clickNodeId, MenuOperation.MODIFY_NODE);
3215    }
3216
3217    /**
3218     * Add a node.
3219     *
3220     * Icon of a new node, which is generated by the system by default.
3221     * If there is a same-level node, the icon of the first node of the same-level node is used.
3222     * If no icon is set for the first node of the same-level node, the new node does not have an icon.
3223     * If there is no peer node, the icon of the parent node is used.
3224     * If no icon is set for the parent node, the new node does not have an icon.
3225     * The system generates an ID for the new node and registers an ON_ITEM_ADD callback through
3226     * the EventBus mechanism to obtain the ID, parent node ID, node name, normal icon, selected icon,
3227     * edit icon of the new node.
3228     *
3229     * @since 10
3230     */
3231    public add(): void {
3232      let clickNodeId: number = this.listNodeDataSource.getClickNodeId();
3233      if (clickNodeId == this.listNodeDataSource.ROOT_NODE_ID || !this.listNodeDataSource.getIsFolder(clickNodeId)) {
3234        return;
3235      }
3236      let newNodeInfo: { isFolder: boolean, icon: Resource, selectedIcon: Resource, editIcon: Resource,
3237      menu: () =>any, secondaryTitle: number | string } =
3238        { isFolder: true, icon: null, selectedIcon: null, editIcon: null, menu: null, secondaryTitle: '' };
3239      newNodeInfo = this.listNodeUtils.getNewNodeInfo(clickNodeId);
3240      this.nodeIdList.push(this.nodeIdList[this.nodeIdList.length - 1] + 1);
3241      let newNodeId: number = this.nodeIdList[this.nodeIdList.length - 1];
3242      this.listNodeUtils.addNewNodeId = newNodeId;
3243      this.listNodeUtils.addNode(clickNodeId, newNodeId,
3244        { isFolder: newNodeInfo.isFolder, icon: newNodeInfo.icon, selectedIcon: newNodeInfo.selectedIcon,
3245          editIcon: newNodeInfo.editIcon, primaryTitle: '新建文件夹', menu: newNodeInfo.menu,
3246          secondaryTitle: newNodeInfo.secondaryTitle });
3247      this.listNodeDataSource.refreshData(this.listNodeUtils, MenuOperation.ADD_NODE, clickNodeId, [newNodeId]);
3248    }
3249
3250    public addNodeParam(nodeParam: NodeParam): TreeController {
3251      if (nodeParam.primaryTitle != null &&
3252        !this.listNodeUtils.checkMainTitleIsValid(nodeParam.primaryTitle)) {
3253        throw new Error('ListTreeNode[addNode]: ' +
3254          'The directory name cannot contain the following characters\ /: *? "< > | or exceeds the maximum length.');
3255        return null;
3256      }
3257      if (nodeParam.primaryTitle == null && nodeParam.icon == null) {
3258        throw new Error('ListTreeNode[addNode]: ' +
3259          'The icon and directory name cannot be empty at the same time.');
3260        return null;
3261      }
3262      if (nodeParam.currentNodeId === this.ROOT_NODE_ID || nodeParam.currentNodeId === null) {
3263        throw new Error('ListTreeNode[addNode]: currentNodeId can not be -1 or null.');
3264        return null;
3265      }
3266      this.nodeIdList.push(nodeParam.currentNodeId);
3267      this.listNodeUtils.addNode(nodeParam.parentNodeId, nodeParam.currentNodeId, nodeParam);
3268      return this;
3269    }
3270
3271    /**
3272     * Initialize the interface of the tree view. This interface is used to generate ListNodeDataSource data.
3273     * addNode is only designed for initialization. It can only be invoked during initialization.
3274     *
3275     * A maximum of 50 directory levels can be added.
3276     *
3277     * @param parentNodeId ID of the parent node.
3278     * @param currentNodeId ID of the new node. The value cannot be -1 or null.
3279     * @param nodeParam Configuration information of the newly added node.
3280     *                  For details, see the comment description of NodeParam.
3281     * @return ListTreeNode Tree view component proxy class.
3282     *
3283     * @since 10
3284     */
3285    public addNode(nodeParam?: NodeParam): TreeController {
3286      if (nodeParam == null) {
3287        this.add();
3288      } else {
3289        if (nodeParam.primaryTitle != null &&
3290          !this.listNodeUtils.checkMainTitleIsValid(nodeParam.primaryTitle)) {
3291          throw new Error('ListTreeNode[addNode]: ' +
3292            'The directory name cannot contain the following characters\ /: *? "< > | or exceeds the maximum length.');
3293          return null;
3294        }
3295        if (nodeParam.primaryTitle == null && nodeParam.icon == null) {
3296          throw new Error('ListTreeNode[addNode]: ' +
3297            'The icon and directory name cannot be empty at the same time.');
3298          return null;
3299        }
3300        if (nodeParam.currentNodeId === this.ROOT_NODE_ID || nodeParam.currentNodeId === null) {
3301          throw new Error('ListTreeNode[addNode]: currentNodeId can not be -1 or null.');
3302          return null;
3303        }
3304        this.nodeIdList.push(nodeParam.currentNodeId);
3305        this.listNodeUtils.addNode(nodeParam.parentNodeId, nodeParam.currentNodeId, nodeParam);
3306        return this;
3307      }
3308    }
3309
3310    /**
3311     * After the initialization is complete by calling the addNode interface,
3312     * call this interface to complete initialization.
3313     *
3314     * This interface must be called when you finish initializing the ListTreeView by addNode.
3315     * @since 10
3316     */
3317    public buildDone() {
3318      this.listNodeDataSource.init(this.listNodeUtils);
3319      this.nodeIdList.sort((a, b) => a - b);
3320    }
3321
3322    public refreshNode(parentId: number, parentSubTitle: ResourceStr = '',
3323                       currentSubtitle: ResourceStr = '') {
3324      this.listNodeDataSource.setNodeSubtitlePara(parentId, parentSubTitle, currentSubtitle);
3325    }
3326  }
3327}