1 //////////////////////////////////////////////////////////////////////// 2 // 3 // Copyright (C) 2011-2021 The Octave Project Developers 4 // 5 // See the file COPYRIGHT.md in the top-level directory of this 6 // distribution or <https://octave.org/copyright/>. 7 // 8 // This file is part of Octave. 9 // 10 // Octave is free software: you can redistribute it and/or modify it 11 // under the terms of the GNU General Public License as published by 12 // the Free Software Foundation, either version 3 of the License, or 13 // (at your option) any later version. 14 // 15 // Octave is distributed in the hope that it will be useful, but 16 // WITHOUT ANY WARRANTY; without even the implied warranty of 17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 // GNU General Public License for more details. 19 // 20 // You should have received a copy of the GNU General Public License 21 // along with Octave; see the file COPYING. If not, see 22 // <https://www.gnu.org/licenses/>. 23 // 24 //////////////////////////////////////////////////////////////////////// 25 26 #if defined (HAVE_CONFIG_H) 27 # include "config.h" 28 #endif 29 30 #include <list> 31 32 #include <QApplication> 33 #include <QKeyEvent> 34 #include <QMouseEvent> 35 36 #include "Container.h" 37 #include "KeyMap.h" 38 #include "Object.h" 39 #include "QtHandlesUtils.h" 40 #include "qt-graphics-toolkit.h" 41 42 #include "oct-string.h" 43 44 #include "graphics.h" 45 #include "ov.h" 46 47 namespace QtHandles 48 { 49 50 namespace Utils 51 { 52 53 QString fromStdString(const std::string & s)54 fromStdString (const std::string& s) 55 { 56 return QString::fromUtf8 (s.c_str ()); 57 } 58 59 std::string toStdString(const QString & s)60 toStdString (const QString& s) 61 { 62 return std::string (s.toUtf8 ().data ()); 63 } 64 65 QStringList fromStringVector(const string_vector & v)66 fromStringVector (const string_vector& v) 67 { 68 QStringList l; 69 octave_idx_type n = v.numel (); 70 71 for (octave_idx_type i = 0; i < n; i++) 72 l << fromStdString (v[i]); 73 74 return l; 75 } 76 77 string_vector toStringVector(const QStringList & l)78 toStringVector (const QStringList& l) 79 { 80 string_vector v (l.length ()); 81 int i = 0; 82 83 for (const auto& s : l) 84 v[i++] = toStdString (s); 85 86 return v; 87 } 88 toCellString(const QStringList & l)89 Cell toCellString (const QStringList& l) 90 { 91 QStringList tmp = l; 92 93 // don't get any empty lines from end of the list 94 while ((tmp.length () > 0) && tmp.last ().isEmpty ()) 95 { 96 tmp.removeLast (); 97 } 98 // no strings converts to a 1x1 cell with empty string 99 if (tmp.isEmpty ()) 100 tmp += ""; 101 102 Cell v (toStringVector (tmp)); 103 return v; 104 } 105 106 template <typename T> 107 QFont computeFont(const typename T::properties & props,int height)108 computeFont (const typename T::properties& props, int height) 109 { 110 QFont f (fromStdString (props.get_fontname ())); 111 112 static std::map<std::string, QFont::Weight> weightMap; 113 static std::map<std::string, QFont::Style> angleMap; 114 static bool mapsInitialized = false; 115 116 if (! mapsInitialized) 117 { 118 weightMap["normal"] = QFont::Normal; 119 weightMap["bold"] = QFont::Bold; 120 121 angleMap["normal"] = QFont::StyleNormal; 122 angleMap["italic"] = QFont::StyleItalic; 123 angleMap["oblique"] = QFont::StyleOblique; 124 125 mapsInitialized = true; 126 } 127 128 f.setPointSizeF (props.get___fontsize_points__ (height)); 129 f.setWeight (weightMap[props.get_fontweight ()]); 130 f.setStyle (angleMap[props.get_fontangle ()]); 131 132 return f; 133 } 134 135 template QFont computeFont<uicontrol> (const uicontrol::properties& props, 136 int height); 137 138 template QFont computeFont<uipanel> (const uipanel::properties& props, 139 int height); 140 141 template QFont computeFont<uibuttongroup> (const uibuttongroup::properties& 142 props, 143 int height); 144 145 template QFont computeFont<uitable> (const uitable::properties& props, 146 int height); 147 148 QColor fromRgb(const Matrix & rgb)149 fromRgb (const Matrix& rgb) 150 { 151 QColor c; 152 153 if (rgb.numel () == 3) 154 c.setRgbF (rgb(0), rgb(1), rgb(2)); 155 156 return c; 157 } 158 159 Matrix toRgb(const QColor & c)160 toRgb (const QColor& c) 161 { 162 Matrix rgb (1, 3); 163 double *rgbData = rgb.fortran_vec (); 164 165 // qreal is a typedef for double except for ARM CPU architectures 166 // where it is a typedef for float (Bug #44970). 167 qreal tmp[3]; 168 c.getRgbF (tmp, tmp+1, tmp+2); 169 rgbData[0] = tmp[0]; rgbData[1] = tmp[1]; rgbData[2] = tmp[2]; 170 171 return rgb; 172 } 173 174 std::string figureSelectionType(QMouseEvent * event,bool isDoubleClick)175 figureSelectionType (QMouseEvent *event, bool isDoubleClick) 176 { 177 if (isDoubleClick) 178 return "open"; 179 else 180 { 181 Qt::MouseButtons buttons = event->buttons (); 182 Qt::KeyboardModifiers mods = event->modifiers (); 183 184 if (mods == Qt::NoModifier) 185 { 186 if (buttons == Qt::LeftButton) 187 return "normal"; 188 else if (buttons == Qt::RightButton) 189 return "alt"; 190 else if (buttons == Qt::MidButton 191 || buttons == (Qt::LeftButton | Qt::RightButton)) 192 return "extend"; 193 } 194 else if (buttons == Qt::LeftButton) 195 { 196 if (mods == Qt::ShiftModifier) 197 return "extend"; 198 else if (mods == Qt::ControlModifier) 199 return "alt"; 200 } 201 } 202 203 return "normal"; 204 } 205 206 /* 207 Two figureCurrentPoint() routines are required: 208 1) Used for QMouseEvents where cursor position data is in callback from Qt. 209 2) Used for QKeyEvents where cursor position must be determined. 210 */ 211 Matrix figureCurrentPoint(const graphics_object & fig,QMouseEvent * event)212 figureCurrentPoint (const graphics_object& fig, QMouseEvent *event) 213 { 214 Object *tkFig = qt_graphics_toolkit::toolkitObject (fig); 215 216 if (tkFig) 217 { 218 Container *c = tkFig->innerContainer (); 219 220 if (c) 221 { 222 QPoint qp = c->mapFromGlobal (event->globalPos ()); 223 224 return tkFig->properties<figure> ().map_from_boundingbox (qp.x (), 225 qp.y ()); 226 } 227 } 228 229 return Matrix (1, 2, 0.0); 230 } 231 232 Matrix figureCurrentPoint(const graphics_object & fig)233 figureCurrentPoint (const graphics_object& fig) 234 { 235 Object *tkFig = qt_graphics_toolkit::toolkitObject (fig); 236 237 if (tkFig) 238 { 239 Container *c = tkFig->innerContainer (); 240 241 if (c) 242 { 243 // FIXME: QCursor::pos() may give inaccurate results with 244 // asynchronous window systems like X11 over ssh. 245 QPoint qp = c->mapFromGlobal (QCursor::pos ()); 246 247 return tkFig->properties<figure> ().map_from_boundingbox (qp.x (), 248 qp.y ()); 249 } 250 } 251 252 return Matrix (1, 2, 0.0); 253 } 254 255 Qt::Alignment fromHVAlign(const std::string & halign,const std::string & valign)256 fromHVAlign (const std::string& halign, const std::string& valign) 257 { 258 Qt::Alignment flags; 259 260 if (octave::string::strcmpi (halign, "left")) 261 flags |= Qt::AlignLeft; 262 else if (octave::string::strcmpi (halign, "center")) 263 flags |= Qt::AlignHCenter; 264 else if (octave::string::strcmpi (halign, "right")) 265 flags |= Qt::AlignRight; 266 else 267 flags |= Qt::AlignLeft; 268 269 if (octave::string::strcmpi (valign, "middle")) 270 flags |= Qt::AlignVCenter; 271 else if (octave::string::strcmpi (valign, "top")) 272 flags |= Qt::AlignTop; 273 else if (octave::string::strcmpi (valign, "bottom")) 274 flags |= Qt::AlignBottom; 275 else 276 flags |= Qt::AlignVCenter; 277 278 return flags; 279 } 280 281 QImage makeImageFromCData(const octave_value & v,int width,int height)282 makeImageFromCData (const octave_value& v, int width, int height) 283 { 284 dim_vector dv (v.dims ()); 285 286 if (dv.ndims () == 3 && dv(2) == 3) 287 { 288 int w = qMin (dv(1), static_cast<octave_idx_type> (width)); 289 int h = qMin (dv(0), static_cast<octave_idx_type> (height)); 290 291 int x_off = (w < width ? (width - w) / 2 : 0); 292 int y_off = (h < height ? (height - h) / 2 : 0); 293 294 QImage img (width, height, QImage::Format_ARGB32); 295 img.fill (qRgba (0, 0, 0, 0)); 296 297 if (v.is_uint8_type ()) 298 { 299 uint8NDArray d = v.uint8_array_value (); 300 301 for (int i = 0; i < w; i++) 302 for (int j = 0; j < h; j++) 303 { 304 int r = d(j, i, 0); 305 int g = d(j, i, 1); 306 int b = d(j, i, 2); 307 int a = 255; 308 309 img.setPixel (x_off + i, y_off + j, qRgba (r, g, b, a)); 310 } 311 } 312 else if (v.is_single_type ()) 313 { 314 FloatNDArray f = v.float_array_value (); 315 316 for (int i = 0; i < w; i++) 317 for (int j = 0; j < h; j++) 318 { 319 float r = f(j, i, 0); 320 float g = f(j, i, 1); 321 float b = f(j, i, 2); 322 int a = (octave::math::isnan (r) || octave::math::isnan (g) 323 || octave::math::isnan (b) ? 0 : 255); 324 325 img.setPixel (x_off + i, y_off + j, 326 qRgba (octave::math::round (r * 255), 327 octave::math::round (g * 255), 328 octave::math::round (b * 255), 329 a)); 330 } 331 } 332 else if (v.isreal ()) 333 { 334 NDArray d = v.array_value (); 335 336 for (int i = 0; i < w; i++) 337 for (int j = 0; j < h; j++) 338 { 339 double r = d(j, i, 0); 340 double g = d(j, i, 1); 341 double b = d(j, i, 2); 342 int a = (octave::math::isnan (r) || octave::math::isnan (g) 343 || octave::math::isnan (b) ? 0 : 255); 344 345 img.setPixel (x_off + i, y_off + j, 346 qRgba (octave::math::round (r * 255), 347 octave::math::round (g * 255), 348 octave::math::round (b * 255), 349 a)); 350 } 351 } 352 353 return img; 354 } 355 356 return QImage (); 357 } 358 359 octave_scalar_map makeKeyEventStruct(QKeyEvent * event)360 makeKeyEventStruct (QKeyEvent *event) 361 { 362 octave_scalar_map retval; 363 364 retval.setfield ("Key", KeyMap::qKeyToKeyString (event->key ())); 365 retval.setfield ("Character", toStdString (event->text ())); 366 367 std::list<std::string> modList; 368 Qt::KeyboardModifiers mods = event->modifiers (); 369 370 if (mods & Qt::ShiftModifier) 371 modList.push_back ("shift"); 372 if (mods & Qt::ControlModifier) 373 #if defined (Q_OS_MAC) 374 modList.push_back ("command"); 375 #else 376 modList.push_back ("control"); 377 #endif 378 if (mods & Qt::AltModifier) 379 modList.push_back ("alt"); 380 #if defined (Q_OS_MAC) 381 if (mods & Qt::MetaModifier) 382 modList.push_back ("control"); 383 #endif 384 385 retval.setfield ("Modifier", Cell (modList)); 386 387 return retval; 388 } 389 390 octave_scalar_map makeScrollEventStruct(QWheelEvent * event)391 makeScrollEventStruct (QWheelEvent *event) 392 { 393 octave_scalar_map retval; 394 395 // We assume a standard mouse with 15 degree steps and Qt returns 396 // 1/8 of a degree. 397 #if defined (HAVE_QWHEELEVENT_ANGLEDELTA) 398 int ydelta = -(event->angleDelta().y ()); 399 #else 400 int ydelta = (event->orientation () == Qt::Vertical 401 ? -(event->delta ()) : 0); 402 #endif 403 retval.setfield ("VerticalScrollCount", octave_value (ydelta / 120)); 404 405 // FIXME: Is there any way to access the number of lines a scroll step 406 // should correspond to? 407 retval.setfield ("VerticalScrollAmount", octave_value (3)); 408 retval.setfield ("EventName", octave_value ("WindowScrollWheel")); 409 410 return retval; 411 } 412 413 } 414 415 } 416