mirror of
https://github.com/gnosygnu/xowa.git
synced 2024-10-27 20:34:16 +00:00
272 lines
13 KiB
Java
272 lines
13 KiB
Java
/*
|
|
XOWA: the XOWA Offline Wiki Application
|
|
Copyright (C) 2012 gnosygnu@gmail.com
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as
|
|
published by the Free Software Foundation, either version 3 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package gplx.gfui;
|
|
import gplx.*;
|
|
import gplx.threads.ThreadAdp_;
|
|
|
|
import org.eclipse.swt.*;
|
|
import org.eclipse.swt.custom.*;
|
|
import org.eclipse.swt.events.*;
|
|
import org.eclipse.swt.graphics.*;
|
|
import org.eclipse.swt.layout.*;
|
|
import org.eclipse.swt.widgets.*;
|
|
public class Swt_tab_mgr implements Gxw_tab_mgr, Swt_control, FocusListener, GfoEvMgrOwner {
|
|
private GfuiInvkCmd cmd_sync;
|
|
// private GfuiInvkCmd cmd_async; // NOTE: async needed for some actions like responding to key_down and calling .setSelection; else app hangs; DATE:2014-04-30
|
|
public Swt_tab_mgr(Swt_kit kit, Swt_control owner_control, KeyValHash ctorArgs) {
|
|
this.kit = kit;
|
|
tab_folder = new CTabFolder(owner_control.Under_composite(), SWT.BORDER);
|
|
tab_folder.setBorderVisible(false);
|
|
tab_folder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
|
|
tab_folder.setSimple(true);
|
|
tab_folder.addListener(SWT.Selection, new Swt_tab_mgr_lnr_selection(this));
|
|
tab_folder.addKeyListener(new Swt_lnr_key(this));
|
|
|
|
new Swt_tab_mgr_lnr_drag_drop(this, tab_folder);
|
|
tab_folder.addCTabFolder2Listener(new Swt_tab_mgr_lnr_close(this));
|
|
core = new Swt_core_cmds(tab_folder);
|
|
// cmd_async = kit.New_cmd_async(this);
|
|
cmd_sync = kit.New_cmd_sync(this);
|
|
}
|
|
public Swt_kit Kit() {return kit;} private Swt_kit kit;
|
|
public CTabFolder Under_ctabFolder() {return tab_folder;}
|
|
@Override public Control Under_control() {return tab_folder;} private CTabFolder tab_folder;
|
|
@Override public Composite Under_composite() {return tab_folder;}
|
|
@Override public Control Under_menu_control() {return tab_folder;}
|
|
public GfoEvMgr EvMgr() {return ev_mgr;} private GfoEvMgr ev_mgr;
|
|
public void EvMgr_(GfoEvMgr v) {ev_mgr = v;}
|
|
public ColorAdp Btns_selected_color() {return btns_selected_color;} private ColorAdp btns_selected_color;
|
|
public void Btns_selected_color_(ColorAdp v) {
|
|
btns_selected_color = v;
|
|
tab_folder.setSelectionBackground(kit.New_color(v));
|
|
}
|
|
public ColorAdp Btns_unselected_color() {return btns_unselected_color;}
|
|
public void Btns_unselected_color_(ColorAdp v) {
|
|
btns_unselected_color = v;
|
|
tab_folder.setBackground(kit.New_color(v));
|
|
} private ColorAdp btns_unselected_color;
|
|
@Override public boolean Btns_curved() {return tab_folder.getSimple();} @Override public void Btns_curved_(boolean v) {tab_folder.setSimple(!v);}
|
|
@Override public boolean Btns_place_on_top() {return tab_folder.getTabPosition() == SWT.TOP;}
|
|
@Override public void Btns_place_on_top_(boolean v) {tab_folder.setTabPosition(v ? SWT.TOP : SWT.BOTTOM); tab_folder.layout();}
|
|
@Override public int Btns_height() {return tab_folder.getTabHeight();} @Override public void Btns_height_(int v) {tab_folder.setTabHeight(v); tab_folder.layout();}
|
|
@Override public boolean Btns_close_visible() {return btns_close_visible;} private boolean btns_close_visible = true;
|
|
@Override public void Btns_close_visible_(boolean v) {
|
|
this.btns_close_visible = v;
|
|
CTabItem[] itms = tab_folder.getItems();
|
|
int len = itms.length;
|
|
for (int i = 0; i < len; i++)
|
|
itms[i].setShowClose(v);
|
|
}
|
|
@Override public boolean Btns_unselected_close_visible() {return tab_folder.getUnselectedCloseVisible();} @Override public void Btns_unselected_close_visible_(boolean v) {
|
|
tab_folder.setUnselectedCloseVisible(v);}
|
|
@Override public Gxw_tab_itm Tabs_add(Gfui_tab_itm_data tab_data) {
|
|
Swt_tab_itm rv = new Swt_tab_itm(this, kit, tab_folder, tab_data);
|
|
rv.Under_CTabItem().setData(tab_data);
|
|
CTabItem ctab_itm = rv.Under_CTabItem();
|
|
ctab_itm.setShowClose(btns_close_visible);
|
|
return rv;
|
|
}
|
|
@Override public void Tabs_close_by_idx(int i) {
|
|
CTabItem itm = tab_folder.getItems()[i];
|
|
Gfui_tab_itm_data tab_data = Get_tab_data(itm);
|
|
CTabItem next_tab = Tabs_select_after_closing_itm(tab_data); // NOTE: must calc next_tab before calling Pub_tab_closed; latter will recalc idx
|
|
this.Tabs_select_by_itm(next_tab); // NOTE: select tab before closing; DATE:2014-09-10
|
|
Pub_tab_closed(tab_data.Key()); // NOTE: dispose does not call event for .close; must manually raise event;
|
|
itm.dispose();
|
|
}
|
|
@Override public void Tabs_select_by_idx(int i) {
|
|
if (i == Gfui_tab_itm_data.Idx_null) return; // 0 tabs; return;
|
|
msg_tabs_select_by_idx_swt.Clear();
|
|
msg_tabs_select_by_idx_swt.Add("v", i);
|
|
cmd_sync.Invk(GfsCtx._, 0, Invk_tabs_select_by_idx_swt, msg_tabs_select_by_idx_swt);
|
|
} private GfoMsg msg_tabs_select_by_idx_swt = GfoMsg_.new_cast_(Invk_tabs_select_by_idx_swt);
|
|
@Override public void Tabs_switch(int src, int trg) {Tabs_switch(tab_folder.getItem(src), tab_folder.getItem(trg));}
|
|
public boolean Tabs_switch(CTabItem src_tab_itm, CTabItem trg_tab_itm) {
|
|
Control temp_control = src_tab_itm.getControl();
|
|
src_tab_itm.setControl(trg_tab_itm.getControl());
|
|
trg_tab_itm.setControl(temp_control);
|
|
|
|
String temp_str = src_tab_itm.getText();
|
|
src_tab_itm.setText(trg_tab_itm.getText());
|
|
trg_tab_itm.setText(temp_str);
|
|
|
|
temp_str = src_tab_itm.getToolTipText();
|
|
src_tab_itm.setToolTipText(trg_tab_itm.getToolTipText());
|
|
trg_tab_itm.setToolTipText(temp_str);
|
|
|
|
Gfui_tab_itm_data src_tab_data = Get_tab_data(src_tab_itm);
|
|
Gfui_tab_itm_data trg_tab_data = Get_tab_data(trg_tab_itm);
|
|
int src_tab_idx = src_tab_data.Idx(), trg_tab_idx = trg_tab_data.Idx();
|
|
tab_folder.setSelection(trg_tab_itm);
|
|
GfoEvMgr_.PubVals(this, Gfui_tab_mgr.Evt_tab_switched, KeyVal_.new_("src", src_tab_data.Key()), KeyVal_.new_("trg", trg_tab_data.Key()));
|
|
return src_tab_idx < trg_tab_idx;
|
|
}
|
|
public void Tabs_select_by_itm(CTabItem itm) {
|
|
if (itm == null) return; // 0 tabs; return;
|
|
msg_tabs_select_by_itm_swt.Clear();
|
|
msg_tabs_select_by_itm_swt.Add("v", itm);
|
|
cmd_sync.Invk(GfsCtx._, 0, Invk_tabs_select_by_itm_swt, msg_tabs_select_by_itm_swt);
|
|
} private GfoMsg msg_tabs_select_by_itm_swt = GfoMsg_.new_cast_(Invk_tabs_select_by_itm_swt);
|
|
private void Tabs_select_by_idx_swt(int idx) {
|
|
tab_folder.setSelection(idx);
|
|
CTabItem itm = tab_folder.getItem(idx);
|
|
Pub_tab_selected(Get_tab_key(itm)); // NOTE: setSelection does not call event for SWT.Selection; must manually raise event;
|
|
}
|
|
private void Tabs_select_by_itm_swt(CTabItem itm) {
|
|
tab_folder.setSelection(itm);
|
|
Pub_tab_selected(Get_tab_key(itm)); // NOTE: setSelection does not call event for SWT.Selection; must manually raise event;
|
|
}
|
|
public CTabItem Tabs_select_after_closing_itm(Gfui_tab_itm_data tab_data) {
|
|
int next_idx = Gfui_tab_itm_data.Get_idx_after_closing(tab_data.Idx(), tab_folder.getItemCount());
|
|
return next_idx == Gfui_tab_itm_data.Idx_null ? null : tab_folder.getItem(next_idx);
|
|
}
|
|
public void Pub_tab_selected(String key) {
|
|
GfoEvMgr_.PubObj(this, Gfui_tab_mgr.Evt_tab_selected, "key", key);
|
|
}
|
|
public void Pub_tab_closed(String key) {
|
|
GfoEvMgr_.PubObj(this, Gfui_tab_mgr.Evt_tab_closed, "key", key);
|
|
}
|
|
@Override public GxwCore_base Core() {return core;} GxwCore_base core;
|
|
@Override public GxwCbkHost Host() {return host;} @Override public void Host_set(GxwCbkHost host) {this.host = host;} GxwCbkHost host;
|
|
@Override public String TextVal() {return "not implemented";}
|
|
@Override public void TextVal_set(String v) {}
|
|
@Override public void EnableDoubleBuffering() {}
|
|
@Override public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) {
|
|
if (String_.Eq(k, Invk_tabs_select_by_idx_swt)) Tabs_select_by_idx_swt(m.ReadInt("v"));
|
|
else if (String_.Eq(k, Invk_tabs_select_by_itm_swt)) Tabs_select_by_itm_swt((CTabItem)m.ReadObj("v", null));
|
|
else return GfoInvkAble_.Rv_unhandled;
|
|
return this;
|
|
}
|
|
@Override public void focusGained(FocusEvent arg0) {}
|
|
@Override public void focusLost(FocusEvent arg0) {}
|
|
private static String
|
|
Invk_tabs_select_by_idx_swt = "tabs_select_by_idx"
|
|
, Invk_tabs_select_by_itm_swt = "tabs_select_by_itm"
|
|
;
|
|
public static Gfui_tab_itm_data Get_tab_data_by_obj(Object data) {return (Gfui_tab_itm_data)data;}
|
|
public static Gfui_tab_itm_data Get_tab_data(CTabItem itm) {return (Gfui_tab_itm_data)itm.getData();}
|
|
public static String Get_tab_key(CTabItem itm) {return ((Gfui_tab_itm_data)itm.getData()).Key();}
|
|
}
|
|
class Swt_tab_mgr_lnr_selection implements Listener {
|
|
public Swt_tab_mgr_lnr_selection(Swt_tab_mgr tab_folder) {this.tab_folder = tab_folder;} private Swt_tab_mgr tab_folder;
|
|
public void handleEvent(Event ev) {
|
|
tab_folder.Pub_tab_selected(Swt_tab_mgr.Get_tab_data_by_obj(ev.item.getData()).Key());
|
|
}
|
|
}
|
|
class Swt_tab_mgr_lnr_close extends CTabFolder2Adapter { // handles close when tab x is clicked
|
|
public Swt_tab_mgr_lnr_close(Swt_tab_mgr tab_folder) {this.tab_folder = tab_folder;} private Swt_tab_mgr tab_folder;
|
|
@Override public void close(CTabFolderEvent ev) {
|
|
Gfui_tab_itm_data tab_data = Swt_tab_mgr.Get_tab_data_by_obj(ev.item.getData());
|
|
tab_folder.Tabs_close_by_idx(tab_data.Idx());
|
|
ev.doit = false; // mark ev handled, since Tabs_close_by_idx closes tab
|
|
}
|
|
}
|
|
class Swt_tab_mgr_lnr_drag_drop implements Listener {
|
|
private boolean dragging = false;
|
|
private boolean drag_stop = false;
|
|
private CTabItem drag_itm;
|
|
private final Swt_tab_mgr tab_mgr; private final CTabFolder tab_folder; private final Display display;
|
|
private Point prv_mouse;
|
|
private Point dead_zone = null;
|
|
public Swt_tab_mgr_lnr_drag_drop(Swt_tab_mgr tab_mgr, CTabFolder tab_folder) {
|
|
this.tab_mgr = tab_mgr; this.tab_folder = tab_folder; this.display = tab_folder.getDisplay();
|
|
tab_folder.addListener(SWT.DragDetect, this);
|
|
tab_folder.addListener(SWT.MouseUp, this);
|
|
tab_folder.addListener(SWT.MouseMove, this);
|
|
tab_folder.addListener(SWT.MouseExit, this);
|
|
tab_folder.addListener(SWT.MouseEnter, this);
|
|
}
|
|
private void Drag_drop_bgn(CTabItem itm) {
|
|
dragging = true;
|
|
drag_stop = false;
|
|
drag_itm = itm;
|
|
dead_zone = null;
|
|
}
|
|
private void Drag_drop_end() {
|
|
tab_folder.setInsertMark(null, false);
|
|
dragging = false;
|
|
drag_stop = false;
|
|
drag_itm = null;
|
|
}
|
|
public void handleEvent(Event e) {
|
|
Point cur_mouse = e.type == SWT.DragDetect
|
|
? tab_folder.toControl(display.getCursorLocation()) //see bug 43251
|
|
: new Point(e.x, e.y)
|
|
;
|
|
switch (e.type) {
|
|
case SWT.DragDetect: {
|
|
CTabItem itm = tab_folder.getItem(cur_mouse);
|
|
if (itm == null) return;
|
|
this.Drag_drop_bgn(itm);
|
|
break;
|
|
}
|
|
case SWT.MouseEnter:
|
|
if (drag_stop) {
|
|
dragging = e.button != 0;
|
|
drag_stop = false;
|
|
}
|
|
break;
|
|
case SWT.MouseExit:
|
|
if (dragging)
|
|
Drag_drop_end();
|
|
break;
|
|
case SWT.MouseUp: {
|
|
if (!dragging) return;
|
|
Drag_drop_end();
|
|
break;
|
|
}
|
|
case SWT.MouseMove: {
|
|
if (!dragging) return;
|
|
CTabItem curr_itm = tab_folder.getItem(cur_mouse);
|
|
if (curr_itm == null) {
|
|
tab_folder.setInsertMark(null, false);
|
|
return;
|
|
}
|
|
if (curr_itm == drag_itm) return; // curr_itm is same as drag_itm; ignore
|
|
int cur_mouse_x = cur_mouse.x;
|
|
int prv_mouse_x = prv_mouse == null ? 0 : prv_mouse.x;
|
|
prv_mouse = cur_mouse; // set prv_mouse now b/c of early return below; note that cur_mouse_x and prv_mouse_x are cached above
|
|
if ( dead_zone != null // dead_zone exists
|
|
&& Int_.Between(cur_mouse_x, dead_zone.x, dead_zone.y)) { // mouse is in dead_zone
|
|
int drag_idx = Swt_tab_mgr.Get_tab_data(drag_itm).Idx();
|
|
int curr_idx = Swt_tab_mgr.Get_tab_data(curr_itm).Idx();
|
|
if (drag_idx > curr_idx && cur_mouse_x < prv_mouse_x) {} // drag_itm is right of curr_itm, but mouse is moving left (direction reversed); cancel
|
|
else if (drag_idx < curr_idx && cur_mouse_x > prv_mouse_x) {} // drag_itm is left of curr_itm, but mouse is moving right (direction reversed); cancel
|
|
else
|
|
return; // in dead zone, and still moving in original direction; return early
|
|
}
|
|
boolean fwd = tab_mgr.Tabs_switch(drag_itm, curr_itm);
|
|
drag_itm = curr_itm;
|
|
Rectangle drag_rect = drag_itm.getBounds();
|
|
dead_zone = Calc_dead_zone(fwd, cur_mouse_x, drag_rect.x, drag_rect.width);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
public static Point Calc_dead_zone(boolean fwd, int mouse_x, int drag_l, int drag_w) {
|
|
if (fwd) { // drag_itm was moving fwd (moving right)
|
|
if (mouse_x < drag_l) return new Point(mouse_x, drag_l); // mouse_x < drag_l; create dead_zone until mouse_x reaches drag_l; occurs when moving drag is small_title and trg_itm is large_title
|
|
}
|
|
else { // drag_itm was moving bwd (moving left)
|
|
int drag_r = drag_l + drag_w;
|
|
if (mouse_x > drag_r) return new Point(drag_r, mouse_x); // mouse_x > drag_r; create dead_zone until mouse_x reaches drag_r
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
//#} |