OpenCPN Partial API docs
Loading...
Searching...
No Matches
ocpn_aui_manager.cpp
Go to the documentation of this file.
1/**************************************************************************
2 * Copyright (C) 2018 by David S. Register *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
16 **************************************************************************/
17
23#include "gl_headers.h" // Must come before anything using GL stuff
24#ifndef WX_PRECOMP
25#include <wx/wx.h>
26#endif
27
28#include "ocpn_aui_manager.h"
29#include "ocpn_plugin.h"
30
31#ifdef __WXMSW__
32#include <wx/msw/wrapwin.h>
33#include <wx/msw/private.h>
34#include <wx/msw/dc.h>
35#endif
36
37// -- static utility functions --
38
40
41static wxBitmap wxOPaneCreateStippleBitmap() {
42 unsigned char data[] = {0, 0, 0, 192, 192, 192, 192, 192, 192, 0, 0, 0};
43 wxImage img(2, 2, data, true);
44 return wxBitmap(img);
45}
46
47static void ODrawResizeHint(wxDC& dc, const wxRect& rect) {
48#if 1
49 wxBitmap stipple = wxOPaneCreateStippleBitmap();
50 wxBrush brush(stipple);
51 dc.SetBrush(brush);
52#ifdef __WXMSW__
53 wxMSWDCImpl* impl = (wxMSWDCImpl*)dc.GetImpl();
54 PatBlt(GetHdcOf(*impl), rect.GetX(), rect.GetY(), rect.GetWidth(),
55 rect.GetHeight(), PATINVERT);
56#else
57 dc.SetPen(*wxTRANSPARENT_PEN);
58
59 dc.SetLogicalFunction(wxXOR);
60 dc.DrawRectangle(rect);
61#endif
62#endif
63}
64
65// Convenience function
66static bool OAuiManager_HasLiveResize(wxAuiManager& manager) {
67 // With Core Graphics on Mac, it's not possible to show sash feedback,
68 // so we'll always use live update instead.
69#if defined(__WXMAC__)
70 wxUnusedVar(manager);
71 return true;
72#else
73 return (manager.GetFlags() & wxAUI_MGR_LIVE_RESIZE) == wxAUI_MGR_LIVE_RESIZE;
74#endif
75}
76
77// OCPN_AUIManager Implementation
78
79BEGIN_EVENT_TABLE(OCPN_AUIManager, wxEvtHandler)
80EVT_AUI_PANE_BUTTON(wxAuiManager::OnPaneButton)
81EVT_AUI_RENDER(wxAuiManager::OnRender)
82EVT_PAINT(OCPN_AUIManager::OnPaint)
83EVT_ERASE_BACKGROUND(OCPN_AUIManager::OnEraseBackground)
84EVT_SIZE(OCPN_AUIManager::OnSize)
85EVT_SET_CURSOR(OCPN_AUIManager::OnSetCursor)
86EVT_LEFT_DOWN(OCPN_AUIManager::OnLeftDown)
87EVT_LEFT_UP(OCPN_AUIManager::OnLeftUp)
88EVT_MOTION(OCPN_AUIManager::OnMotionx)
89EVT_LEAVE_WINDOW(OCPN_AUIManager::OnLeaveWindow)
90EVT_MOUSE_CAPTURE_LOST(OCPN_AUIManager::OnCaptureLost)
91EVT_CHILD_FOCUS(OCPN_AUIManager::OnChildFocus)
92EVT_AUI_FIND_MANAGER(OCPN_AUIManager::OnFindManager)
93END_EVENT_TABLE()
94
95OCPN_AUIManager::OCPN_AUIManager(wxWindow* managed_wnd, unsigned int flags)
96 : wxAuiManager(managed_wnd, flags)
97
98{}
99
100OCPN_AUIManager::~OCPN_AUIManager() {}
101
102void OCPN_AUIManager::OnMotionx(wxMouseEvent& event) {
103 // sometimes when Update() is called from inside this method,
104 // a spurious mouse move event is generated; this check will make
105 // sure that only real mouse moves will get anywhere in this method;
106 // this appears to be a bug somewhere, and I don't know where the
107 // mouse move event is being generated. only verified on MSW
108
109 wxPoint mouse_pos = event.GetPosition();
110 if (m_lastMouseMove == mouse_pos) return;
111 m_lastMouseMove = mouse_pos;
112
113 if (m_action == actionResize) {
114 // It's necessary to reset m_actionPart since it destroyed
115 // by the Update within DoEndResizeAction.
116 if (m_currentDragItem != -1)
117 m_actionPart = &(m_uiParts.Item(m_currentDragItem));
118 else {
119 // Find by address: wx 3.3 array Index() needs operator==
120 for (size_t i = 0; i < m_uiParts.GetCount(); i++) {
121 if (&m_uiParts.Item(i) == m_actionPart) {
122 m_currentDragItem = i;
123 break;
124 }
125 }
126 }
127
128 if (m_actionPart) {
129 wxPoint pos = m_actionPart->rect.GetPosition();
130 if (m_actionPart->orientation == wxHORIZONTAL)
131 pos.y = wxMax(0, event.m_y - m_actionOffset.y);
132 else
133 pos.x = wxMax(0, event.m_x - m_actionOffset.x);
134
135 wxSize client_size = m_frame->GetClientSize();
136 int used_width = 0, used_height = 0;
137
138 size_t dock_i, dock_count = m_docks.GetCount();
139 for (dock_i = 0; dock_i < dock_count; ++dock_i) {
140 wxAuiDockInfo& dock = m_docks.Item(dock_i);
141 if (dock.dock_direction == wxAUI_DOCK_TOP ||
142 dock.dock_direction == wxAUI_DOCK_BOTTOM) {
143 used_height += dock.size;
144 }
145 if (dock.dock_direction == wxAUI_DOCK_LEFT ||
146 dock.dock_direction == wxAUI_DOCK_RIGHT) {
147 used_width += dock.size;
148 }
149 // if (dock.resizable)
150 // used_width += sashSize;
151 }
152
153 if (OAuiManager_HasLiveResize(*this)) {
154 m_frame->ReleaseMouse();
155 if ((used_width < client_size.x * 9 / 10) &&
156 (used_width > client_size.x * 1 / 10))
157 DoEndResizeAction(event);
158
159 m_frame->CaptureMouse();
160 } else {
161 bool bhasMouse = m_frame->HasCapture();
162
163 if (bhasMouse) m_frame->ReleaseMouse();
164
165 // Tell MyFrame that the sash is moving, so that he
166 // may disable any top-level windows and so avoid mouse focus problems.
167
168 wxRect rect(m_frame->ClientToScreen(pos), m_actionPart->rect.GetSize());
169 wxScreenDC dc;
170
171 if (!m_0actionHintRect.IsEmpty()) {
172 // remove old resize hint
173 ODrawResizeHint(dc, m_0actionHintRect);
174 m_0actionHintRect = wxRect();
175 }
176
177 wxRect frameScreenRect = m_frame->GetScreenRect();
178
179 rect.x =
180 wxMax(rect.x, frameScreenRect.x + frameScreenRect.width * 1 / 10);
181 rect.x =
182 wxMin(rect.x, frameScreenRect.x + frameScreenRect.width * 9 / 10);
183
184 // draw new resize hint, if it's inside the managed frame
185 if (1 /*frameScreenRect.Contains(rect)*/) {
186 ODrawResizeHint(dc, rect);
187 m_0actionHintRect = rect;
188 }
189
190 if (bhasMouse) m_frame->CaptureMouse();
191 }
192 }
193 } else {
194 OnMotion(event);
195 }
196}
197
198bool OCPN_AUIManager::DoEndResizeAction(wxMouseEvent& event) {
199 // resize the dock or the pane
200 if (m_actionPart && m_actionPart->type == wxAuiDockUIPart::typeDockSizer) {
201 // first, we must calculate the maximum size the dock may be
202 int sashSize = m_art->GetMetric(wxAUI_DOCKART_SASH_SIZE);
203
204 int used_width = 0, used_height = 0;
205
206 wxSize client_size = m_frame->GetClientSize();
207
208 size_t dock_i, dock_count = m_docks.GetCount();
209 for (dock_i = 0; dock_i < dock_count; ++dock_i) {
210 wxAuiDockInfo& dock = m_docks.Item(dock_i);
211 if (dock.dock_direction == wxAUI_DOCK_TOP ||
212 dock.dock_direction == wxAUI_DOCK_BOTTOM) {
213 used_height += dock.size;
214 }
215 if (dock.dock_direction == wxAUI_DOCK_LEFT ||
216 dock.dock_direction == wxAUI_DOCK_RIGHT) {
217 used_width += dock.size;
218 }
219 if (dock.resizable) used_width += sashSize;
220 }
221
222 int available_width = client_size.GetWidth() - used_width;
223 int available_height = client_size.GetHeight() - used_height;
224
225#if wxUSE_STATUSBAR
226 // if there's a status control, the available
227 // height decreases accordingly
228 if (dynamic_cast<wxFrame*>(m_frame)) {
229 wxFrame* frame = static_cast<wxFrame*>(m_frame);
230 wxStatusBar* status = frame->GetStatusBar();
231 if (status) {
232 wxSize status_client_size = status->GetClientSize();
233 available_height -= status_client_size.GetHeight();
234 }
235 }
236#endif
237
238 wxRect& rect = m_actionPart->dock->rect;
239
240 wxPoint new_pos(event.m_x - m_actionOffset.x, event.m_y - m_actionOffset.y);
241 int new_size, old_size = m_actionPart->dock->size;
242
243 switch (m_actionPart->dock->dock_direction) {
244 case wxAUI_DOCK_LEFT:
245 new_size = new_pos.x - rect.x;
246 if (new_size - old_size > available_width)
247 new_size = old_size + available_width;
248 m_actionPart->dock->size = new_size;
249 break;
250 case wxAUI_DOCK_TOP:
251 new_size = new_pos.y - rect.y;
252 if (new_size - old_size > available_height)
253 new_size = old_size + available_height;
254 m_actionPart->dock->size = new_size;
255 break;
256 case wxAUI_DOCK_RIGHT:
257 new_size =
258 rect.x + rect.width - new_pos.x - m_actionPart->rect.GetWidth();
259 if (new_size - old_size > available_width)
260 new_size = old_size + available_width;
261 m_actionPart->dock->size = new_size;
262
263 m_actionPart->dock->size =
264 wxMax(m_actionPart->dock->size, client_size.x * 1 / 10);
265 m_actionPart->dock->size =
266 wxMin(m_actionPart->dock->size, client_size.x * 9 / 10);
267
268 break;
269 case wxAUI_DOCK_BOTTOM:
270 new_size =
271 rect.y + rect.height - new_pos.y - m_actionPart->rect.GetHeight();
272 if (new_size - old_size > available_height)
273 new_size = old_size + available_height;
274 m_actionPart->dock->size = new_size;
275 break;
276 }
277
278 Update();
279 Repaint(NULL);
280 } else if (m_actionPart &&
281 m_actionPart->type == wxAuiDockUIPart::typePaneSizer) {
282 wxAuiDockInfo& dock = *m_actionPart->dock;
283 wxAuiPaneInfo& pane = *m_actionPart->pane;
284
285 int total_proportion = 0;
286 int dock_pixels = 0;
287 int new_pixsize = 0;
288
289 int caption_size = m_art->GetMetric(wxAUI_DOCKART_CAPTION_SIZE);
290 int pane_borderSize = m_art->GetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE);
291 int sashSize = m_art->GetMetric(wxAUI_DOCKART_SASH_SIZE);
292
293 wxPoint new_pos(event.m_x - m_actionOffset.x, event.m_y - m_actionOffset.y);
294
295 // determine the pane rectangle by getting the pane part
296 wxAuiDockUIPart* pane_part = GetPanePart(pane.window);
297 wxASSERT_MSG(pane_part, "Pane border part not found -- shouldn't happen");
298
299 // determine the new pixel size that the user wants;
300 // this will help us recalculate the pane's proportion
301 if (dock.IsHorizontal())
302 new_pixsize = new_pos.x - pane_part->rect.x;
303 else
304 new_pixsize = new_pos.y - pane_part->rect.y;
305
306 // determine the size of the dock, based on orientation
307 if (dock.IsHorizontal())
308 dock_pixels = dock.rect.GetWidth();
309 else
310 dock_pixels = dock.rect.GetHeight();
311
312 // determine the total proportion of all resizable panes,
313 // and the total size of the dock minus the size of all
314 // the fixed panes
315 int i, dock_pane_count = dock.panes.GetCount();
316 int pane_position = -1;
317 for (i = 0; i < dock_pane_count; ++i) {
318 wxAuiPaneInfo& p = *dock.panes.Item(i);
319 if (p.window == pane.window) pane_position = i;
320
321 // while we're at it, subtract the pane sash
322 // width from the dock width, because this would
323 // skew our proportion calculations
324 if (i > 0) dock_pixels -= sashSize;
325
326 // also, the whole size (including decorations) of
327 // all fixed panes must also be subtracted, because they
328 // are not part of the proportion calculation
329 if (p.IsFixed()) {
330 if (dock.IsHorizontal())
331 dock_pixels -= p.best_size.x;
332 else
333 dock_pixels -= p.best_size.y;
334 } else {
335 total_proportion += p.dock_proportion;
336 }
337 }
338
339 // new size can never be more than the number of dock pixels
340 if (new_pixsize > dock_pixels) new_pixsize = dock_pixels;
341
342 // find a pane in our dock to 'steal' space from or to 'give'
343 // space to -- this is essentially what is done when a pane is
344 // resized; the pane should usually be the first non-fixed pane
345 // to the right of the action pane
346 int borrow_pane = -1;
347 for (i = pane_position + 1; i < dock_pane_count; ++i) {
348 wxAuiPaneInfo& p = *dock.panes.Item(i);
349 if (!p.IsFixed()) {
350 borrow_pane = i;
351 break;
352 }
353 }
354
355 // demand that the pane being resized is found in this dock
356 // (this assert really never should be raised)
357 wxASSERT_MSG(pane_position != -1, "Pane not found in dock");
358
359 // prevent division by zero
360 if (dock_pixels == 0 || total_proportion == 0 || borrow_pane == -1) {
361 m_action = actionNone;
362 return false;
363 }
364
365 // calculate the new proportion of the pane
366 int new_proportion = (new_pixsize * total_proportion) / dock_pixels;
367
368 // default minimum size
369 int min_size = 0;
370
371 // check against the pane's minimum size, if specified. please note
372 // that this is not enough to ensure that the minimum size will
373 // not be violated, because the whole frame might later be shrunk,
374 // causing the size of the pane to violate it's minimum size
375 if (pane.min_size.IsFullySpecified()) {
376 min_size = 0;
377
378 if (pane.HasBorder()) min_size += (pane_borderSize * 2);
379
380 // calculate minimum size with decorations (border,caption)
381 if (pane_part->orientation == wxVERTICAL) {
382 min_size += pane.min_size.y;
383 if (pane.HasCaption()) min_size += caption_size;
384 } else {
385 min_size += pane.min_size.x;
386 }
387 }
388
389 // for some reason, an arithmatic error somewhere is causing
390 // the proportion calculations to always be off by 1 pixel;
391 // for now we will add the 1 pixel on, but we really should
392 // determine what's causing this.
393 min_size++;
394
395 int min_proportion = (min_size * total_proportion) / dock_pixels;
396
397 if (new_proportion < min_proportion) new_proportion = min_proportion;
398
399 int prop_diff = new_proportion - pane.dock_proportion;
400
401 // borrow the space from our neighbor pane to the
402 // right or bottom (depending on orientation);
403 // also make sure we don't make the neighbor too small
404 int prop_borrow = dock.panes.Item(borrow_pane)->dock_proportion;
405
406 if (prop_borrow - prop_diff < 0) {
407 // borrowing from other pane would make it too small,
408 // so cancel the resize operation
409 prop_borrow = min_proportion;
410 } else {
411 prop_borrow -= prop_diff;
412 }
413
414 dock.panes.Item(borrow_pane)->dock_proportion = prop_borrow;
415 pane.dock_proportion = new_proportion;
416
417 // repaint
418 Update();
419 Repaint(NULL);
420 }
421
422 return true;
423}
424
425void OCPN_AUIManager::OnLeftUp(wxMouseEvent& event) {
426 if (m_action == actionResize) {
427 m_frame->ReleaseMouse();
428
429 if (!OAuiManager_HasLiveResize(*this)) {
430 // get rid of the hint rectangle
431 wxScreenDC dc;
432 ODrawResizeHint(dc, m_0actionHintRect);
433 }
434 if (m_currentDragItem != -1 && OAuiManager_HasLiveResize(*this))
435 m_actionPart = &(m_uiParts.Item(m_currentDragItem));
436
437 DoEndResizeAction(event);
438
439 m_currentDragItem = -1;
440
441 } else if (m_action == actionClickButton) {
442 m_hoverButton = NULL;
443 m_frame->ReleaseMouse();
444
445 if (m_actionPart) {
446#if wxCHECK_VERSION(3, 3, 2)
447 UpdateButtonOnScreen(m_actionPart,
448 m_actionPart == HitTest(event.GetX(), event.GetY())
449 ? wxAUI_BUTTON_STATE_HOVER
450 : wxAUI_BUTTON_STATE_NORMAL);
451#else
452 UpdateButtonOnScreen(m_actionPart, event);
453#endif
454
455 // make sure we're still over the item that was originally clicked
456 if (m_actionPart == HitTest(event.GetX(), event.GetY())) {
457 // fire button-click event
458 wxAuiManagerEvent e(wxEVT_AUI_PANE_BUTTON);
459 e.SetManager(this);
460 e.SetPane(m_actionPart->pane);
461
462#if wxCHECK_VERSION(3, 1, 4)
463 e.SetButton(m_actionPart->button);
464#else
465 e.SetButton(m_actionPart->button->button_id);
466#endif
467 ProcessMgrEvent(e);
468 }
469 }
470 } else if (m_action == actionClickCaption) {
471 m_frame->ReleaseMouse();
472 } else if (m_action == actionDragFloatingPane) {
473 m_frame->ReleaseMouse();
474 }
475#if 0
476 else if (m_action == actionDragToolbarPane)
477 {
478 m_frame->ReleaseMouse();
479
480 wxAuiPaneInfo& pane = GetPane(m_actionWindow);
481 wxASSERT_MSG(pane.IsOk(), "Pane window not found");
482
483 // save the new positions
484 wxAuiDockInfoPtrArray docks;
485 FindDocks(m_docks, pane.dock_direction,
486 pane.dock_layer, pane.dock_row, docks);
487 if (docks.GetCount() == 1)
488 {
489 wxAuiDockInfo& dock = *docks.Item(0);
490
491 wxArrayInt pane_positions, pane_sizes;
492 GetPanePositionsAndSizes(dock, pane_positions, pane_sizes);
493
494 int i, dock_pane_count = dock.panes.GetCount();
495 for (i = 0; i < dock_pane_count; ++i)
496 dock.panes.Item(i)->dock_pos = pane_positions[i];
497 }
498
499 pane.state &= ~wxAuiPaneInfo::actionPane;
500 Update();
501 }
502#endif
503 else {
504 event.Skip();
505 }
506
507 m_action = actionNone;
508 m_lastMouseMove = wxPoint(); // see comment in OnMotion()
509}
510
511// FindDocks() is an internal function that returns a list of docks which meet
512// the specified conditions in the parameters and returns a sorted array
513// (sorted by layer and then row)
514static void OCPNFindDocks(wxAuiDockInfoArray& docks, int dock_direction,
515 int dock_layer, int dock_row,
516 wxAuiDockInfoPtrArray& arr) {
517 int begin_layer = dock_layer;
518 int end_layer = dock_layer;
519 int begin_row = dock_row;
520 int end_row = dock_row;
521 int dock_count = docks.GetCount();
522 int layer, row, i, max_row = 0, max_layer = 0;
523
524 // discover the maximum dock layer and the max row
525 for (i = 0; i < dock_count; ++i) {
526 max_row = wxMax(max_row, docks.Item(i).dock_row);
527 max_layer = wxMax(max_layer, docks.Item(i).dock_layer);
528 }
529
530 // if no dock layer was specified, search all dock layers
531 if (dock_layer == -1) {
532 begin_layer = 0;
533 end_layer = max_layer;
534 }
535
536 // if no dock row was specified, search all dock row
537 if (dock_row == -1) {
538 begin_row = 0;
539 end_row = max_row;
540 }
541
542 arr.Clear();
543
544 for (layer = begin_layer; layer <= end_layer; ++layer)
545 for (row = begin_row; row <= end_row; ++row)
546 for (i = 0; i < dock_count; ++i) {
547 wxAuiDockInfo& d = docks.Item(i);
548 if (dock_direction == -1 || dock_direction == d.dock_direction) {
549 if (d.dock_layer == layer && d.dock_row == row) arr.Add(&d);
550 }
551 }
552}
553
554wxAuiDockInfo* OCPN_AUIManager::FindDock(wxAuiPaneInfo& pane) {
555 wxAuiDockInfoPtrArray arr;
556 OCPNFindDocks(m_docks, pane.dock_direction, pane.dock_layer, pane.dock_row,
557 arr);
558 if (arr.GetCount())
559 return arr.Item(0);
560 else
561 return NULL;
562}
563
564void OCPN_AUIManager::SetDockSize(wxAuiDockInfo* dock, int size) {
565 dock->size = size;
566
567 Update();
568 Repaint(NULL);
569}
570
571bool OCPN_AUIManager::ProcessDockResult(wxAuiPaneInfo& target,
572 const wxAuiPaneInfo& new_pos) {
573 // printf("DockResult direction: %d layer: %d position: %d %d\n" ,
574 // new_pos.dock_direction, new_pos.dock_layer, new_pos.dock_pos,
575 // GetCanvasIndexUnderMouse());
576
577 // If we are docking a Dashboard window, we restrict the spots that can accept
578 // the docking action
579 if (new_pos.window->GetName().IsSameAs("panel")) {
580 // Dashboards can not go on the left( interferes with global toolbar )
581 if (/*(new_pos.dock_layer != 1) ||*/ (new_pos.dock_direction ==
582 wxAUI_DOCK_LEFT))
583 return false;
584
585 // Also, in multi-canvas mode, the dashboard is restricted to layer 1 in
586 // right hand canvas. This forces it to dock at the far right only.
587 if (GetCanvasCount() > 1) {
588 if (GetCanvasIndexUnderMouse() > 0) {
589 if (new_pos.dock_layer == 0) return false;
590 }
591 }
592 }
593
594 return wxAuiManager::ProcessDockResult(target, new_pos);
595}
Platform independent GL includes.
OCPN_AUIManager * g_pauimgr
Global instance.
OCPN_AUIManager.
PlugIn Object Definition/API.
int GetCanvasCount()
Gets total number of chart canvases.
int GetCanvasIndexUnderMouse()
Gets index of chart canvas under mouse cursor.