diff --git a/150_gfui/src/gplx/gfui/kits/swts/Swt_html.java b/150_gfui/src/gplx/gfui/kits/swts/Swt_html.java index 073a03a59..72ce4a54c 100644 --- a/150_gfui/src/gplx/gfui/kits/swts/Swt_html.java +++ b/150_gfui/src/gplx/gfui/kits/swts/Swt_html.java @@ -185,25 +185,6 @@ public class Swt_html implements Gxw_html, Swt_control, FocusListener, Gfo_evt_m , Browser_tid_mozilla = SWT.MOZILLA , Browser_tid_webkit = SWT.WEBKIT ; - - private static final String URL_PREFIX_ABOUT = "about:"; - private static final String URL_PREFIX_BLANK = "blank"; - public static String NormalizeSwtUrl(String url) { - String rv = url; - - // 2020-09-19|ISSUE#:799|strip "about:" from url due to SWT 4.16 - rv = String_.Has_at_bgn(rv, URL_PREFIX_ABOUT) - ? String_.Mid(rv, URL_PREFIX_ABOUT.length()) - : rv; - - // 2015-06-09|webkit prefixes "about:blank" to anchors; causes TOC to fail when clicking on links; EX:about:blank#TOC1 - // 2020-09-22|removed webkit check due to SWT 4.16; `html_box.Browser_tid() == Swt_html.Browser_tid_webkit` - // still strip "blank"; note that SWT 4.16 changes anchors from "file:///#anchor" to "en.w/wiki/page/#anchor" - rv = String_.Has_at_bgn(rv, URL_PREFIX_BLANK) - ? String_.Mid(rv, URL_PREFIX_BLANK.length()) - : rv; - return rv; - } } class Swt_core_cmds_html extends Swt_core__basic { public Swt_core_cmds_html(Swt_html html_box, Control control) {super(control);} @@ -253,7 +234,7 @@ class Swt_html_lnr_status implements StatusTextListener { if (html_box.Kit().Kit_mode__term()) return; // shutting down raises status changed events; ignore, else SWT exception thrown; DATE:2014-05-29 // 2020-09-22|ISSUE#:799|normalize URL due to SWT 4.16 - String ev_text = Swt_html.NormalizeSwtUrl(ev.text); + String ev_text = Swt_html_utl.NormalizeSwtUrl(ev.text); String load_by_url_path = html_box.Load_by_url_path(); if (load_by_url_path != null) ev_text = String_.Replace(ev_text, load_by_url_path, ""); // remove "C:/xowa/tab_1.html" @@ -276,7 +257,7 @@ class Swt_html_lnr_location implements LocationListener { @Override public void changing(LocationEvent arg) {Pub_evt(arg, Gfui_html.Evt_location_changing);} private void Pub_evt(LocationEvent arg, String evt) { // 2020-09-22|ISSUE#:799|normalize URL due to SWT 4.16 - String location = Swt_html.NormalizeSwtUrl(arg.location); + String location = Swt_html_utl.NormalizeSwtUrl(arg.location); // location_changing fires once when page is loaded -> ignore if (String_.Eq(location, String_.Empty)) { @@ -344,7 +325,7 @@ class Swt_html_lnr_wheel implements MouseWheelListener { // public Swt_open_window_listener(Swt_html html_box) {this.html_box = html_box;} // @Override public void open(WindowEvent arg0) { // Tfds.Write(); -// } +// } //} /* NOTE_1:browser scrollbar and click @@ -362,5 +343,5 @@ so, assume: two issues still occur with the workaround 1) even if the scrollbar is not present, any click on the right-hand edge of the screen will be ignored -2) click -> hold -> move mouse over to left -> release; the mouse up should be absorbed, but it is not due to position of release +2) click -> hold -> move mouse over to left -> release; the mouse up should be absorbed, but it is not due to position of release */ diff --git a/150_gfui/src/gplx/gfui/kits/swts/Swt_html_utl.java b/150_gfui/src/gplx/gfui/kits/swts/Swt_html_utl.java new file mode 100644 index 000000000..4b715b1a3 --- /dev/null +++ b/150_gfui/src/gplx/gfui/kits/swts/Swt_html_utl.java @@ -0,0 +1,40 @@ +/* +XOWA: the XOWA Offline Wiki Application +Copyright (C) 2012-2020 gnosygnu@gmail.com + +XOWA is licensed under the terms of the General Public License (GPL) Version 3, +or alternatively under the terms of the Apache License Version 2.0. + +You may use XOWA according to either of these licenses as is most appropriate +for your project on a case-by-case basis. + +The terms of each license can be found in the source code repository: + +GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt +Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt +*/ +package gplx.gfui.kits.swts; + +import gplx.String_; + +public class Swt_html_utl { + private static final String URL_PREFIX_ABOUT = "about:"; + private static final String URL_PREFIX_BLANK = "blank"; + + public static String NormalizeSwtUrl(String url) { + String rv = url; + + // 2020-09-19|ISSUE#:799|strip "about:" from url due to SWT 4.16 + rv = String_.Has_at_bgn(rv, URL_PREFIX_ABOUT) + ? String_.Mid(rv, URL_PREFIX_ABOUT.length()) + : rv; + + // 2015-06-09|webkit prefixes "about:blank" to anchors; causes TOC to fail when clicking on links; EX:about:blank#TOC1 + // 2020-09-22|removed webkit check due to SWT 4.16; `html_box.Browser_tid() == Swt_html.Browser_tid_webkit` + // still strip "blank"; note that SWT 4.16 changes anchors from "file:///#anchor" to "en.w/wiki/page/#anchor" + rv = String_.Has_at_bgn(rv, URL_PREFIX_BLANK) + ? String_.Mid(rv, URL_PREFIX_BLANK.length()) + : rv; + return rv; + } +} diff --git a/400_xowa/src/gplx/xowa/guis/views/Xog_tab_mgr.java b/400_xowa/src/gplx/xowa/guis/views/Xog_tab_mgr.java index 3d61a78d7..669b56cbe 100644 --- a/400_xowa/src/gplx/xowa/guis/views/Xog_tab_mgr.java +++ b/400_xowa/src/gplx/xowa/guis/views/Xog_tab_mgr.java @@ -1,6 +1,6 @@ /* XOWA: the XOWA Offline Wiki Application -Copyright (C) 2012-2017 gnosygnu@gmail.com +Copyright (C) 2012-2020 gnosygnu@gmail.com XOWA is licensed under the terms of the General Public License (GPL) Version 3, or alternatively under the terms of the Apache License Version 2.0. @@ -13,264 +13,299 @@ The terms of each license can be found in the source code repository: GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt */ -package gplx.xowa.guis.views; import gplx.*; import gplx.xowa.*; import gplx.xowa.guis.*; -import gplx.gfui.*; import gplx.gfui.kits.core.*; import gplx.gfui.draws.*; import gplx.gfui.controls.gxws.*; import gplx.gfui.controls.tabs.*; import gplx.gfui.controls.standards.*; -import gplx.xowa.apps.apis.xowa.gui.browsers.*; import gplx.xowa.specials.*; -import gplx.xowa.apps.urls.*; -public class Xog_tab_mgr implements Gfo_evt_itm { - private Ordered_hash tab_regy = Ordered_hash_.New(); private int tab_uid = 0; - private boolean btns__hide_if_one; private int btns__height; - public Xog_tab_mgr(Xog_win_itm win) { - this.win = win; - ev_mgr = new Gfo_evt_mgr(this); - } - public Gfo_evt_mgr Evt_mgr() {return ev_mgr;} private Gfo_evt_mgr ev_mgr; - public Xog_win_itm Win() {return win;} private Xog_win_itm win; - public Gfui_tab_mgr Tab_mgr() {return tab_mgr;} private Gfui_tab_mgr tab_mgr; - public int Btns__min_chars() {return btns__min_chars;} private int btns__min_chars; - public int Btns__max_chars() {return btns__max_chars;} private int btns__max_chars; - public boolean Javascript_enabled() {return javascript_enabled;} private boolean javascript_enabled = true; - private byte page_load_mode; - public boolean Page_load_mode_is_url() {return page_load_mode == Gxw_html_load_tid_.Tid_url;} - public void Init_by_kit(Gfui_kit kit) { - tab_mgr = kit.New_tab_mgr("xowa.tab_mgr", win.Win_box()); - active_tab = Xog_tab_itm_.Null; - Gfo_evt_mgr_.Sub_same_many(tab_mgr, this, Gfui_tab_mgr.Evt_tab_selected, Gfui_tab_mgr.Evt_tab_closed, Gfui_tab_mgr.Evt_tab_switched); - win.App().Cfg().Bind_many_app(this, Cfg__page_load_mode - , Cfg__place_on_top, Cfg__height, Cfg__hide_if_one, Cfg__curved, Cfg__close_btn_visible, Cfg__unselected_close_btn_visible, Cfg__max_chars, Cfg__min_chars); - } - public Xog_tab_itm Active_tab() {return active_tab;} private Xog_tab_itm active_tab; - public Xog_tab_itm Active_tab_assert() { - if (active_tab == Xog_tab_itm_.Null) this.Tabs_new_dflt(true); - return active_tab; - } - public boolean Active_tab_is_null() {return active_tab == Xog_tab_itm_.Null;} - private void Btns_text_recalc() { - int len = this.Tabs_len(); - for (int i = 0; i < len; i++) { - Xog_tab_itm tab_itm = this.Tabs_get_at(i); - tab_itm.Tab_name_(); - } - } - public int Tabs_len() {return tab_regy.Count();} - public Xog_tab_itm Tabs_new_init(Xowe_wiki wiki, Xoae_page page) {return this.Tabs_new(true, true, wiki, page);} - public Xog_tab_itm Tabs_get_at(int i) {return (Xog_tab_itm)tab_regy.Get_at(i);} - public Xog_tab_itm Tabs_new_dflt() {return Tabs_new_dflt(false);} - public Xog_tab_itm Tabs_new_dflt(boolean focus) { - boolean active_tab_is_null = this.Active_tab_is_null(); - Xowe_wiki cur_wiki = active_tab_is_null ? win.App().Usere().Wiki() : active_tab.Wiki(); - Xoa_ttl ttl = Xoa_ttl.Parse(cur_wiki, Xow_special_meta_.Itm__default_tab.Ttl_bry()); - Xoa_url url = cur_wiki.Utl__url_parser().Parse_by_urlbar_or_null(ttl.Full_db_as_str()); if (url == null) throw Err_.new_("url", "invalid url", "url", url); - Xog_tab_itm rv = Tabs_new(focus, active_tab_is_null, cur_wiki, Xoae_page.New(cur_wiki, ttl)); - rv.Page_update_ui(); - rv.Show_url_bgn(url); - return rv; - } - private Xog_tab_itm Tabs_new(boolean focus, boolean active_tab_is_null, Xowe_wiki wiki, Xoae_page page) { - String tab_key = "tab_" + Int_.To_str(tab_uid++); int tab_idx = tab_regy.Count(); - Gfui_tab_itm_data tab_data = new Gfui_tab_itm_data(tab_key, tab_idx); - Xog_tab_itm rv = new Xog_tab_itm(this, tab_data, wiki, page); - Gfui_tab_itm tab_box = tab_mgr.Tabs_add(tab_data); - rv.Make_html_box(tab_uid, tab_box, win, tab_mgr); - rv.Html_itm().Js_enabled_(javascript_enabled); - tab_box.Subs_add(rv.Html_itm().Html_box()); - tab_regy.Add(tab_key, rv); - if ( focus - || active_tab_is_null // NOTE: must select 1st tab, else nothing will show in tab box - ) { - tab_mgr.Tabs_select_by_idx(rv.Tab_idx()); - active_tab = rv; - } - Tabs_hide_if_one_chk(false); - return rv; - } - public void Tabs_new_dupe(boolean focus) { - if (this.Active_tab_is_null()) return; - String url = active_tab.Page().Url().To_str(); - Tabs_new_dflt(focus); - win.Page__navigate_by_url_bar(url); - } - public void Tabs_javascript_enabled_(boolean v) { - this.javascript_enabled = v; - int len = tab_regy.Count(); - for (int i = 0; i < len; i++) { - Xog_tab_itm tab = Tabs_get_by_idx_or_warn(i); - tab.Html_itm().Js_enabled_(v); - } - } - private void Tabs_selected(String key) { - Xog_tab_itm tab = Tabs_get_by_key_or_warn(key); if (tab == null) return; - active_tab = tab; - Xoae_page page = tab.Page(); - Xog_tab_itm_read_mgr.Update_selected_tab(win, page.Url(), page.Ttl()); - tab.Html_itm().Tab_selected(page); - } - public void Tabs_close_cur() { - if (this.Active_tab_is_null()) return; - Tabs__pub_close(active_tab); - tab_mgr.Tabs_close_by_idx(active_tab.Tab_idx()); - Xog_tab_itm cur_tab = this.Active_tab(); // get new current tab for line below - if (cur_tab != null) cur_tab.Html_box().Focus(); // NOTE: needed to focus tab box else tab button will be focused; DATE:2014-07-13 - } - public void Tabs_close_others() {this.Tabs_close_to_bgn(); this.Tabs_close_to_end();} - public void Tabs_close_to_bgn() {if (Active_tab_is_null()) return; Tabs_close_rng(0 , active_tab.Tab_idx());} - public void Tabs_close_to_end() {if (Active_tab_is_null()) return; Tabs_close_rng(active_tab.Tab_idx() + 1 , tab_regy.Count());} - public void Tabs_close_rng(int bgn, int end) { - for (int i = bgn; i < end; i++) { - Xog_tab_itm tab = Tabs_get_at(bgn); - if (!Tabs__pub_close(tab)) return; - } - for (int i = bgn; i < end; i++) - tab_mgr.Tabs_close_by_idx(bgn); // NOTE: close at bgn, not at i, b/c each close will remove a tab from collection - } - public boolean Tabs__pub_close_all() {return Tabs__pub_close_rng(0, this.Tabs_len());} - public boolean Tabs__pub_close_rng(int bgn, int end) { - boolean rv = true; - for (int i = bgn; i < end; i++) { - Xog_tab_itm tab = Tabs_get_at(i); - boolean close_allowed = Tabs__pub_close(tab); - if (!close_allowed) rv = false; - } - return rv; - } - public boolean Tabs__pub_close(Xog_tab_itm tab) { - return tab.Page().Tab_data().Close_mgr().When_close(tab, Xoa_url.Null); - } - public void Tabs_close_undo() { - if (closed_undo_list.Count() == 0) return; - String url = (String)List_adp_.Pop(closed_undo_list); - Tabs_new_dflt(true); - win.Page__navigate_by_url_bar(url); - } - private List_adp closed_undo_list = List_adp_.New(); - private void Tabs_closed(String key) { - Xog_tab_itm itm = Tabs_get_by_key_or_warn(key); if (itm == null) return; - itm.Html_box().Html_dispose(); - closed_undo_list.Add(itm.Page().Url().To_str()); - tab_regy.Del(key); - if (tab_regy.Count() == 0) { - active_tab = Xog_tab_itm_.Null; - Xog_tab_itm_read_mgr.Update_selected_tab_blank(win); - } - else - Tabs_recalc_idx(); - Tabs_hide_if_one_chk(false); - } - private Xog_tab_itm Tabs_get_by_key_or_warn(String key) { - Xog_tab_itm rv = (Xog_tab_itm)tab_regy.Get_by(key); if (rv == null) win.App().Usr_dlg().Warn_many("", "", "tab.selected could not find tab; key={0}", key); - return rv; - } - private Xog_tab_itm Tabs_get_by_idx_or_warn(int idx) { - Xog_tab_itm rv = (Xog_tab_itm)tab_regy.Get_at(idx); if (rv == null) win.App().Usr_dlg().Warn_many("", "", "tab.selected could not find tab; idx={0}", idx); - return rv; - } - private void Tabs_recalc_idx() { - int len = tab_regy.Count(); - for (int i = 0; i < len; i++) { - Xog_tab_itm itm = Tabs_get_by_idx_or_warn(i); - itm.Tab_idx_(i); - } - } - public void Tabs_select(boolean fwd) { - if (this.Active_tab_is_null()) return; - int new_idx = TabBox_.Cycle(fwd, active_tab.Tab_idx(), tab_regy.Count()); - tab_mgr.Tabs_select_by_idx(new_idx); - } - public void Tabs_select_by_idx(int v) { - if (v < 0 || v >= tab_regy.Count()) return; - tab_mgr.Tabs_select_by_idx(v); - } - public void Tabs_move(boolean fwd) { - if (this.Active_tab_is_null()) return; - int src_idx = active_tab.Tab_idx(); - int trg_idx = TabBox_.Cycle(fwd, src_idx, tab_regy.Count()); - tab_mgr.Tabs_switch(src_idx, trg_idx); - } - private void Tabs_switched(String src_key, String trg_key) { - Xog_tab_itm src_itm = Tabs_get_by_key_or_warn(src_key); - Xog_tab_itm trg_itm = Tabs_get_by_key_or_warn(trg_key); - src_itm.Switch_mem(trg_itm); - active_tab = trg_itm; // NOTE: src_itm initiated switch, but trg_itm is now active b/c everything in src_itm has now been reparented to trg_itm; DATE:2014-05-12 - } - public void Tabs_new_link(boolean focus, String link) { // handle empty link - if (String_.Len_eq_0(link)) { - if (this.Active_tab_is_null()) return; - link = gplx.langs.htmls.encoders.Gfo_url_encoder_.Http_url.Decode_str(active_tab.Html_itm().Html_selected_get_active_or_selection()); // NOTE: must decode else url-encoded special pages don't work; EX:home/wiki/Special:XowaCfg%3Fgrp%3Dxowa.html.css; DATE:2017-01-02 - } - if (String_.Len_eq_0(link)) {win.App().Usr_dlg().Prog_many("", "", "no link or text selected"); return;} - Tabs_new_link(link, focus); - } - public void Tabs_new_link(String link, boolean focus) { - Xowe_wiki wiki = active_tab.Wiki(); - Xog_tab_itm new_tab = Tabs_new(focus, false, wiki, Xoae_page.New(wiki, active_tab.Page().Ttl())); // NOTE: do not use ttl from link, else middle-clicking pages with anchors won't work; DATE:2015-05-03 - Xoa_url url = wiki.Utl__url_parser().Parse_by_urlbar_or_null(link); if (url == null) return; // NOTE: link must be of form domain/wiki/page; DATE:2014-05-27 - new_tab.Show_url_bgn(url); - if (focus) - tab_mgr.Tabs_select_by_idx(new_tab.Tab_idx()); - } - private void Tabs_hide_if_one_chk(boolean force) { - if (btns__hide_if_one || force) {// run code only if enabled or forced - if (tab_regy.Count() == 1) { - int desired_height = btns__hide_if_one ? 0 : btns__height; - if (tab_mgr.Btns_height() != desired_height) - tab_mgr.Btns_height_(desired_height); - } - else { - if (tab_mgr.Btns_height() != btns__height) - tab_mgr.Btns_height_(btns__height); - } - } - } - public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { - if (ctx.Match(k, Invk_tabs_new_dflt__at_dflt__focus_y)) Tabs_new_dflt(Bool_.Y); - else if (ctx.Match(k, Invk_tabs_new_link__at_dflt__focus_n)) Tabs_new_link(Bool_.N, m.ReadStrOr("v", null)); - else if (ctx.Match(k, Invk_tabs_new_link__at_dflt__focus_y)) Tabs_new_link(Bool_.Y, m.ReadStrOr("v", null)); - else if (ctx.Match(k, Gfui_tab_mgr.Evt_tab_selected)) Tabs_selected(m.ReadStr("key")); - else if (ctx.Match(k, Gfui_tab_mgr.Evt_tab_closed)) Tabs_closed(m.ReadStr("key")); - else if (ctx.Match(k, Gfui_tab_mgr.Evt_tab_switched)) Tabs_switched(m.ReadStr("src"), m.ReadStr("trg")); - else if (ctx.Match(k, Invk_tabs_close_cur)) Tabs_close_cur(); - else if (ctx.Match(k, Invk_tabs_select_bwd)) Tabs_select(Bool_.N); - else if (ctx.Match(k, Invk_tabs_select_fwd)) Tabs_select(Bool_.Y); - else if (ctx.Match(k, Invk_tabs_switch_cur_bwd)) Tabs_move(Bool_.N); - else if (ctx.Match(k, Invk_tabs_switch_cur_fwd)) Tabs_move(Bool_.Y); - - else if (ctx.Match(k, Cfg__place_on_top)) tab_mgr.Btns_place_on_top_(m.ReadYn("v")); - else if (ctx.Match(k, Cfg__height)) {btns__height = m.ReadInt("v"); tab_mgr.Btns_height_(btns__height);} - else if (ctx.Match(k, Cfg__hide_if_one)) {btns__hide_if_one = m.ReadYn("v"); Tabs_hide_if_one_chk(true);} - else if (ctx.Match(k, Cfg__curved)) tab_mgr.Btns_curved_(m.ReadYn("v")); - else if (ctx.Match(k, Cfg__close_btn_visible)) tab_mgr.Btns_close_visible_(m.ReadYn("v")); - else if (ctx.Match(k, Cfg__unselected_close_btn_visible)) tab_mgr.Btns_unselected_close_visible_(m.ReadYn("v")); - else if (ctx.Match(k, Cfg__max_chars)) {btns__max_chars = m.ReadInt("v"); Btns_text_recalc();} - else if (ctx.Match(k, Cfg__min_chars)) {btns__min_chars = m.ReadInt("v"); Btns_text_recalc();} - - else if (ctx.Match(k, Cfg__javascript_enabled)) Tabs_javascript_enabled_(m.ReadYnOrY("v")); // NOTE: must be "OrY" else broken cfg.db will break cfg_maint; DATE:2016-12-15 - else if (ctx.Match(k, Cfg__page_load_mode)) Page_load_mode_(m.ReadStr("v")); - else return Gfo_invk_.Rv_unhandled; - return this; - } - private void Page_load_mode_(String v) { - page_load_mode = Gxw_html_load_tid_.Xto_tid(v); - // Gfo_evt_mgr_.Pub_val(this, Evt_load_tid_changed, load_tid); - } - - public static final String - Invk_tabs_select_fwd = "tabs_select_fwd" , Invk_tabs_select_bwd = "tabs_select_bwd" - , Invk_tabs_switch_cur_fwd = "tabs_switch_cur_fwd" , Invk_tabs_switch_cur_bwd = "tabs_switch_cur_bwd" - , Invk_tabs_new_dflt__at_dflt__focus_y = "tabs_new_dflt__at_dflt__focus_y" - , Invk_tabs_new_link__at_dflt__focus_n = "tabs_new_link__at_dflt__focus_n" - , Invk_tabs_new_link__at_dflt__focus_y = "tabs_new_link__at_dflt__focus_y" - , Invk_tabs_close_cur = "tabs_close_cur" - ; - private static final String - Cfg__place_on_top = "xowa.gui.tabs.place_on_top" - , Cfg__height = "xowa.gui.tabs.height" - , Cfg__hide_if_one = "xowa.gui.tabs.hide_if_one" - , Cfg__curved = "xowa.gui.tabs.curved" - , Cfg__close_btn_visible = "xowa.gui.tabs.close_btn_visible" - , Cfg__unselected_close_btn_visible = "xowa.gui.tabs.unselected_close_btn_visible" - , Cfg__max_chars = "xowa.gui.tabs.max_chars" - , Cfg__min_chars = "xowa.gui.tabs.min_chars" - , Cfg__javascript_enabled = "xowa.gui.html_box.javascript_enabled" - , Cfg__page_load_mode = "xowa.gui.html_box.page_load_mode" - ; -} +package gplx.xowa.guis.views; + +import gplx.Bool_; +import gplx.Err_; +import gplx.GfoMsg; +import gplx.Gfo_evt_itm; +import gplx.Gfo_evt_mgr; +import gplx.Gfo_evt_mgr_; +import gplx.Gfo_invk_; +import gplx.GfsCtx; +import gplx.Int_; +import gplx.List_adp; +import gplx.List_adp_; +import gplx.Ordered_hash; +import gplx.Ordered_hash_; +import gplx.String_; +import gplx.gfui.controls.gxws.Gxw_html_load_tid_; +import gplx.gfui.controls.standards.Gfui_tab_itm; +import gplx.gfui.controls.standards.Gfui_tab_itm_data; +import gplx.gfui.controls.standards.Gfui_tab_mgr; +import gplx.gfui.controls.tabs.TabBox_; +import gplx.gfui.kits.core.Gfui_kit; +import gplx.gfui.kits.swts.Swt_html_utl; +import gplx.xowa.Xoa_ttl; +import gplx.xowa.Xoa_url; +import gplx.xowa.Xoae_page; +import gplx.xowa.Xowe_wiki; +import gplx.xowa.htmls.hrefs.Xoh_href_; +import gplx.xowa.specials.Xow_special_meta_; + +public class Xog_tab_mgr implements Gfo_evt_itm { + private Ordered_hash tab_regy = Ordered_hash_.New(); private int tab_uid = 0; + private boolean btns__hide_if_one; private int btns__height; + public Xog_tab_mgr(Xog_win_itm win) { + this.win = win; + ev_mgr = new Gfo_evt_mgr(this); + } + public Gfo_evt_mgr Evt_mgr() {return ev_mgr;} private Gfo_evt_mgr ev_mgr; + public Xog_win_itm Win() {return win;} private Xog_win_itm win; + public Gfui_tab_mgr Tab_mgr() {return tab_mgr;} private Gfui_tab_mgr tab_mgr; + public int Btns__min_chars() {return btns__min_chars;} private int btns__min_chars; + public int Btns__max_chars() {return btns__max_chars;} private int btns__max_chars; + public boolean Javascript_enabled() {return javascript_enabled;} private boolean javascript_enabled = true; + private byte page_load_mode; + public boolean Page_load_mode_is_url() {return page_load_mode == Gxw_html_load_tid_.Tid_url;} + public void Init_by_kit(Gfui_kit kit) { + tab_mgr = kit.New_tab_mgr("xowa.tab_mgr", win.Win_box()); + active_tab = Xog_tab_itm_.Null; + Gfo_evt_mgr_.Sub_same_many(tab_mgr, this, Gfui_tab_mgr.Evt_tab_selected, Gfui_tab_mgr.Evt_tab_closed, Gfui_tab_mgr.Evt_tab_switched); + win.App().Cfg().Bind_many_app(this, Cfg__page_load_mode + , Cfg__place_on_top, Cfg__height, Cfg__hide_if_one, Cfg__curved, Cfg__close_btn_visible, Cfg__unselected_close_btn_visible, Cfg__max_chars, Cfg__min_chars); + } + public Xog_tab_itm Active_tab() {return active_tab;} private Xog_tab_itm active_tab; + public Xog_tab_itm Active_tab_assert() { + if (active_tab == Xog_tab_itm_.Null) this.Tabs_new_dflt(true); + return active_tab; + } + public boolean Active_tab_is_null() {return active_tab == Xog_tab_itm_.Null;} + private void Btns_text_recalc() { + int len = this.Tabs_len(); + for (int i = 0; i < len; i++) { + Xog_tab_itm tab_itm = this.Tabs_get_at(i); + tab_itm.Tab_name_(); + } + } + public int Tabs_len() {return tab_regy.Count();} + public Xog_tab_itm Tabs_new_init(Xowe_wiki wiki, Xoae_page page) {return this.Tabs_new(true, true, wiki, page);} + public Xog_tab_itm Tabs_get_at(int i) {return (Xog_tab_itm)tab_regy.Get_at(i);} + public Xog_tab_itm Tabs_new_dflt() {return Tabs_new_dflt(false);} + public Xog_tab_itm Tabs_new_dflt(boolean focus) { + boolean active_tab_is_null = this.Active_tab_is_null(); + Xowe_wiki cur_wiki = active_tab_is_null ? win.App().Usere().Wiki() : active_tab.Wiki(); + Xoa_ttl ttl = Xoa_ttl.Parse(cur_wiki, Xow_special_meta_.Itm__default_tab.Ttl_bry()); + Xoa_url url = cur_wiki.Utl__url_parser().Parse_by_urlbar_or_null(ttl.Full_db_as_str()); if (url == null) throw Err_.new_("url", "invalid url", "url", url); + Xog_tab_itm rv = Tabs_new(focus, active_tab_is_null, cur_wiki, Xoae_page.New(cur_wiki, ttl)); + rv.Page_update_ui(); + rv.Show_url_bgn(url); + return rv; + } + private Xog_tab_itm Tabs_new(boolean focus, boolean active_tab_is_null, Xowe_wiki wiki, Xoae_page page) { + String tab_key = "tab_" + Int_.To_str(tab_uid++); int tab_idx = tab_regy.Count(); + Gfui_tab_itm_data tab_data = new Gfui_tab_itm_data(tab_key, tab_idx); + Xog_tab_itm rv = new Xog_tab_itm(this, tab_data, wiki, page); + Gfui_tab_itm tab_box = tab_mgr.Tabs_add(tab_data); + rv.Make_html_box(tab_uid, tab_box, win, tab_mgr); + rv.Html_itm().Js_enabled_(javascript_enabled); + tab_box.Subs_add(rv.Html_itm().Html_box()); + tab_regy.Add(tab_key, rv); + if ( focus + || active_tab_is_null // NOTE: must select 1st tab, else nothing will show in tab box + ) { + tab_mgr.Tabs_select_by_idx(rv.Tab_idx()); + active_tab = rv; + } + Tabs_hide_if_one_chk(false); + return rv; + } + public void Tabs_new_dupe(boolean focus) { + if (this.Active_tab_is_null()) return; + String url = active_tab.Page().Url().To_str(); + Tabs_new_dflt(focus); + win.Page__navigate_by_url_bar(url); + } + public void Tabs_javascript_enabled_(boolean v) { + this.javascript_enabled = v; + int len = tab_regy.Count(); + for (int i = 0; i < len; i++) { + Xog_tab_itm tab = Tabs_get_by_idx_or_warn(i); + tab.Html_itm().Js_enabled_(v); + } + } + private void Tabs_selected(String key) { + Xog_tab_itm tab = Tabs_get_by_key_or_warn(key); if (tab == null) return; + active_tab = tab; + Xoae_page page = tab.Page(); + Xog_tab_itm_read_mgr.Update_selected_tab(win, page.Url(), page.Ttl()); + tab.Html_itm().Tab_selected(page); + } + public void Tabs_close_cur() { + if (this.Active_tab_is_null()) return; + Tabs__pub_close(active_tab); + tab_mgr.Tabs_close_by_idx(active_tab.Tab_idx()); + Xog_tab_itm cur_tab = this.Active_tab(); // get new current tab for line below + if (cur_tab != null) cur_tab.Html_box().Focus(); // NOTE: needed to focus tab box else tab button will be focused; DATE:2014-07-13 + } + public void Tabs_close_others() {this.Tabs_close_to_bgn(); this.Tabs_close_to_end();} + public void Tabs_close_to_bgn() {if (Active_tab_is_null()) return; Tabs_close_rng(0 , active_tab.Tab_idx());} + public void Tabs_close_to_end() {if (Active_tab_is_null()) return; Tabs_close_rng(active_tab.Tab_idx() + 1 , tab_regy.Count());} + public void Tabs_close_rng(int bgn, int end) { + for (int i = bgn; i < end; i++) { + Xog_tab_itm tab = Tabs_get_at(bgn); + if (!Tabs__pub_close(tab)) return; + } + for (int i = bgn; i < end; i++) + tab_mgr.Tabs_close_by_idx(bgn); // NOTE: close at bgn, not at i, b/c each close will remove a tab from collection + } + public boolean Tabs__pub_close_all() {return Tabs__pub_close_rng(0, this.Tabs_len());} + public boolean Tabs__pub_close_rng(int bgn, int end) { + boolean rv = true; + for (int i = bgn; i < end; i++) { + Xog_tab_itm tab = Tabs_get_at(i); + boolean close_allowed = Tabs__pub_close(tab); + if (!close_allowed) rv = false; + } + return rv; + } + public boolean Tabs__pub_close(Xog_tab_itm tab) { + return tab.Page().Tab_data().Close_mgr().When_close(tab, Xoa_url.Null); + } + public void Tabs_close_undo() { + if (closed_undo_list.Count() == 0) return; + String url = (String)List_adp_.Pop(closed_undo_list); + Tabs_new_dflt(true); + win.Page__navigate_by_url_bar(url); + } + private List_adp closed_undo_list = List_adp_.New(); + private void Tabs_closed(String key) { + Xog_tab_itm itm = Tabs_get_by_key_or_warn(key); if (itm == null) return; + itm.Html_box().Html_dispose(); + closed_undo_list.Add(itm.Page().Url().To_str()); + tab_regy.Del(key); + if (tab_regy.Count() == 0) { + active_tab = Xog_tab_itm_.Null; + Xog_tab_itm_read_mgr.Update_selected_tab_blank(win); + } + else + Tabs_recalc_idx(); + Tabs_hide_if_one_chk(false); + } + private Xog_tab_itm Tabs_get_by_key_or_warn(String key) { + Xog_tab_itm rv = (Xog_tab_itm)tab_regy.Get_by(key); if (rv == null) win.App().Usr_dlg().Warn_many("", "", "tab.selected could not find tab; key={0}", key); + return rv; + } + private Xog_tab_itm Tabs_get_by_idx_or_warn(int idx) { + Xog_tab_itm rv = (Xog_tab_itm)tab_regy.Get_at(idx); if (rv == null) win.App().Usr_dlg().Warn_many("", "", "tab.selected could not find tab; idx={0}", idx); + return rv; + } + private void Tabs_recalc_idx() { + int len = tab_regy.Count(); + for (int i = 0; i < len; i++) { + Xog_tab_itm itm = Tabs_get_by_idx_or_warn(i); + itm.Tab_idx_(i); + } + } + public void Tabs_select(boolean fwd) { + if (this.Active_tab_is_null()) return; + int new_idx = TabBox_.Cycle(fwd, active_tab.Tab_idx(), tab_regy.Count()); + tab_mgr.Tabs_select_by_idx(new_idx); + } + public void Tabs_select_by_idx(int v) { + if (v < 0 || v >= tab_regy.Count()) return; + tab_mgr.Tabs_select_by_idx(v); + } + public void Tabs_move(boolean fwd) { + if (this.Active_tab_is_null()) return; + int src_idx = active_tab.Tab_idx(); + int trg_idx = TabBox_.Cycle(fwd, src_idx, tab_regy.Count()); + tab_mgr.Tabs_switch(src_idx, trg_idx); + } + private void Tabs_switched(String src_key, String trg_key) { + Xog_tab_itm src_itm = Tabs_get_by_key_or_warn(src_key); + Xog_tab_itm trg_itm = Tabs_get_by_key_or_warn(trg_key); + src_itm.Switch_mem(trg_itm); + active_tab = trg_itm; // NOTE: src_itm initiated switch, but trg_itm is now active b/c everything in src_itm has now been reparented to trg_itm; DATE:2014-05-12 + } + public void Tabs_new_link(boolean focus, String link) { // handle empty link + if (String_.Len_eq_0(link)) { + if (this.Active_tab_is_null()) return; + link = active_tab.Html_itm().Html_selected_get_active_or_selection(); + // 2020-12-16|ISSUE#:823|Open in new tab creates links like `about:/wiki/PAGE_NAME` or `about:/site/WIKI_NAME/wiki/PAGE_NAME` + link = Swt_html_utl.NormalizeSwtUrl(link); + if (link.startsWith(Xoh_href_.Str__site)) { + link = link.substring(Xoh_href_.Str__site.length()); + } + else if (link.startsWith(Xoh_href_.Str__wiki)) { + link = active_tab.Wiki().Domain_str() + link; + } + link = gplx.langs.htmls.encoders.Gfo_url_encoder_.Http_url.Decode_str(link); // NOTE: must decode else url-encoded special pages don't work; EX:home/wiki/Special:XowaCfg%3Fgrp%3Dxowa.html.css; DATE:2017-01-02 + } + if (String_.Len_eq_0(link)) {win.App().Usr_dlg().Prog_many("", "", "no link or text selected"); return;} + Tabs_new_link(link, focus); + } + public void Tabs_new_link(String link, boolean focus) { + Xowe_wiki wiki = active_tab.Wiki(); + Xog_tab_itm new_tab = Tabs_new(focus, false, wiki, Xoae_page.New(wiki, active_tab.Page().Ttl())); // NOTE: do not use ttl from link, else middle-clicking pages with anchors won't work; DATE:2015-05-03 + Xoa_url url = wiki.Utl__url_parser().Parse_by_urlbar_or_null(link); if (url == null) return; // NOTE: link must be of form domain/wiki/page; DATE:2014-05-27 + new_tab.Show_url_bgn(url); + if (focus) + tab_mgr.Tabs_select_by_idx(new_tab.Tab_idx()); + } + private void Tabs_hide_if_one_chk(boolean force) { + if (btns__hide_if_one || force) {// run code only if enabled or forced + if (tab_regy.Count() == 1) { + int desired_height = btns__hide_if_one ? 0 : btns__height; + if (tab_mgr.Btns_height() != desired_height) + tab_mgr.Btns_height_(desired_height); + } + else { + if (tab_mgr.Btns_height() != btns__height) + tab_mgr.Btns_height_(btns__height); + } + } + } + public Object Invk(GfsCtx ctx, int ikey, String k, GfoMsg m) { + if (ctx.Match(k, Invk_tabs_new_dflt__at_dflt__focus_y)) Tabs_new_dflt(Bool_.Y); + else if (ctx.Match(k, Invk_tabs_new_link__at_dflt__focus_n)) Tabs_new_link(Bool_.N, m.ReadStrOr("v", null)); + else if (ctx.Match(k, Invk_tabs_new_link__at_dflt__focus_y)) Tabs_new_link(Bool_.Y, m.ReadStrOr("v", null)); + else if (ctx.Match(k, Gfui_tab_mgr.Evt_tab_selected)) Tabs_selected(m.ReadStr("key")); + else if (ctx.Match(k, Gfui_tab_mgr.Evt_tab_closed)) Tabs_closed(m.ReadStr("key")); + else if (ctx.Match(k, Gfui_tab_mgr.Evt_tab_switched)) Tabs_switched(m.ReadStr("src"), m.ReadStr("trg")); + else if (ctx.Match(k, Invk_tabs_close_cur)) Tabs_close_cur(); + else if (ctx.Match(k, Invk_tabs_select_bwd)) Tabs_select(Bool_.N); + else if (ctx.Match(k, Invk_tabs_select_fwd)) Tabs_select(Bool_.Y); + else if (ctx.Match(k, Invk_tabs_switch_cur_bwd)) Tabs_move(Bool_.N); + else if (ctx.Match(k, Invk_tabs_switch_cur_fwd)) Tabs_move(Bool_.Y); + + else if (ctx.Match(k, Cfg__place_on_top)) tab_mgr.Btns_place_on_top_(m.ReadYn("v")); + else if (ctx.Match(k, Cfg__height)) {btns__height = m.ReadInt("v"); tab_mgr.Btns_height_(btns__height);} + else if (ctx.Match(k, Cfg__hide_if_one)) {btns__hide_if_one = m.ReadYn("v"); Tabs_hide_if_one_chk(true);} + else if (ctx.Match(k, Cfg__curved)) tab_mgr.Btns_curved_(m.ReadYn("v")); + else if (ctx.Match(k, Cfg__close_btn_visible)) tab_mgr.Btns_close_visible_(m.ReadYn("v")); + else if (ctx.Match(k, Cfg__unselected_close_btn_visible)) tab_mgr.Btns_unselected_close_visible_(m.ReadYn("v")); + else if (ctx.Match(k, Cfg__max_chars)) {btns__max_chars = m.ReadInt("v"); Btns_text_recalc();} + else if (ctx.Match(k, Cfg__min_chars)) {btns__min_chars = m.ReadInt("v"); Btns_text_recalc();} + + else if (ctx.Match(k, Cfg__javascript_enabled)) Tabs_javascript_enabled_(m.ReadYnOrY("v")); // NOTE: must be "OrY" else broken cfg.db will break cfg_maint; DATE:2016-12-15 + else if (ctx.Match(k, Cfg__page_load_mode)) Page_load_mode_(m.ReadStr("v")); + else return Gfo_invk_.Rv_unhandled; + return this; + } + private void Page_load_mode_(String v) { + page_load_mode = Gxw_html_load_tid_.Xto_tid(v); + // Gfo_evt_mgr_.Pub_val(this, Evt_load_tid_changed, load_tid); + } + + public static final String + Invk_tabs_select_fwd = "tabs_select_fwd" , Invk_tabs_select_bwd = "tabs_select_bwd" + , Invk_tabs_switch_cur_fwd = "tabs_switch_cur_fwd" , Invk_tabs_switch_cur_bwd = "tabs_switch_cur_bwd" + , Invk_tabs_new_dflt__at_dflt__focus_y = "tabs_new_dflt__at_dflt__focus_y" + , Invk_tabs_new_link__at_dflt__focus_n = "tabs_new_link__at_dflt__focus_n" + , Invk_tabs_new_link__at_dflt__focus_y = "tabs_new_link__at_dflt__focus_y" + , Invk_tabs_close_cur = "tabs_close_cur" + ; + private static final String + Cfg__place_on_top = "xowa.gui.tabs.place_on_top" + , Cfg__height = "xowa.gui.tabs.height" + , Cfg__hide_if_one = "xowa.gui.tabs.hide_if_one" + , Cfg__curved = "xowa.gui.tabs.curved" + , Cfg__close_btn_visible = "xowa.gui.tabs.close_btn_visible" + , Cfg__unselected_close_btn_visible = "xowa.gui.tabs.unselected_close_btn_visible" + , Cfg__max_chars = "xowa.gui.tabs.max_chars" + , Cfg__min_chars = "xowa.gui.tabs.min_chars" + , Cfg__javascript_enabled = "xowa.gui.html_box.javascript_enabled" + , Cfg__page_load_mode = "xowa.gui.html_box.page_load_mode" + ; +}